@akira-tl/forgerelay 1.1.2 → 1.2.0

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 (170) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +14 -0
  3. package/dist/activity/runtime/mcp-query-tools.js +15 -5
  4. package/dist/cli/config/inspect.js +331 -0
  5. package/dist/cli/config/migrate.js +351 -0
  6. package/dist/cli/init/setup-config.js +41 -0
  7. package/dist/cli/init.js +128 -130
  8. package/dist/cli/maintenance.js +15 -5
  9. package/dist/cli/mcp/external-mcp.js +14 -10
  10. package/dist/cli/mcp/status.js +11 -6
  11. package/dist/cli.js +40 -7
  12. package/dist/lsp/language-server-config.js +66 -144
  13. package/dist/lsp/runtime/manager.js +35 -4
  14. package/dist/mcp/hooks/config.js +141 -0
  15. package/dist/mcp/hooks/external-mcp-transform.js +22 -13
  16. package/dist/mcp/hooks/hook-cli.js +62 -52
  17. package/dist/mcp/hooks/hooks.js +111 -85
  18. package/dist/mcp/operations/external-mcp/external-mcp-runtime.js +8 -5
  19. package/dist/mcp/operations/external-mcp/external-mcp.js +9 -2
  20. package/dist/mcp/server/core/capability-support.js +7 -0
  21. package/dist/runtime/config/config.js +142 -64
  22. package/dist/runtime/config/definition/catalog.js +12 -0
  23. package/dist/runtime/config/definition/definition.js +115 -0
  24. package/dist/runtime/config/definition/external-mcp.js +58 -0
  25. package/dist/runtime/config/definition/general-config.js +258 -0
  26. package/dist/runtime/config/definition/language-servers.js +116 -0
  27. package/dist/runtime/config/definition/schema.js +113 -0
  28. package/dist/runtime/config/definition/types.js +2 -0
  29. package/dist/runtime/config/external-mcp-auth-store.js +105 -29
  30. package/dist/runtime/config/external-mcp-config.js +39 -0
  31. package/dist/runtime/config/external-mcp-registry.js +284 -106
  32. package/dist/runtime/config/inspection/live.js +77 -0
  33. package/dist/runtime/config/resolution/general.js +93 -0
  34. package/dist/runtime/config/resolution/hooks.js +315 -0
  35. package/dist/runtime/config/resolution/language-servers.js +237 -0
  36. package/dist/runtime/config/resolution/project-sources.js +159 -0
  37. package/dist/runtime/config/resolution/resolver.js +485 -0
  38. package/dist/runtime/config/resolution/types.js +1 -0
  39. package/dist/runtime/config/runtime/config-runtime.js +74 -0
  40. package/dist/runtime/config/runtime/source-refresh.js +263 -0
  41. package/dist/runtime/config/user-config.js +38 -31
  42. package/dist/runtime/security/project-execution-trust.js +34 -0
  43. package/dist/runtime/testing/server-fixture.js +9 -8
  44. package/dist/server.js +10 -6
  45. package/dist/subagents/profiles.js +336 -53
  46. package/dist/subagents/sessions/execution.js +6 -35
  47. package/dist/subagents/sessions/manager.js +14 -2
  48. package/dist/ui/.vite/manifest.json +275 -275
  49. package/dist/ui/activity-panel-app.html +1 -1
  50. package/dist/ui/assets/{activity-panel-app-Bw7ZFX2g.js → activity-panel-app-BeDiwd7D.js} +3 -3
  51. package/dist/ui/assets/{angular-html-DeUNP12X.js → angular-html-DuoCE7nn.js} +1 -1
  52. package/dist/ui/assets/{angular-ts-B3v8FKi_.js → angular-ts-D5tGk0hL.js} +1 -1
  53. package/dist/ui/assets/{apl-Bs3iQm4P.js → apl-lciOTPbw.js} +1 -1
  54. package/dist/ui/assets/{astro-D6GHecBY.js → astro-vJbqfNnJ.js} +1 -1
  55. package/dist/ui/assets/{blade-BMNbVOHv.js → blade-C7655Q4K.js} +1 -1
  56. package/dist/ui/assets/{c-BquuA6bv.js → c-DdHp1Jrw.js} +1 -1
  57. package/dist/ui/assets/{cobol-BuZ6kfps.js → cobol-DgvzQxZP.js} +1 -1
  58. package/dist/ui/assets/{coffee-DgC5uBzL.js → coffee-BIjVUOUd.js} +1 -1
  59. package/dist/ui/assets/{cpp-BdvWqCaB.js → cpp-CU8tGzvI.js} +1 -1
  60. package/dist/ui/assets/{crystal-0Q5qBvNI.js → crystal-CUQAwsg9.js} +1 -1
  61. package/dist/ui/assets/{css-Be_T8idh.js → css-DtMTqD3F.js} +1 -1
  62. package/dist/ui/assets/{edge-DaGLdgh_.js → edge-DoCX9RnL.js} +1 -1
  63. package/dist/ui/assets/{elixir-CMgiFIzB.js → elixir-DJxV9_U6.js} +1 -1
  64. package/dist/ui/assets/{elm-B-ufMlMC.js → elm-DIgWqK4r.js} +1 -1
  65. package/dist/ui/assets/{erb-CaGiOZM-.js → erb-BHxP0c_e.js} +1 -1
  66. package/dist/ui/assets/{git-rebase-CpwUWCzK.js → git-rebase-HbI_Vo8d.js} +1 -1
  67. package/dist/ui/assets/{glimmer-js-BRxccvnE.js → glimmer-js-CthY4Ty_.js} +1 -1
  68. package/dist/ui/assets/{glimmer-ts-CgQbPzlT.js → glimmer-ts-B2-_A1Xz.js} +1 -1
  69. package/dist/ui/assets/{glsl-D7JmHFr0.js → glsl-D9V9gq98.js} +1 -1
  70. package/dist/ui/assets/{graphql-C8n0eUBK.js → graphql-CddeAaS3.js} +1 -1
  71. package/dist/ui/assets/{hack-lVN7KVQv.js → hack-D_loGkOT.js} +1 -1
  72. package/dist/ui/assets/{haml-h18lG0pS.js → haml-BoWDczd0.js} +1 -1
  73. package/dist/ui/assets/{handlebars-lRFjRPfj.js → handlebars-ZoSOvw9y.js} +1 -1
  74. package/dist/ui/assets/{heavy-payload-Cqx9MOkH.js → heavy-payload-DO34LwoZ.js} +3 -3
  75. package/dist/ui/assets/{html-DtILqTE1.js → html-CE-obXVo.js} +1 -1
  76. package/dist/ui/assets/{html-derivative-CSTSmPup.js → html-derivative-Cd-smAbW.js} +1 -1
  77. package/dist/ui/assets/{http-DNI8fsbY.js → http-CZrkbrSl.js} +1 -1
  78. package/dist/ui/assets/{hurl-DgRi60JG.js → hurl-CxTuxBt1.js} +1 -1
  79. package/dist/ui/assets/{java-CW9pZ5bh.js → java-BshLjCat.js} +1 -1
  80. package/dist/ui/assets/{javascript-Cvg2UrWL.js → javascript-D2pxQTiX.js} +1 -1
  81. package/dist/ui/assets/{jinja-CZw_S1FB.js → jinja-kbfPP7-F.js} +1 -1
  82. package/dist/ui/assets/{jison-C56u_FZv.js → jison-C2mt9l1M.js} +1 -1
  83. package/dist/ui/assets/{json-DW2UTv1q.js → json-DU_rg_AU.js} +1 -1
  84. package/dist/ui/assets/{jsx-Zj5gobdD.js → jsx-D_8aQMlx.js} +1 -1
  85. package/dist/ui/assets/{julia-Cvcrre-L.js → julia-BmefTaVV.js} +1 -1
  86. package/dist/ui/assets/{just-8CZ2u6Tj.js → just-DXV8vk1S.js} +1 -1
  87. package/dist/ui/assets/{latex-D289AXWG.js → latex-Cjm2MvGW.js} +1 -1
  88. package/dist/ui/assets/{liquid-CFEX3SkZ.js → liquid-DyQDlo0u.js} +1 -1
  89. package/dist/ui/assets/{lua-BKpJ0ybO.js → lua-B2A1vzzy.js} +1 -1
  90. package/dist/ui/assets/{marko-D9maZ0a6.js → marko-B9vOkXOU.js} +1 -1
  91. package/dist/ui/assets/{mdc-rM9l0qWR.js → mdc-D42m6spU.js} +1 -1
  92. package/dist/ui/assets/{nginx-C9w6p2C9.js → nginx-TpfVxSUY.js} +1 -1
  93. package/dist/ui/assets/{nim-BRePHk0J.js → nim-CJr5T1FK.js} +1 -1
  94. package/dist/ui/assets/{perl-CskTmiPQ.js → perl-cJZcsdm9.js} +1 -1
  95. package/dist/ui/assets/{php-DCU_FvNI.js → php-CSZL5KRT.js} +1 -1
  96. package/dist/ui/assets/{pug-XLaVxTvv.js → pug-B1eMKzuA.js} +1 -1
  97. package/dist/ui/assets/{qml-CaONKSl-.js → qml-Dl2_7zc9.js} +1 -1
  98. package/dist/ui/assets/{r-BCVR0ZE3.js → r-F0aaO9gG.js} +1 -1
  99. package/dist/ui/assets/{razor-DDnGXxnU.js → razor-BPnkSMCw.js} +1 -1
  100. package/dist/ui/assets/{regexp-DWGTYYVx.js → regexp-BnGHMFbv.js} +1 -1
  101. package/dist/ui/assets/{rst-Dz3GWvQm.js → rst-B3CO7WYO.js} +1 -1
  102. package/dist/ui/assets/{ruby-DC3jGtMY.js → ruby-B6VMzT1T.js} +1 -1
  103. package/dist/ui/assets/{sas-7mNpvsjZ.js → sas-Df2lHVuU.js} +1 -1
  104. package/dist/ui/assets/{scss-uBsoCJVy.js → scss-4XxI6sgu.js} +1 -1
  105. package/dist/ui/assets/{shellscript-BCN9N_Jf.js → shellscript-DYASCN4r.js} +1 -1
  106. package/dist/ui/assets/{shellsession-Hsw46VXV.js → shellsession-Br6AGJO_.js} +1 -1
  107. package/dist/ui/assets/{soy-D6KV69HU.js → soy-p3ZNq1We.js} +1 -1
  108. package/dist/ui/assets/{sql-CVzVQkJh.js → sql-CAdv2OXL.js} +1 -1
  109. package/dist/ui/assets/{stata-BwUjXNhI.js → stata-Dx7yUbG_.js} +1 -1
  110. package/dist/ui/assets/{surrealql-CgloWqRa.js → surrealql-6YsE8HQa.js} +1 -1
  111. package/dist/ui/assets/{svelte-DcC3n6IC.js → svelte-YgzedwpD.js} +1 -1
  112. package/dist/ui/assets/{templ-DOFfnWTJ.js → templ-Bahqx3uD.js} +1 -1
  113. package/dist/ui/assets/{tex-ln89VPaX.js → tex-o6fTQjkH.js} +1 -1
  114. package/dist/ui/assets/{ts-tags-CWMzCstH.js → ts-tags-BO-Co0Ju.js} +1 -1
  115. package/dist/ui/assets/{tsx-DgzfiGeK.js → tsx-DtQe5AoH.js} +1 -1
  116. package/dist/ui/assets/{twig-Bi2itZL9.js → twig-BcUs6Kqc.js} +1 -1
  117. package/dist/ui/assets/{typescript-CQ24f4zW.js → typescript-BPtM8h4u.js} +1 -1
  118. package/dist/ui/assets/{vue-BS8jE5u6.js → vue-5KkGUzJJ.js} +1 -1
  119. package/dist/ui/assets/{vue-html-o38xqhyB.js → vue-html-Df_Xfsz9.js} +1 -1
  120. package/dist/ui/assets/{vue-vine-D2NvKv50.js → vue-vine-hUIe_u_Y.js} +1 -1
  121. package/dist/ui/assets/{xml-_44rx9_2.js → xml-C3Jy7as5.js} +1 -1
  122. package/dist/ui/assets/{xsl-B5QWQPoi.js → xsl-BnF9OuFo.js} +1 -1
  123. package/dist/ui/assets/{yaml-CKL2pEWe.js → yaml-DyrFgCou.js} +1 -1
  124. package/dist/workspaces/conversation-test-support.js +3 -11
  125. package/dist/workspaces/sessions.js +2 -0
  126. package/dist/workspaces/state/project-context.js +221 -0
  127. package/dist/workspaces.js +3 -1
  128. package/docs/chatgpt-coding-workflow.md +8 -3
  129. package/docs/configuration.md +99 -41
  130. package/docs/gotchas.md +24 -5
  131. package/docs/security.md +14 -3
  132. package/docs/setup.md +12 -7
  133. package/package.json +12 -3
  134. package/schemas/v1/config.project-local.schema.json +14 -0
  135. package/schemas/v1/config.project.schema.json +14 -0
  136. package/schemas/v1/config.user.schema.json +538 -0
  137. package/schemas/v1/hooks.project-local.schema.json +118 -0
  138. package/schemas/v1/hooks.project.schema.json +118 -0
  139. package/schemas/v1/hooks.user.schema.json +118 -0
  140. package/schemas/v1/language-servers.project-local.schema.json +230 -0
  141. package/schemas/v1/language-servers.project.schema.json +230 -0
  142. package/schemas/v1/language-servers.user.schema.json +230 -0
  143. package/schemas/v1/mcp.project-local.schema.json +143 -0
  144. package/schemas/v1/mcp.project.schema.json +143 -0
  145. package/schemas/v1/mcp.user.schema.json +143 -0
  146. package/scripts/ci/architecture.mjs +54 -4
  147. package/scripts/ci/cmd-acceptance.mjs +9 -9
  148. package/scripts/ci/config-v2-product-acceptance.mjs +639 -0
  149. package/scripts/ci/gates/acceptance-artifact.mjs +66 -0
  150. package/scripts/ci/gates/core-contract.mjs +27 -0
  151. package/scripts/ci/gates/platform-tests.mjs +38 -0
  152. package/scripts/ci/gates/prepare-windows-product.mjs +57 -0
  153. package/scripts/ci/gates/test-shard.mjs +71 -0
  154. package/scripts/ci/powershell51-acceptance.mjs +9 -9
  155. package/scripts/ci/pwsh-acceptance.mjs +9 -9
  156. package/scripts/ci/verify.mjs +2 -0
  157. package/scripts/ci/windows-product-acceptance.mjs +9 -10
  158. package/scripts/config/schema-text.mjs +4 -0
  159. package/scripts/config/schema-text.test.mjs +18 -0
  160. package/scripts/config/schema.mjs +74 -0
  161. package/scripts/lsp/interop.mjs +4 -1
  162. package/scripts/release/pack.mjs +64 -4
  163. package/scripts/release/release-gate.test.mjs +93 -27
  164. package/scripts/release-parity.mjs +19 -2
  165. package/templates/instructions/cmd.md +12 -0
  166. package/templates/instructions/fish.md +11 -0
  167. package/templates/instructions/powershell.md +14 -0
  168. package/templates/instructions/pwsh.md +14 -0
  169. package/templates/instructions/zsh.md +11 -0
  170. package/dist/runtime/instructions/powershell-skill.js +0 -65
@@ -0,0 +1,237 @@
1
+ import { join } from "node:path";
2
+ import { BUILTIN_LANGUAGE_SERVER_DEFINITIONS, languageServersConfigDefinition, } from "../definition/language-servers.js";
3
+ import { refreshLegacyUserConfigField } from "./project-sources.js";
4
+ import { resolveConfigDomain } from "./resolver.js";
5
+ import { ConfigSourceRuntime } from "../runtime/source-refresh.js";
6
+ const CANONICAL_PRIORITY = 100;
7
+ const LEGACY_PRIORITY = 0;
8
+ const LEGACY_DEPRECATION = {
9
+ since: "1.2.0",
10
+ removeIn: "1.4.0",
11
+ replacement: "language-servers.json",
12
+ };
13
+ export async function resolveLanguageServersConfig(input) {
14
+ const sources = [];
15
+ const refreshDiagnostics = [];
16
+ const sourceRuntime = input.sourceRuntime ?? new ConfigSourceRuntime();
17
+ if (input.configDir) {
18
+ const legacy = readLegacyInlineLanguageServers(sourceRuntime, input.configDir);
19
+ if (legacy.source)
20
+ sources.push(legacy.source);
21
+ if (legacy.diagnostic)
22
+ refreshDiagnostics.push(legacy.diagnostic);
23
+ const user = readLanguageServerSource(sourceRuntime, "canonical:user:language-servers", "user", join(input.configDir, "language-servers.json"), input.environment);
24
+ if (user.source)
25
+ sources.push(user.source);
26
+ if (user.diagnostic)
27
+ refreshDiagnostics.push(user.diagnostic);
28
+ }
29
+ const sharedConfigDir = input.project?.sharedConfigDir ?? input.projectSharedConfigDir;
30
+ if (sharedConfigDir) {
31
+ const shared = readLanguageServerSource(sourceRuntime, "canonical:project:language-servers", "project", join(sharedConfigDir, "language-servers.json"), input.environment);
32
+ if (shared.source)
33
+ sources.push(shared.source);
34
+ if (shared.diagnostic)
35
+ refreshDiagnostics.push(shared.diagnostic);
36
+ }
37
+ if (input.project) {
38
+ const local = readLanguageServerSource(sourceRuntime, "canonical:project-local:language-servers", "project-local", join(input.project.localConfigDir, "language-servers.json"), input.environment);
39
+ if (local.source)
40
+ sources.push(local.source);
41
+ if (local.diagnostic)
42
+ refreshDiagnostics.push(local.diagnostic);
43
+ }
44
+ const resolution = resolveConfigDomain({
45
+ definition: languageServersConfigDefinition,
46
+ sources,
47
+ environment: input.environment,
48
+ });
49
+ resolution.diagnostics = mergeDiagnostics(resolution.diagnostics, refreshDiagnostics);
50
+ return resolution;
51
+ }
52
+ export function effectiveLanguageServerEntries(resolution) {
53
+ const servers = isRecord(resolution.values.servers) ? resolution.values.servers : {};
54
+ return Object.entries(servers).map(([id, value]) => {
55
+ const entry = resolution.entries[`servers.${id}`];
56
+ if (!entry || !isRecord(value)) {
57
+ throw new Error(`Language Server resolver produced an invalid entry for ${id}.`);
58
+ }
59
+ return {
60
+ id,
61
+ value: value,
62
+ scope: entry.effective.source.scope,
63
+ };
64
+ });
65
+ }
66
+ function readLegacyInlineLanguageServers(sourceRuntime, configDir) {
67
+ const location = join(configDir, "config.json");
68
+ const refreshed = refreshLegacyUserConfigField({
69
+ sourceRuntime,
70
+ configDir,
71
+ field: "languageServers",
72
+ parse: (value) => {
73
+ if (value === undefined)
74
+ return undefined;
75
+ if (!isRecord(value))
76
+ throw new Error("languageServers must be an object.");
77
+ return normalizeLanguageServerDefinitions(value);
78
+ },
79
+ invalidMessage: "Legacy inline Language Server configuration is invalid.",
80
+ });
81
+ if (refreshed.status.state === "missing")
82
+ return {};
83
+ const source = refreshed.value === undefined ? undefined : {
84
+ id: "legacy:user:languageServers",
85
+ scope: "user",
86
+ kind: "file",
87
+ location,
88
+ priority: LEGACY_PRIORITY,
89
+ value: refreshed.value,
90
+ deprecation: LEGACY_DEPRECATION,
91
+ };
92
+ if (refreshed.status.state !== "invalid" || !refreshed.issue)
93
+ return source ? { source } : {};
94
+ if (refreshed.issueScope === "container")
95
+ return source ? { source } : {};
96
+ if (source) {
97
+ return {
98
+ source,
99
+ diagnostic: {
100
+ severity: "error",
101
+ code: refreshed.issue.code,
102
+ source: sourceReference(source),
103
+ message: refreshed.issue.message,
104
+ usingLastKnownGood: refreshed.status.usingLastKnownGood,
105
+ diagnosticChanged: refreshed.status.diagnosticChanged,
106
+ },
107
+ };
108
+ }
109
+ return {
110
+ source: invalidSource("legacy:user:languageServers", "user", location, refreshed.issue.message, refreshed.issue.code, LEGACY_PRIORITY, LEGACY_DEPRECATION),
111
+ };
112
+ }
113
+ function readLanguageServerSource(sourceRuntime, id, scope, location, environment) {
114
+ const refreshed = sourceRuntime.refreshFile({
115
+ key: `language-servers:${id}:${location}`,
116
+ path: location,
117
+ parse: (raw) => {
118
+ const value = JSON.parse(raw);
119
+ return isRecord(value) ? normalizeFileDefinitions(value) : value;
120
+ },
121
+ parseIssue: {
122
+ code: "invalid_source",
123
+ message: "Configuration source is not valid JSON.",
124
+ },
125
+ readIssue: {
126
+ code: "invalid_source",
127
+ message: "Configuration source could not be read.",
128
+ },
129
+ validate: (value) => {
130
+ const validation = resolveConfigDomain({
131
+ definition: languageServersConfigDefinition,
132
+ sources: [canonicalLanguageServerSource(id, scope, location, value)],
133
+ environment,
134
+ });
135
+ const error = validation.diagnostics.find((diagnostic) => diagnostic.severity === "error");
136
+ if (!error)
137
+ return undefined;
138
+ return {
139
+ code: error.code === "missing_environment" ? "missing_environment" : "invalid_source",
140
+ message: error.message,
141
+ };
142
+ },
143
+ });
144
+ if (refreshed.status.state === "missing")
145
+ return {};
146
+ if (refreshed.value !== undefined) {
147
+ const source = canonicalLanguageServerSource(id, scope, location, refreshed.value);
148
+ if (refreshed.status.state !== "invalid" || !refreshed.issue)
149
+ return { source };
150
+ return {
151
+ source,
152
+ diagnostic: {
153
+ severity: "error",
154
+ code: refreshed.issue.code,
155
+ source: sourceReference(source),
156
+ message: refreshed.issue.message,
157
+ usingLastKnownGood: true,
158
+ diagnosticChanged: refreshed.status.diagnosticChanged,
159
+ },
160
+ };
161
+ }
162
+ const source = invalidSource(id, scope, location, refreshed.issue?.message ?? "Language Server configuration is invalid.", refreshed.issue?.code);
163
+ return { source };
164
+ }
165
+ function canonicalLanguageServerSource(id, scope, location, value) {
166
+ return {
167
+ id,
168
+ scope,
169
+ kind: "file",
170
+ location,
171
+ priority: CANONICAL_PRIORITY,
172
+ shadowsLowerPriority: true,
173
+ value,
174
+ };
175
+ }
176
+ function invalidSource(id, scope, location, message, code = "invalid_source", priority = CANONICAL_PRIORITY, deprecation) {
177
+ return {
178
+ id,
179
+ scope,
180
+ kind: "file",
181
+ location,
182
+ priority,
183
+ ...(priority === CANONICAL_PRIORITY ? { shadowsLowerPriority: true } : {}),
184
+ ...(deprecation ? { deprecation } : {}),
185
+ error: { code, message },
186
+ };
187
+ }
188
+ function sourceReference(source) {
189
+ return {
190
+ id: source.id,
191
+ scope: source.scope,
192
+ kind: source.kind,
193
+ ...(source.location ? { location: source.location } : {}),
194
+ priority: source.priority,
195
+ };
196
+ }
197
+ function mergeDiagnostics(left, right) {
198
+ const seen = new Set();
199
+ return [...left, ...right].filter((diagnostic) => {
200
+ const key = `${diagnostic.source.id}\0${diagnostic.code}\0${diagnostic.message}\0${diagnostic.usingLastKnownGood === true}`;
201
+ if (seen.has(key))
202
+ return false;
203
+ seen.add(key);
204
+ return true;
205
+ });
206
+ }
207
+ function normalizeFileDefinitions(value) {
208
+ const { $schema, ...definitions } = value;
209
+ return {
210
+ ...($schema === undefined ? {} : { $schema }),
211
+ ...normalizeLanguageServerDefinitions(definitions),
212
+ };
213
+ }
214
+ export function normalizeLanguageServerDefinitions(value) {
215
+ return Object.fromEntries(Object.entries(value).map(([id, raw]) => {
216
+ if (!isRecord(raw))
217
+ return [id, raw];
218
+ if (raw.disabled === true || raw.enabled === false)
219
+ return [id, { disabled: true }];
220
+ const { enabled: _enabled, disabled: _disabled, ...rest } = raw;
221
+ const builtin = BUILTIN_LANGUAGE_SERVER_DEFINITIONS[id];
222
+ if (!builtin)
223
+ return [id, rest];
224
+ return [id, {
225
+ ...builtin,
226
+ ...rest,
227
+ env: { ...(builtin.env ?? {}), ...(isRecord(rest.env) ? rest.env : {}) },
228
+ languageIdByExtension: {
229
+ ...(builtin.languageIdByExtension ?? {}),
230
+ ...(isRecord(rest.languageIdByExtension) ? rest.languageIdByExtension : {}),
231
+ },
232
+ }];
233
+ }));
234
+ }
235
+ function isRecord(value) {
236
+ return typeof value === "object" && value !== null && !Array.isArray(value);
237
+ }
@@ -0,0 +1,159 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename, join } from "node:path";
3
+ import { resolveGeneralConfig } from "./general.js";
4
+ export function refreshLegacyUserConfigField(input) {
5
+ const location = join(input.configDir, "config.json");
6
+ const refreshed = input.sourceRuntime.refreshFile({
7
+ key: `legacy-user-config:${input.field}:${location}`,
8
+ path: location,
9
+ parse: (raw) => {
10
+ let value;
11
+ try {
12
+ value = JSON.parse(raw);
13
+ }
14
+ catch {
15
+ throw new LegacyUserConfigFieldError("container");
16
+ }
17
+ if (!isRecord(value))
18
+ throw new LegacyUserConfigFieldError("container");
19
+ const validation = resolveGeneralConfig({
20
+ env: {},
21
+ user: value,
22
+ userSourcePath: location,
23
+ });
24
+ const error = validation.diagnostics.find((diagnostic) => diagnostic.severity === "error");
25
+ if (error)
26
+ throw new LegacyUserConfigFieldError("container");
27
+ try {
28
+ return input.parse(value[input.field]);
29
+ }
30
+ catch {
31
+ throw new LegacyUserConfigFieldError("field");
32
+ }
33
+ },
34
+ parseIssue: (error) => ({
35
+ code: "invalid_source",
36
+ message: error instanceof LegacyUserConfigFieldError && error.scope === "field"
37
+ ? input.invalidMessage
38
+ : "General configuration is invalid.",
39
+ }),
40
+ readIssue: {
41
+ code: "invalid_source",
42
+ message: "General configuration could not be read.",
43
+ },
44
+ });
45
+ return {
46
+ ...refreshed,
47
+ ...(refreshed.issue
48
+ ? { issueScope: refreshed.issue.message === input.invalidMessage ? "field" : "container" }
49
+ : {}),
50
+ };
51
+ }
52
+ class LegacyUserConfigFieldError extends Error {
53
+ scope;
54
+ constructor(scope) {
55
+ super(scope);
56
+ this.scope = scope;
57
+ }
58
+ }
59
+ export async function resolveProjectGeneralConfig(project, input = {}) {
60
+ const userSource = await readJsonConfigSource({
61
+ id: "user:config",
62
+ scope: "user",
63
+ location: join(project.configDir, "config.json"),
64
+ });
65
+ const projectSources = await loadProjectConfigSources(project, {
66
+ domain: "config",
67
+ fileName: "config.json",
68
+ });
69
+ const resolutionInput = {
70
+ ...(input.env === undefined ? {} : { env: input.env }),
71
+ ...(input.cli === undefined ? {} : { cli: input.cli }),
72
+ ...(userSource === undefined ? {} : { userSource }),
73
+ };
74
+ const projectSource = projectSources.find((source) => source.scope === "project");
75
+ const projectLocalSource = projectSources.find((source) => source.scope === "project-local");
76
+ if (projectSource)
77
+ resolutionInput.projectSource = projectSource;
78
+ if (projectLocalSource)
79
+ resolutionInput.projectLocalSource = projectLocalSource;
80
+ return resolveGeneralConfig(resolutionInput);
81
+ }
82
+ export async function loadProjectConfigSources(project, options) {
83
+ const fileName = normalizeConfigFileName(options.fileName);
84
+ const domain = normalizeDomain(options.domain);
85
+ const sources = [];
86
+ const projectSource = await readJsonConfigSource({
87
+ id: `project:${domain}`,
88
+ scope: "project",
89
+ location: join(project.sharedConfigDir, fileName),
90
+ });
91
+ if (projectSource)
92
+ sources.push(projectSource);
93
+ const localSource = await readJsonConfigSource({
94
+ id: `project-local:${domain}`,
95
+ scope: "project-local",
96
+ location: join(project.localConfigDir, fileName),
97
+ });
98
+ if (localSource)
99
+ sources.push(localSource);
100
+ return sources;
101
+ }
102
+ export async function readJsonConfigSource(input) {
103
+ let raw;
104
+ try {
105
+ raw = await readFile(input.location, "utf8");
106
+ }
107
+ catch (error) {
108
+ if (isErrno(error, "ENOENT"))
109
+ return undefined;
110
+ return {
111
+ ...input,
112
+ kind: "file",
113
+ priority: 0,
114
+ error: {
115
+ code: "invalid_source",
116
+ message: "Configuration source could not be read.",
117
+ },
118
+ };
119
+ }
120
+ try {
121
+ return {
122
+ ...input,
123
+ kind: "file",
124
+ priority: 0,
125
+ value: JSON.parse(raw),
126
+ };
127
+ }
128
+ catch {
129
+ return {
130
+ ...input,
131
+ kind: "file",
132
+ priority: 0,
133
+ error: {
134
+ code: "invalid_source",
135
+ message: "Configuration source is not valid JSON.",
136
+ },
137
+ };
138
+ }
139
+ }
140
+ function normalizeConfigFileName(value) {
141
+ const normalized = value.trim();
142
+ if (!normalized || normalized === "." || normalized === ".." || basename(normalized) !== normalized) {
143
+ throw new Error("Project configuration fileName must be one file name without path separators.");
144
+ }
145
+ return normalized;
146
+ }
147
+ function normalizeDomain(value) {
148
+ const normalized = value.trim();
149
+ if (!/^[a-z][a-z0-9-]*$/.test(normalized)) {
150
+ throw new Error("Project configuration domain must use lowercase letters, digits, and hyphens.");
151
+ }
152
+ return normalized;
153
+ }
154
+ function isErrno(error, code) {
155
+ return error instanceof Error && "code" in error && error.code === code;
156
+ }
157
+ function isRecord(value) {
158
+ return typeof value === "object" && value !== null && !Array.isArray(value);
159
+ }