@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.3

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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/openvulcan-icon.svg +33 -0
  4. package/cordis.patch.yml +19 -0
  5. package/node_modules/@openvulcan/dsh-decision-guard/LICENSE +21 -0
  6. package/node_modules/@openvulcan/dsh-decision-guard/README.md +14 -0
  7. package/node_modules/@openvulcan/dsh-decision-guard/assets/decision-guard-icon.png +0 -0
  8. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.d.ts +40 -0
  9. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.js +150 -0
  10. package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +2250 -0
  11. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.d.ts +90 -0
  12. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.js +108 -0
  13. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.d.ts +129 -0
  14. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.js +572 -0
  15. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.d.ts +22 -0
  16. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.js +77 -0
  17. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.d.ts +158 -0
  18. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.js +340 -0
  19. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.d.ts +133 -0
  20. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.js +186 -0
  21. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.d.ts +55 -0
  22. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.js +202 -0
  23. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.d.ts +62 -0
  24. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.js +155 -0
  25. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.d.ts +2 -0
  26. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.js +4 -0
  27. package/node_modules/@openvulcan/dsh-decision-guard/package.json +68 -0
  28. package/node_modules/@openvulcan/dsh-vulcan-account/LICENSE +21 -0
  29. package/node_modules/@openvulcan/dsh-vulcan-account/assets/account-icon.png +0 -0
  30. package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +573 -0
  31. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.d.ts +92 -0
  32. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.js +194 -0
  33. package/node_modules/@openvulcan/dsh-vulcan-account/package.json +57 -0
  34. package/node_modules/@openvulcan/dsh-vulcan-answers/LICENSE +21 -0
  35. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/answers-icon.png +0 -0
  36. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-interactive.md +79 -0
  37. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-standard.md +1 -0
  38. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.d.ts +258 -0
  39. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.js +698 -0
  40. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +671 -0
  41. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.d.ts +29 -0
  42. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.js +70 -0
  43. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.d.ts +19 -0
  44. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.js +52 -0
  45. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.d.ts +9 -0
  46. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.js +9 -0
  47. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.d.ts +22 -0
  48. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.js +39 -0
  49. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.d.ts +20 -0
  50. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.js +17 -0
  51. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.d.ts +66 -0
  52. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.js +118 -0
  53. package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +59 -0
  54. package/node_modules/@openvulcan/dsh-vulcan-luaskills/LICENSE +21 -0
  55. package/node_modules/@openvulcan/dsh-vulcan-luaskills/assets/luaskills-icon.svg +1 -0
  56. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +949 -0
  57. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.d.ts +46 -0
  58. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.js +51 -0
  59. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.d.ts +55 -0
  60. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.js +149 -0
  61. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.d.ts +49 -0
  62. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.js +116 -0
  63. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.d.ts +26 -0
  64. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.js +276 -0
  65. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.d.ts +65 -0
  66. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.js +2 -0
  67. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.d.ts +111 -0
  68. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.js +281 -0
  69. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.d.ts +26 -0
  70. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.js +87 -0
  71. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.d.ts +79 -0
  72. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.js +165 -0
  73. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.d.ts +34 -0
  74. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.js +61 -0
  75. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.d.ts +59 -0
  76. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.js +107 -0
  77. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.d.ts +10 -0
  78. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.js +140 -0
  79. package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +57 -0
  80. package/node_modules/@openvulcan/dsh-vulcan-memory/LICENSE +21 -0
  81. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +21 -0
  82. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +31 -0
  83. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-icon.png +0 -0
  84. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/client.js +1090 -0
  85. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.d.ts +30 -0
  86. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.js +240 -0
  87. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.d.ts +30 -0
  88. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.js +46 -0
  89. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.d.ts +23 -0
  90. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.js +25 -0
  91. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.d.ts +108 -0
  92. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.js +373 -0
  93. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +14 -0
  94. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +31 -0
  95. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +196 -0
  96. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +204 -0
  97. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.d.ts +92 -0
  98. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +472 -0
  99. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.d.ts +126 -0
  100. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.js +553 -0
  101. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.d.ts +56 -0
  102. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.js +182 -0
  103. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.d.ts +13 -0
  104. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.js +24 -0
  105. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.d.ts +65 -0
  106. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.js +227 -0
  107. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.d.ts +34 -0
  108. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.js +171 -0
  109. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.d.ts +72 -0
  110. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.js +250 -0
  111. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.d.ts +6 -0
  112. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.js +12 -0
  113. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.d.ts +8 -0
  114. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.js +38 -0
  115. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.d.ts +132 -0
  116. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.js +235 -0
  117. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.d.ts +14 -0
  118. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.js +27 -0
  119. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.d.ts +29 -0
  120. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.js +62 -0
  121. package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +63 -0
  122. package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
  123. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
  124. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
  125. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
  126. package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
  127. package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
  128. package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
  129. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1121 -0
  130. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
  131. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
  132. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
  133. package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
  134. package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
  135. package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
  136. package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
  137. package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
  138. package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
  139. package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
  140. package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
  141. package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
  142. package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
  143. package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
  144. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
  145. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
  146. package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
  147. package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
  148. package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
  149. package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
  150. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
  151. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
  152. package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
  153. package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
  154. package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
  155. package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
  156. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
  157. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
  158. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
  159. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
  160. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
  161. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
  162. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
  163. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
  164. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
  165. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
  166. package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
  167. package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
  168. package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
  169. package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
  170. package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
  171. package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
  172. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
  173. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
  174. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
  175. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
  176. package/package.json +55 -4
@@ -0,0 +1,281 @@
1
+ /**
2
+ * The LuaSkills runtime's lifecycle: absent until the user installs it, then installed in five
3
+ * steps with progress, started on every dsh start while its versions match, restarted when
4
+ * settings change, and removable. A failed or cancelled install leaves nothing half-installed.
5
+ *
6
+ * Layout below the plugin's folder in the dsh home:
7
+ * - `sdk/node_modules/` — the SDK and Koffi;
8
+ * - `runtime/` — the SDK's runtime root (core library, Lua packages, skill layers, state);
9
+ * - `installed.json` — written last; without it the runtime counts as not installed;
10
+ * - `settings.json` — the user's settings, kept when the runtime is removed.
11
+ */
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { readdir, rm, writeFile } from 'node:fs/promises';
14
+ import { tmpdir } from 'node:os';
15
+ import path from 'node:path';
16
+ import { DEFAULT_SKILL, HUB_SITES, LUA_RUNTIME_VERSION, LUASKILLS_VERSION, RUNTIME_SIZE_MB, SDK_PACKAGE, SDK_PACKAGES, } from './constants.js';
17
+ import { LuaSkillsWorker } from './engine.js';
18
+ import { installPackages, packageDir } from './sdk-install.js';
19
+ import { engineOptions, loadSettings, parseSettings, saveSettings, } from './settings.js';
20
+ /** The versions this plugin installs. */
21
+ export const PINNED = { sdk: SDK_PACKAGE.version, luaskills: LUASKILLS_VERSION, luaRuntime: LUA_RUNTIME_VERSION };
22
+ /** Install steps, in the order the SDK fetches the runtime archives. */
23
+ export const PHASES = ['sdk', 'lua', 'core', 'engine', 'skill'];
24
+ export class RuntimeError extends Error {
25
+ code;
26
+ constructor(message, code = 'not_ready') {
27
+ super(message);
28
+ this.code = code;
29
+ }
30
+ }
31
+ export class LuaSkillsRuntime {
32
+ home;
33
+ changed;
34
+ runtimeRoot;
35
+ nodeModules;
36
+ marker;
37
+ settingsFile;
38
+ scratch;
39
+ state = { kind: 'absent' };
40
+ worker;
41
+ loaded;
42
+ installing;
43
+ closed = false;
44
+ /**
45
+ * @param home - this plugin's folder in the dsh home.
46
+ * @param changed - called after every state change.
47
+ */
48
+ constructor(home, changed) {
49
+ this.home = home;
50
+ this.changed = changed;
51
+ this.runtimeRoot = path.join(home, 'runtime');
52
+ this.nodeModules = path.join(home, 'sdk', 'node_modules');
53
+ this.marker = path.join(home, 'installed.json');
54
+ this.settingsFile = path.join(home, 'settings.json');
55
+ this.scratch = path.join(home, 'download');
56
+ this.loaded = loadSettings(this.settingsFile);
57
+ }
58
+ get current() {
59
+ return this.state;
60
+ }
61
+ view() {
62
+ return {
63
+ state: this.state,
64
+ location: this.home,
65
+ sizeMb: RUNTIME_SIZE_MB,
66
+ versions: PINNED,
67
+ settings: this.loaded.settings,
68
+ settingsSource: this.loaded.kind,
69
+ ...this.loaded.kind === 'invalid' ? { settingsError: this.loaded.error } : {},
70
+ hubSites: HUB_SITES,
71
+ };
72
+ }
73
+ get settings() {
74
+ return this.loaded.settings;
75
+ }
76
+ /** The running engine. @throws RuntimeError when it is not ready. */
77
+ engine() {
78
+ if (this.state.kind !== 'ready' || this.worker === undefined)
79
+ throw new RuntimeError('the LuaSkills runtime is not running');
80
+ return this.worker;
81
+ }
82
+ /** Start the installed runtime, if there is one this version can use. */
83
+ async boot() {
84
+ const installed = this.readMarker();
85
+ if (installed === undefined)
86
+ return;
87
+ if (installed.sdk !== PINNED.sdk || installed.luaskills !== PINNED.luaskills || installed.luaRuntime !== PINNED.luaRuntime) {
88
+ this.set({ kind: 'outdated', installed: { sdk: installed.sdk, luaskills: installed.luaskills, luaRuntime: installed.luaRuntime } });
89
+ return;
90
+ }
91
+ await this.start();
92
+ }
93
+ /** Begin installing in the background; progress shows in the state. */
94
+ install() {
95
+ if (this.state.kind === 'installing' || this.state.kind === 'starting')
96
+ throw new RuntimeError('the LuaSkills runtime is busy', 'busy');
97
+ if (this.state.kind === 'ready')
98
+ throw new RuntimeError('the LuaSkills runtime is already installed', 'busy');
99
+ const controller = new AbortController();
100
+ this.installing = controller;
101
+ void this.runInstall(controller.signal).finally(() => {
102
+ if (this.installing === controller)
103
+ this.installing = undefined;
104
+ });
105
+ }
106
+ /** Stop the running install; what it wrote is removed. */
107
+ cancel() {
108
+ this.installing?.abort(new Error('the install was cancelled'));
109
+ }
110
+ /** Stop the engine and delete the runtime; settings are kept. */
111
+ async remove() {
112
+ if (this.state.kind === 'installing' || this.state.kind === 'starting')
113
+ throw new RuntimeError('the LuaSkills runtime is busy', 'busy');
114
+ await this.stopWorker();
115
+ await this.wipe();
116
+ this.set({ kind: 'absent' });
117
+ }
118
+ /** Restart the engine with the current settings. */
119
+ async restart() {
120
+ if (this.state.kind === 'installing' || this.state.kind === 'starting')
121
+ throw new RuntimeError('the LuaSkills runtime is busy', 'busy');
122
+ if (this.readMarker() === undefined)
123
+ throw new RuntimeError('the LuaSkills runtime is not installed');
124
+ await this.start();
125
+ }
126
+ /**
127
+ * Check and save the settings, then restart a running engine so they apply.
128
+ * @throws SettingsError when the value is not valid; nothing is written then.
129
+ */
130
+ async saveSettings(value) {
131
+ if (this.state.kind === 'installing' || this.state.kind === 'starting')
132
+ throw new RuntimeError('the LuaSkills runtime is busy', 'busy');
133
+ const settings = parseSettings(value);
134
+ saveSettings(this.settingsFile, settings);
135
+ this.loaded = { kind: 'saved', settings };
136
+ if (this.state.kind === 'ready' || (this.state.kind === 'failed' && this.state.installed))
137
+ await this.start();
138
+ else
139
+ this.changed();
140
+ }
141
+ async close() {
142
+ this.closed = true;
143
+ this.cancel();
144
+ await this.stopWorker();
145
+ }
146
+ set(state) {
147
+ this.state = state;
148
+ if (!this.closed)
149
+ this.changed();
150
+ }
151
+ sdkEntry() {
152
+ return path.join(packageDir(this.nodeModules, SDK_PACKAGE.name), 'dist', 'index.js');
153
+ }
154
+ readMarker() {
155
+ try {
156
+ const marker = JSON.parse(readFileSync(this.marker, 'utf8'));
157
+ if (typeof marker.sdk !== 'string' || typeof marker.luaskills !== 'string' || typeof marker.luaRuntime !== 'string')
158
+ return undefined;
159
+ if (!existsSync(this.sdkEntry()) || !existsSync(this.runtimeRoot))
160
+ return undefined;
161
+ return marker;
162
+ }
163
+ catch {
164
+ return undefined;
165
+ }
166
+ }
167
+ spawn() {
168
+ const worker = new LuaSkillsWorker(this.sdkEntry(), (error) => {
169
+ if (this.worker !== worker)
170
+ return;
171
+ this.worker = undefined;
172
+ if (this.state.kind === 'ready' || this.state.kind === 'starting')
173
+ this.set({ kind: 'failed', error: error.message, installed: true });
174
+ });
175
+ this.worker = worker;
176
+ return worker;
177
+ }
178
+ async stopWorker() {
179
+ const worker = this.worker;
180
+ this.worker = undefined;
181
+ await worker?.stop();
182
+ }
183
+ async start() {
184
+ await this.stopWorker();
185
+ this.set({ kind: 'starting' });
186
+ try {
187
+ const worker = this.spawn();
188
+ await worker.start({ runtimeRoot: this.runtimeRoot, ...engineOptions(this.loaded.settings) });
189
+ this.set({ kind: 'ready' });
190
+ }
191
+ catch (error) {
192
+ await this.stopWorker();
193
+ this.set({ kind: 'failed', error: messageOf(error), installed: true });
194
+ }
195
+ }
196
+ async wipe() {
197
+ // The marker goes first, so an interrupted removal never looks installed.
198
+ await rm(this.marker, { force: true });
199
+ for (const folder of [path.join(this.home, 'sdk'), this.runtimeRoot, this.scratch]) {
200
+ await rm(folder, { recursive: true, force: true, maxRetries: 3 });
201
+ }
202
+ }
203
+ async runInstall(signal) {
204
+ const progress = (phase, received = 0, total = null) => {
205
+ this.set({ kind: 'installing', phase, received, total });
206
+ };
207
+ const started = Date.now();
208
+ try {
209
+ progress('sdk');
210
+ await this.stopWorker();
211
+ await this.wipe();
212
+ await installPackages(SDK_PACKAGES, this.nodeModules, this.scratch, signal, (_index, received, total) => {
213
+ throttled(() => { progress('sdk', received, total); }, received === total);
214
+ });
215
+ signal.throwIfAborted();
216
+ progress('lua');
217
+ const worker = this.spawn();
218
+ worker.onDownload = (role, received, total) => {
219
+ throttled(() => { progress(role === 'lua_runtime' ? 'lua' : 'core', received, total); }, received === total || received === 0);
220
+ };
221
+ await abortable(worker.installRuntime({ runtimeRoot: this.runtimeRoot, luaskillsVersion: LUASKILLS_VERSION, luaRuntimeVersion: LUA_RUNTIME_VERSION }), signal);
222
+ worker.onDownload = undefined;
223
+ progress('engine');
224
+ await abortable(worker.start({ runtimeRoot: this.runtimeRoot, ...engineOptions(this.loaded.settings) }), signal);
225
+ progress('skill');
226
+ const result = await abortable(worker.install('ROOT', {
227
+ skill_id: DEFAULT_SKILL.skillId, source: DEFAULT_SKILL.source, source_type: DEFAULT_SKILL.sourceType,
228
+ }), signal);
229
+ if (!['installed', 'updated', 'up_to_date'].includes(result.status))
230
+ throw new Error(`${DEFAULT_SKILL.skillId}: ${result.message}`);
231
+ const marker = { ...PINNED, platform: `${process.platform}-${process.arch}`, installedAt: new Date().toISOString() };
232
+ await writeFile(this.marker, `${JSON.stringify(marker, null, 2)}\n`, 'utf8');
233
+ await rm(this.scratch, { recursive: true, force: true });
234
+ this.set({ kind: 'ready' });
235
+ }
236
+ catch (error) {
237
+ const worker = this.worker;
238
+ this.worker = undefined;
239
+ await worker?.kill();
240
+ await this.wipe().catch(() => { });
241
+ if (worker?.pid !== undefined)
242
+ await removeSdkScratch(worker.pid, started).catch(() => { });
243
+ if (signal.aborted)
244
+ this.set({ kind: 'absent' });
245
+ else
246
+ this.set({ kind: 'failed', error: messageOf(error), installed: false });
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * The SDK unpacks runtime archives in `<tmpdir>/luaskills-runtime-assets-<pid>-<ms>` and removes it
252
+ * itself, unless the worker is stopped first; remove the ones this install left behind.
253
+ */
254
+ async function removeSdkScratch(pid, since) {
255
+ const prefix = `luaskills-runtime-assets-${pid}-`;
256
+ for (const name of await readdir(tmpdir())) {
257
+ if (name.startsWith(prefix) && Number(name.slice(prefix.length)) >= since) {
258
+ await rm(path.join(tmpdir(), name), { recursive: true, force: true, maxRetries: 3 });
259
+ }
260
+ }
261
+ }
262
+ let lastReport = 0;
263
+ /** Report download progress at most every 200 ms, and always the last one. */
264
+ function throttled(report, final) {
265
+ const now = Date.now();
266
+ if (!final && now - lastReport < 200)
267
+ return;
268
+ lastReport = now;
269
+ report();
270
+ }
271
+ async function abortable(work, signal) {
272
+ signal.throwIfAborted();
273
+ return await new Promise((resolve, reject) => {
274
+ const onAbort = () => { reject(signal.reason); };
275
+ signal.addEventListener('abort', onAbort, { once: true });
276
+ work.then(resolve, reject).finally(() => { signal.removeEventListener('abort', onAbort); });
277
+ });
278
+ }
279
+ function messageOf(error) {
280
+ return error instanceof Error ? error.message : String(error);
281
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Install the pinned SDK packages into the runtime's own `node_modules`: download each npm
3
+ * tarball, check it against the registry's sha512 integrity, check every archive member stays
4
+ * inside its folder, then unpack it with the system `tar`. No package manager and no install
5
+ * script runs.
6
+ */
7
+ import type { PinnedPackage } from './constants.js';
8
+ /** Where one package lives below a `node_modules` folder. */
9
+ export declare function packageDir(nodeModules: string, name: string): string;
10
+ /**
11
+ * Download one URL whole, reporting bytes as they arrive.
12
+ * @throws when the answer is not 200 or the signal aborts.
13
+ */
14
+ export declare function download(url: string, signal: AbortSignal, progress: (received: number, total: number | null) => void): Promise<Buffer>;
15
+ /** @throws when the bytes do not match a `sha512-<base64>` integrity value. */
16
+ export declare function checkIntegrity(bytes: Buffer, integrity: string, name: string): void;
17
+ /**
18
+ * @throws when an npm tarball member is absolute, climbs out of `package/`, or is a link.
19
+ * npm tarballs hold one top folder (usually `package/`); it is stripped on extraction.
20
+ */
21
+ export declare function checkMembers(listing: string, name: string): void;
22
+ /**
23
+ * Install `packages` below `nodeModules`, each package folder replaced whole.
24
+ * @param progress - bytes of the package downloading now, with its index.
25
+ */
26
+ export declare function installPackages(packages: readonly PinnedPackage[], nodeModules: string, scratch: string, signal: AbortSignal, progress: (index: number, received: number, total: number | null) => void): Promise<void>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Install the pinned SDK packages into the runtime's own `node_modules`: download each npm
3
+ * tarball, check it against the registry's sha512 integrity, check every archive member stays
4
+ * inside its folder, then unpack it with the system `tar`. No package manager and no install
5
+ * script runs.
6
+ */
7
+ import { execFile } from 'node:child_process';
8
+ import { createHash } from 'node:crypto';
9
+ import { mkdir, rm, writeFile } from 'node:fs/promises';
10
+ import path from 'node:path';
11
+ import { promisify } from 'node:util';
12
+ const run = promisify(execFile);
13
+ /** Where one package lives below a `node_modules` folder. */
14
+ export function packageDir(nodeModules, name) {
15
+ return path.join(nodeModules, ...name.split('/'));
16
+ }
17
+ /**
18
+ * Download one URL whole, reporting bytes as they arrive.
19
+ * @throws when the answer is not 200 or the signal aborts.
20
+ */
21
+ export async function download(url, signal, progress) {
22
+ const response = await fetch(url, { signal });
23
+ if (!response.ok || response.body === null)
24
+ throw new Error(`download of ${url} failed: HTTP ${response.status}`);
25
+ const header = Number(response.headers.get('content-length'));
26
+ const total = Number.isSafeInteger(header) && header > 0 ? header : null;
27
+ const chunks = [];
28
+ let received = 0;
29
+ progress(0, total);
30
+ for await (const chunk of response.body) {
31
+ chunks.push(chunk);
32
+ received += chunk.byteLength;
33
+ progress(received, total);
34
+ }
35
+ return Buffer.concat(chunks);
36
+ }
37
+ /** @throws when the bytes do not match a `sha512-<base64>` integrity value. */
38
+ export function checkIntegrity(bytes, integrity, name) {
39
+ const expected = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity)?.[1];
40
+ if (expected === undefined)
41
+ throw new Error(`${name}: unsupported integrity ${integrity}`);
42
+ const actual = createHash('sha512').update(bytes).digest('base64');
43
+ if (actual !== expected)
44
+ throw new Error(`${name}: the download does not match its published integrity`);
45
+ }
46
+ /**
47
+ * @throws when an npm tarball member is absolute, climbs out of `package/`, or is a link.
48
+ * npm tarballs hold one top folder (usually `package/`); it is stripped on extraction.
49
+ */
50
+ export function checkMembers(listing, name) {
51
+ const members = listing.split(/\r?\n/).filter(line => line !== '');
52
+ if (members.length === 0)
53
+ throw new Error(`${name}: the archive is empty`);
54
+ const top = members[0].split('/')[0];
55
+ for (const member of members) {
56
+ const parts = member.split('/');
57
+ if (member.startsWith('/') || /^[A-Za-z]:/.test(member) || member.includes('\\') || parts.includes('..') || parts[0] !== top) {
58
+ throw new Error(`${name}: the archive member ${JSON.stringify(member)} is outside its package folder`);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Install `packages` below `nodeModules`, each package folder replaced whole.
64
+ * @param progress - bytes of the package downloading now, with its index.
65
+ */
66
+ export async function installPackages(packages, nodeModules, scratch, signal, progress) {
67
+ await mkdir(scratch, { recursive: true });
68
+ for (const [index, pkg] of packages.entries()) {
69
+ const bytes = await download(pkg.tarball, signal, (received, total) => { progress(index, received, total); });
70
+ checkIntegrity(bytes, pkg.integrity, pkg.name);
71
+ const file = `${pkg.name.replace('/', '__')}-${pkg.version}.tgz`;
72
+ await writeFile(path.join(scratch, file), bytes);
73
+ // Run beside the archive with relative names, so a GNU tar on PATH never reads a drive colon as a host.
74
+ const tar = (args) => run('tar', args, { cwd: scratch, maxBuffer: 64 * 1024 * 1024, signal });
75
+ const { stdout } = await tar(['-tzf', `./${file}`]);
76
+ checkMembers(stdout, pkg.name);
77
+ // Symbolic and hard links are refused by listing their types as well.
78
+ const { stdout: verbose } = await tar(['-tvzf', `./${file}`]);
79
+ if (verbose.split(/\r?\n/).some(line => /^[lh]/.test(line)))
80
+ throw new Error(`${pkg.name}: the archive contains links`);
81
+ const target = packageDir(nodeModules, pkg.name);
82
+ await rm(target, { recursive: true, force: true });
83
+ await mkdir(target, { recursive: true });
84
+ const relative = path.relative(scratch, target).split(path.sep).join('/');
85
+ await tar(['-xzf', `./${file}`, '-C', relative, '--strip-components=1']);
86
+ }
87
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * LuaSkills runtime settings. Fields, defaults and limits follow VulcanCode's
3
+ * `luaskills-runtime-setting.json`; the settings file belongs to this plugin and is written only
4
+ * when the user saves. A missing file means the defaults, held in memory.
5
+ */
6
+ /** One VM pool: the size it keeps warm, the size it may grow to, and how long an idle VM lives. */
7
+ export interface PoolSettings {
8
+ readonly minSize: number;
9
+ readonly maxSize: number;
10
+ readonly idleTtlSecs: number;
11
+ }
12
+ export interface LuaSkillsSettings {
13
+ /** The main pool skill calls run in. */
14
+ readonly vmPool: PoolSettings;
15
+ /** The isolated pool `runLua` runs in. */
16
+ readonly runLuaPool: PoolSettings;
17
+ readonly toolCache: {
18
+ readonly maxEntries: number;
19
+ readonly defaultTtlSecs: number;
20
+ readonly maxTtlSecs: number;
21
+ };
22
+ readonly managedRuntime: {
23
+ readonly workerPoolMaxSizePerEnvironment: number;
24
+ readonly workerIdleTtlSecs: number;
25
+ readonly persistentSessionLimitPerEngine: number;
26
+ readonly persistentSessionDefaultBufferLimitBytesPerStream: number;
27
+ readonly invokeDefaultTimeoutMs: number;
28
+ };
29
+ readonly capabilities: {
30
+ /** Null leaves the choice to LuaSkills. */
31
+ readonly defaultTextEncoding: TextEncoding | null;
32
+ readonly enableSkillManagementBridge: boolean;
33
+ readonly enableManagedIoCompat: boolean;
34
+ };
35
+ readonly network: {
36
+ readonly allowDownloads: boolean;
37
+ /** Null uses `https://github.com`. */
38
+ readonly githubBaseUrl: string | null;
39
+ /** Null uses `https://api.github.com`. */
40
+ readonly githubApiBaseUrl: string | null;
41
+ readonly officialSkillHubBaseUrl: string;
42
+ };
43
+ }
44
+ export declare const TEXT_ENCODINGS: readonly ["utf-8", "system", "oem", "gbk", "gb18030", "latin1", "base64"];
45
+ export type TextEncoding = (typeof TEXT_ENCODINGS)[number];
46
+ export declare const DEFAULT_SETTINGS: LuaSkillsSettings;
47
+ export declare class SettingsError extends Error {
48
+ readonly code = "invalid_settings";
49
+ }
50
+ /**
51
+ * Check a whole settings value: every field present, nothing extra, counts positive whole
52
+ * numbers, every maximum at least its minimum, URLs absolute http(s).
53
+ * @throws SettingsError naming the first offending field.
54
+ */
55
+ export declare function parseSettings(value: unknown): LuaSkillsSettings;
56
+ /** What reading the settings file found. */
57
+ export type LoadedSettings = {
58
+ readonly kind: 'defaults';
59
+ readonly settings: LuaSkillsSettings;
60
+ } | {
61
+ readonly kind: 'saved';
62
+ readonly settings: LuaSkillsSettings;
63
+ } | {
64
+ readonly kind: 'invalid';
65
+ readonly settings: LuaSkillsSettings;
66
+ readonly error: string;
67
+ };
68
+ /**
69
+ * Read the settings file. An unreadable file is reported and the defaults are used in memory;
70
+ * the file is left untouched so nothing the user wrote is lost.
71
+ */
72
+ export declare function loadSettings(file: string): LoadedSettings;
73
+ /** Write the settings file whole, through a temporary file so a crash leaves the old one. */
74
+ export declare function saveSettings(file: string, settings: LuaSkillsSettings): void;
75
+ /** The engine options a settings value turns into, in the SDK's wire names. */
76
+ export declare function engineOptions(settings: LuaSkillsSettings): {
77
+ poolConfig: Record<string, unknown>;
78
+ hostOptions: Record<string, unknown>;
79
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * LuaSkills runtime settings. Fields, defaults and limits follow VulcanCode's
3
+ * `luaskills-runtime-setting.json`; the settings file belongs to this plugin and is written only
4
+ * when the user saves. A missing file means the defaults, held in memory.
5
+ */
6
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { HUB_SITES } from './constants.js';
9
+ export const TEXT_ENCODINGS = ['utf-8', 'system', 'oem', 'gbk', 'gb18030', 'latin1', 'base64'];
10
+ export const DEFAULT_SETTINGS = {
11
+ vmPool: { minSize: 4, maxSize: 8, idleTtlSecs: 60 },
12
+ runLuaPool: { minSize: 1, maxSize: 4, idleTtlSecs: 30 },
13
+ toolCache: { maxEntries: 1000, defaultTtlSecs: 1800, maxTtlSecs: 1800 },
14
+ managedRuntime: {
15
+ workerPoolMaxSizePerEnvironment: 4,
16
+ workerIdleTtlSecs: 60,
17
+ persistentSessionLimitPerEngine: 256,
18
+ persistentSessionDefaultBufferLimitBytesPerStream: 1_048_576,
19
+ invokeDefaultTimeoutMs: 60_000,
20
+ },
21
+ capabilities: { defaultTextEncoding: null, enableSkillManagementBridge: false, enableManagedIoCompat: true },
22
+ network: { allowDownloads: true, githubBaseUrl: null, githubApiBaseUrl: null, officialSkillHubBaseUrl: HUB_SITES[0].url },
23
+ };
24
+ export class SettingsError extends Error {
25
+ code = 'invalid_settings';
26
+ }
27
+ const POOL = { minSize: 'count', maxSize: 'count', idleTtlSecs: 'count' };
28
+ const SHAPE = {
29
+ vmPool: POOL,
30
+ runLuaPool: POOL,
31
+ toolCache: { maxEntries: 'count', defaultTtlSecs: 'count', maxTtlSecs: 'count' },
32
+ managedRuntime: {
33
+ workerPoolMaxSizePerEnvironment: 'count',
34
+ workerIdleTtlSecs: 'count',
35
+ persistentSessionLimitPerEngine: 'count',
36
+ persistentSessionDefaultBufferLimitBytesPerStream: 'count',
37
+ invokeDefaultTimeoutMs: 'count',
38
+ },
39
+ capabilities: { defaultTextEncoding: 'encoding', enableSkillManagementBridge: 'boolean', enableManagedIoCompat: 'boolean' },
40
+ network: { allowDownloads: 'boolean', githubBaseUrl: 'optional-url', githubApiBaseUrl: 'optional-url', officialSkillHubBaseUrl: 'url' },
41
+ };
42
+ /**
43
+ * Check a whole settings value: every field present, nothing extra, counts positive whole
44
+ * numbers, every maximum at least its minimum, URLs absolute http(s).
45
+ * @throws SettingsError naming the first offending field.
46
+ */
47
+ export function parseSettings(value) {
48
+ check(value, SHAPE, 'settings');
49
+ const settings = value;
50
+ for (const [name, pool] of [['vmPool', settings.vmPool], ['runLuaPool', settings.runLuaPool]]) {
51
+ if (pool.maxSize < pool.minSize)
52
+ throw new SettingsError(`${name}.maxSize must be at least ${name}.minSize`);
53
+ }
54
+ if (settings.toolCache.maxTtlSecs < settings.toolCache.defaultTtlSecs) {
55
+ throw new SettingsError('toolCache.maxTtlSecs must be at least toolCache.defaultTtlSecs');
56
+ }
57
+ return settings;
58
+ }
59
+ function check(value, shape, at) {
60
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
61
+ throw new SettingsError(`${at} must be an object`);
62
+ const record = value;
63
+ for (const key of Object.keys(record)) {
64
+ if (!(key in shape))
65
+ throw new SettingsError(`${at}.${key} is not a setting`);
66
+ }
67
+ for (const [key, kind] of Object.entries(shape)) {
68
+ const field = record[key];
69
+ const name = `${at}.${key}`;
70
+ if (typeof kind === 'object') {
71
+ check(field, kind, name);
72
+ }
73
+ else if (kind === 'count') {
74
+ if (!Number.isSafeInteger(field) || field <= 0)
75
+ throw new SettingsError(`${name} must be a whole number above zero`);
76
+ }
77
+ else if (kind === 'boolean') {
78
+ if (typeof field !== 'boolean')
79
+ throw new SettingsError(`${name} must be true or false`);
80
+ }
81
+ else if (kind === 'encoding') {
82
+ if (field !== null && !TEXT_ENCODINGS.includes(field)) {
83
+ throw new SettingsError(`${name} must be null or one of ${TEXT_ENCODINGS.join(', ')}`);
84
+ }
85
+ }
86
+ else if (!(kind === 'optional-url' && field === null) && !isHttpUrl(field)) {
87
+ throw new SettingsError(`${name} must be an absolute http or https URL`);
88
+ }
89
+ }
90
+ }
91
+ function isHttpUrl(value) {
92
+ if (typeof value !== 'string')
93
+ return false;
94
+ try {
95
+ const url = new URL(value);
96
+ return url.protocol === 'https:' || url.protocol === 'http:';
97
+ }
98
+ catch {
99
+ return false;
100
+ }
101
+ }
102
+ /**
103
+ * Read the settings file. An unreadable file is reported and the defaults are used in memory;
104
+ * the file is left untouched so nothing the user wrote is lost.
105
+ */
106
+ export function loadSettings(file) {
107
+ let raw;
108
+ try {
109
+ raw = readFileSync(file, 'utf8');
110
+ }
111
+ catch (error) {
112
+ if (error.code === 'ENOENT')
113
+ return { kind: 'defaults', settings: DEFAULT_SETTINGS };
114
+ return { kind: 'invalid', settings: DEFAULT_SETTINGS, error: messageOf(error) };
115
+ }
116
+ try {
117
+ return { kind: 'saved', settings: parseSettings(JSON.parse(raw)) };
118
+ }
119
+ catch (error) {
120
+ return { kind: 'invalid', settings: DEFAULT_SETTINGS, error: `${file}: ${messageOf(error)}` };
121
+ }
122
+ }
123
+ /** Write the settings file whole, through a temporary file so a crash leaves the old one. */
124
+ export function saveSettings(file, settings) {
125
+ mkdirSync(path.dirname(file), { recursive: true });
126
+ const temporary = `${file}.${process.pid}.tmp`;
127
+ writeFileSync(temporary, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
128
+ renameSync(temporary, file);
129
+ }
130
+ /** The engine options a settings value turns into, in the SDK's wire names. */
131
+ export function engineOptions(settings) {
132
+ const pool = (value) => ({ min_size: value.minSize, max_size: value.maxSize, idle_ttl_secs: value.idleTtlSecs });
133
+ const managed = settings.managedRuntime;
134
+ return {
135
+ poolConfig: pool(settings.vmPool),
136
+ hostOptions: {
137
+ runlua_pool_config: pool(settings.runLuaPool),
138
+ cache_config: {
139
+ max_entries: settings.toolCache.maxEntries,
140
+ default_ttl_secs: settings.toolCache.defaultTtlSecs,
141
+ max_ttl_secs: settings.toolCache.maxTtlSecs,
142
+ },
143
+ managed_runtime_config: {
144
+ worker_pool_max_size_per_environment: managed.workerPoolMaxSizePerEnvironment,
145
+ worker_idle_ttl_secs: managed.workerIdleTtlSecs,
146
+ persistent_session_limit_per_engine: managed.persistentSessionLimitPerEngine,
147
+ persistent_session_default_buffer_limit_bytes_per_stream: managed.persistentSessionDefaultBufferLimitBytesPerStream,
148
+ invoke_default_timeout_ms: managed.invokeDefaultTimeoutMs,
149
+ },
150
+ capabilities: {
151
+ enable_skill_management_bridge: settings.capabilities.enableSkillManagementBridge,
152
+ enable_managed_io_compat: settings.capabilities.enableManagedIoCompat,
153
+ },
154
+ // Read by the Rust core although the SDK's TypeScript type does not list them.
155
+ ...settings.capabilities.defaultTextEncoding === null ? {} : { default_text_encoding: settings.capabilities.defaultTextEncoding },
156
+ official_skill_hub_base_url: settings.network.officialSkillHubBaseUrl,
157
+ allow_network_download: settings.network.allowDownloads,
158
+ github_base_url: settings.network.githubBaseUrl,
159
+ github_api_base_url: settings.network.githubApiBaseUrl,
160
+ },
161
+ };
162
+ }
163
+ function messageOf(error) {
164
+ return error instanceof Error ? error.message : String(error);
165
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * What is installed in the ROOT and USER skill layers, read from the runtime's folders: the
3
+ * skill folders themselves, their `skill.yaml` name and version, the install record and whether
4
+ * the skill is disabled. Reading changes nothing.
5
+ */
6
+ /** The layers this plugin manages; PROJECT skills are left to the project. */
7
+ export declare const LAYERS: readonly [{
8
+ readonly root: "ROOT";
9
+ readonly folder: "root_skills";
10
+ }, {
11
+ readonly root: "USER";
12
+ readonly folder: "user_skills";
13
+ }];
14
+ export type Layer = (typeof LAYERS)[number]['root'];
15
+ export interface InstalledSkill {
16
+ readonly skillId: string;
17
+ readonly root: Layer;
18
+ readonly name: string;
19
+ readonly version: string;
20
+ readonly enabled: boolean;
21
+ /** Where it was installed from, when an install recorded it. */
22
+ readonly source?: {
23
+ readonly type: string;
24
+ readonly locator: string;
25
+ };
26
+ }
27
+ /** Top-level `key: value` scalars of a small YAML document; nested blocks are skipped. */
28
+ export declare function topLevelScalars(text: string): Map<string, string>;
29
+ /** `source_type` and `locator` from the `source:` block of an install record. */
30
+ export declare function installSource(text: string): {
31
+ type: string;
32
+ locator: string;
33
+ } | undefined;
34
+ export declare function listInstalled(runtimeRoot: string): InstalledSkill[];