getbotons 0.0.0-stage → 0.1.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 (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +505 -2
  3. package/installer/admin.mjs +6 -0
  4. package/installer/botons.mjs +430 -0
  5. package/installer/brain-clis.mjs +28 -0
  6. package/installer/linger.mjs +39 -0
  7. package/installer/output.mjs +27 -0
  8. package/installer/revision.mjs +30 -0
  9. package/installer/shell-path.mjs +66 -0
  10. package/installer/source-lock.json +2891 -0
  11. package/package.json +26 -4
  12. package/payload/LICENSE +21 -0
  13. package/payload/SECURITY.md +33 -0
  14. package/payload/installer/admin.mjs +6 -0
  15. package/payload/installer/botons.mjs +430 -0
  16. package/payload/installer/brain-clis.mjs +28 -0
  17. package/payload/installer/linger.mjs +39 -0
  18. package/payload/installer/output.mjs +27 -0
  19. package/payload/installer/revision.mjs +30 -0
  20. package/payload/installer/shell-path.mjs +66 -0
  21. package/payload/installer/source-lock.json +2891 -0
  22. package/payload/npm-shrinkwrap.json +51 -0
  23. package/payload/package.json +13 -0
  24. package/payload/scripts/backup.ts +47 -0
  25. package/payload/scripts/botons-backup.service +15 -0
  26. package/payload/scripts/botons-backup.timer +11 -0
  27. package/payload/scripts/botons.service +36 -0
  28. package/payload/scripts/check-browser.ts +90 -0
  29. package/payload/scripts/doctor.ts +790 -0
  30. package/payload/scripts/install-backup-timer.ts +52 -0
  31. package/payload/scripts/install-service.ts +206 -0
  32. package/payload/scripts/login.ts +17 -0
  33. package/payload/scripts/migrate-names.ts +47 -0
  34. package/payload/scripts/pair.ts +163 -0
  35. package/payload/scripts/restore.ts +108 -0
  36. package/payload/server/bin/botons +4 -0
  37. package/payload/server/bin/legacy/minion +4 -0
  38. package/payload/server/bin/legacy/tots +4 -0
  39. package/payload/server/src/acting.ts +16 -0
  40. package/payload/server/src/api-keys.ts +544 -0
  41. package/payload/server/src/api.ts +1484 -0
  42. package/payload/server/src/app.ts +546 -0
  43. package/payload/server/src/approvals.ts +873 -0
  44. package/payload/server/src/ask-rules.ts +181 -0
  45. package/payload/server/src/audit.ts +158 -0
  46. package/payload/server/src/auth.ts +497 -0
  47. package/payload/server/src/backup.ts +382 -0
  48. package/payload/server/src/brain-login.ts +75 -0
  49. package/payload/server/src/browser-cert.ts +67 -0
  50. package/payload/server/src/browser-handoff.ts +119 -0
  51. package/payload/server/src/browser-handover.ts +175 -0
  52. package/payload/server/src/browser-policy.ts +210 -0
  53. package/payload/server/src/browser-proxy.ts +580 -0
  54. package/payload/server/src/browser-request.ts +335 -0
  55. package/payload/server/src/browser-screenshot.ts +56 -0
  56. package/payload/server/src/browser-send.ts +155 -0
  57. package/payload/server/src/browser.ts +2164 -0
  58. package/payload/server/src/callback.ts +218 -0
  59. package/payload/server/src/capabilities.ts +90 -0
  60. package/payload/server/src/cards.ts +243 -0
  61. package/payload/server/src/cli/approve-mcp.ts +119 -0
  62. package/payload/server/src/cli/botons.ts +629 -0
  63. package/payload/server/src/cli/browser-mcp.ts +168 -0
  64. package/payload/server/src/cli/workspace-file.ts +78 -0
  65. package/payload/server/src/cli-homes.ts +38 -0
  66. package/payload/server/src/cli-install.ts +104 -0
  67. package/payload/server/src/cli-updates.ts +112 -0
  68. package/payload/server/src/codex-config-repair.ts +80 -0
  69. package/payload/server/src/config.ts +473 -0
  70. package/payload/server/src/connector-access.ts +158 -0
  71. package/payload/server/src/connector-launch.ts +203 -0
  72. package/payload/server/src/connectors.ts +947 -0
  73. package/payload/server/src/disk-usage.ts +62 -0
  74. package/payload/server/src/engine.ts +2328 -0
  75. package/payload/server/src/env-init.ts +5 -0
  76. package/payload/server/src/events.ts +46 -0
  77. package/payload/server/src/files.ts +528 -0
  78. package/payload/server/src/github-access.ts +52 -0
  79. package/payload/server/src/health.ts +117 -0
  80. package/payload/server/src/held-dir.ts +96 -0
  81. package/payload/server/src/hooks.ts +141 -0
  82. package/payload/server/src/http.ts +207 -0
  83. package/payload/server/src/index.ts +139 -0
  84. package/payload/server/src/install-type.ts +11 -0
  85. package/payload/server/src/instructions.ts +204 -0
  86. package/payload/server/src/isolation.ts +234 -0
  87. package/payload/server/src/legacy-upgrade.ts +668 -0
  88. package/payload/server/src/limits.ts +44 -0
  89. package/payload/server/src/lock.ts +75 -0
  90. package/payload/server/src/log.ts +137 -0
  91. package/payload/server/src/managed-clis.ts +65 -0
  92. package/payload/server/src/mcp-client.ts +229 -0
  93. package/payload/server/src/notify.ts +193 -0
  94. package/payload/server/src/oauth-connections.ts +650 -0
  95. package/payload/server/src/oauth.ts +311 -0
  96. package/payload/server/src/outbound.ts +142 -0
  97. package/payload/server/src/portable.ts +1669 -0
  98. package/payload/server/src/prompt.ts +676 -0
  99. package/payload/server/src/providers/api-brain.ts +612 -0
  100. package/payload/server/src/providers/api-history.ts +131 -0
  101. package/payload/server/src/providers/api-tools.ts +481 -0
  102. package/payload/server/src/providers/api-wire.ts +690 -0
  103. package/payload/server/src/providers/claude-cli.ts +620 -0
  104. package/payload/server/src/providers/cli-check.ts +265 -0
  105. package/payload/server/src/providers/cli-turn.ts +20 -0
  106. package/payload/server/src/providers/codex-cli.ts +552 -0
  107. package/payload/server/src/providers/codex-workspace.ts +61 -0
  108. package/payload/server/src/providers/failures.ts +143 -0
  109. package/payload/server/src/providers/index.ts +131 -0
  110. package/payload/server/src/providers/mcp-stdio.ts +147 -0
  111. package/payload/server/src/providers/mock.ts +94 -0
  112. package/payload/server/src/providers/process.ts +268 -0
  113. package/payload/server/src/providers/readiness.ts +43 -0
  114. package/payload/server/src/providers/sse.ts +73 -0
  115. package/payload/server/src/providers/types.ts +173 -0
  116. package/payload/server/src/rates.ts +128 -0
  117. package/payload/server/src/reactions.ts +266 -0
  118. package/payload/server/src/recent-logs.ts +489 -0
  119. package/payload/server/src/rehearsal.ts +593 -0
  120. package/payload/server/src/rehearsals.ts +303 -0
  121. package/payload/server/src/replay.ts +165 -0
  122. package/payload/server/src/sandbox.ts +767 -0
  123. package/payload/server/src/schedule.ts +214 -0
  124. package/payload/server/src/scheduler.ts +24 -0
  125. package/payload/server/src/schema.ts +882 -0
  126. package/payload/server/src/secrets.ts +46 -0
  127. package/payload/server/src/setup.ts +356 -0
  128. package/payload/server/src/skill-packages.ts +363 -0
  129. package/payload/server/src/skills.ts +527 -0
  130. package/payload/server/src/spend.ts +255 -0
  131. package/payload/server/src/ssrf.ts +126 -0
  132. package/payload/server/src/starter-gate.ts +168 -0
  133. package/payload/server/src/starters.ts +610 -0
  134. package/payload/server/src/storage.ts +32 -0
  135. package/payload/server/src/store.ts +3087 -0
  136. package/payload/server/src/tailnet.ts +132 -0
  137. package/payload/server/src/templates.ts +454 -0
  138. package/payload/server/src/trash.ts +772 -0
  139. package/payload/server/src/util.ts +97 -0
  140. package/payload/server/src/webhooks.ts +216 -0
  141. package/payload/server/src/work-feed.ts +473 -0
  142. package/payload/shared/app-version.d.mts +1 -0
  143. package/payload/shared/app-version.mjs +3 -0
  144. package/payload/shared/boton.ts +323 -0
  145. package/payload/shared/brand.ts +107 -0
  146. package/payload/shared/cards.ts +427 -0
  147. package/payload/shared/effort.ts +91 -0
  148. package/payload/shared/files.ts +104 -0
  149. package/payload/shared/handoff.ts +97 -0
  150. package/payload/shared/install-commands.ts +7 -0
  151. package/payload/shared/legacy-names.d.mts +35 -0
  152. package/payload/shared/legacy-names.mjs +244 -0
  153. package/payload/shared/live-view.ts +94 -0
  154. package/payload/shared/look-critter.ts +189 -0
  155. package/payload/shared/look-tater.ts +127 -0
  156. package/payload/shared/look.ts +200 -0
  157. package/payload/shared/reactions.ts +43 -0
  158. package/payload/shared/seeded.ts +34 -0
  159. package/payload/shared/services.ts +90 -0
  160. package/payload/shared/types.ts +3016 -0
  161. package/payload/shared/util.ts +68 -0
  162. package/payload/shared/versions.ts +44 -0
  163. package/payload/shared/wake.ts +164 -0
  164. package/payload/web/dist/apple-touch-icon.png +0 -0
  165. package/payload/web/dist/assets/Icons-nytVAa9g.js +1 -0
  166. package/payload/web/dist/assets/NewBotDialog-DXr2XhDP.js +1 -0
  167. package/payload/web/dist/assets/PairScreen-CMcJO7cQ.js +1 -0
  168. package/payload/web/dist/assets/Panel-Bvqz3I3m.js +4 -0
  169. package/payload/web/dist/assets/ReactionPicker-B0SKsjiy.js +1 -0
  170. package/payload/web/dist/assets/SettingRows-CvLomtuo.js +4 -0
  171. package/payload/web/dist/assets/SettingsDialog-CQ41LV-P.js +8 -0
  172. package/payload/web/dist/assets/Setup-C-3P7Mtn.js +1 -0
  173. package/payload/web/dist/assets/Skills-DXMH69kH.js +1 -0
  174. package/payload/web/dist/assets/StarterSetup-Ce2V1_Cs.js +5 -0
  175. package/payload/web/dist/assets/Steps-CKtB3GAM.js +1 -0
  176. package/payload/web/dist/assets/emoji-catalog-DfpPz8qw.js +540 -0
  177. package/payload/web/dist/assets/index-BEU7FDxV.css +1 -0
  178. package/payload/web/dist/assets/index-Dhwn-rf0.js +3 -0
  179. package/payload/web/dist/assets/react-C1iE8mnY.js +9 -0
  180. package/payload/web/dist/assets/reactions-rw0YeCmA.js +1 -0
  181. package/payload/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  182. package/payload/web/dist/assets/util-Cx5feSJu.js +1 -0
  183. package/payload/web/dist/assets/vendor-B3qdcjGo.js +14 -0
  184. package/payload/web/dist/favicon.svg +1 -0
  185. package/payload/web/dist/icon-192.png +0 -0
  186. package/payload/web/dist/icon-512.png +0 -0
  187. package/payload/web/dist/icon.svg +1 -0
  188. package/payload/web/dist/index.html +67 -0
  189. package/payload/web/dist/manifest.webmanifest +12 -0
  190. package/shared/legacy-names.d.mts +35 -0
  191. package/shared/legacy-names.mjs +244 -0
@@ -0,0 +1,244 @@
1
+ // LEGACY NAMES: the one place that knows what Botons was called before (Minions, then Tots).
2
+ //
3
+ // Everything that still reads, accepts or moves an earlier name gets it from this file, for one release after the
4
+ // rename. To drop them: delete this file and legacy-names.d.mts, delete server/src/legacy-upgrade.ts,
5
+ // scripts/migrate-names.ts, docker/legacy-names.sh and server/test/legacy-names.test.ts, then fix what the type
6
+ // checker lists. None of these names may appear in docs, help text or the app; doctor names them once, as
7
+ // deprecated. Plain JavaScript with no imports, so the server, its scripts, the web app and the installer (which
8
+ // may run on an older Node) can all load it.
9
+
10
+ /** The earlier names as words in text, in any case (check:firstrun looks for them on every screen). */
11
+ export const OLD_TEXT = /\b(minions?|tots?)\b/i;
12
+
13
+ /** Earlier setting prefixes and their current ones, newest first: where two are set, the newer one wins. */
14
+ export const ENV_PREFIXES = Object.freeze([
15
+ ['TOTS_', 'BOTONS_'],
16
+ ['TOT_', 'BOTON_'],
17
+ ['MINIONS_', 'BOTONS_'],
18
+ ['MINION_', 'BOTON_'],
19
+ ]);
20
+
21
+ /** The current name of an earlier setting name, or null when `name` is not one. */
22
+ export function currentEnvName(name) {
23
+ if (!/^[A-Z][A-Z0-9_]*$/.test(name)) return null;
24
+ for (const [old, now] of ENV_PREFIXES) if (name.startsWith(old) && name.length > old.length) return now + name.slice(old.length);
25
+ return null;
26
+ }
27
+
28
+ /** The earlier names of a current setting name, newest first (none when it has no earlier names). */
29
+ export function earlierEnvNames(name) {
30
+ const out = [];
31
+ for (const [old, now] of ENV_PREFIXES) if (name.startsWith(now) && name.length > now.length) out.push(old + name.slice(now.length));
32
+ return out;
33
+ }
34
+
35
+ const isSet = (v) => v !== undefined && v !== null && String(v).trim() !== '';
36
+ const rank = (name) => ENV_PREFIXES.findIndex(([old]) => name.startsWith(old));
37
+
38
+ /**
39
+ * Reads each earlier setting name as its current one, in place: where the current name is unset, it gets the
40
+ * newest earlier name's value. Earlier names are left as they are. Idempotent. One notice per earlier name that is
41
+ * set: `conflict` when its value differs from the one used. Notices name variables, never values.
42
+ */
43
+ export function applyLegacyEnv(env) {
44
+ const notices = [];
45
+ const olds = Object.keys(env)
46
+ .filter((k) => currentEnvName(k) && isSet(env[k]))
47
+ .sort((a, b) => rank(a) - rank(b) || (a < b ? -1 : 1));
48
+ for (const old of olds) {
49
+ const name = currentEnvName(old);
50
+ if (!isSet(env[name])) {
51
+ env[name] = env[old];
52
+ notices.push({ old, name, conflict: false });
53
+ } else notices.push({ old, name, conflict: String(env[name]).trim() !== String(env[old]).trim() });
54
+ }
55
+ return notices;
56
+ }
57
+
58
+ /** Doctor's one line about a notice: names only. */
59
+ export function legacyEnvNoticeText(n) {
60
+ return n.conflict
61
+ ? `${n.old} (deprecated) and ${n.name} are both set, to different values: ${n.name} is used. Remove ${n.old}.`
62
+ : `${n.old} is deprecated and works for this release only: rename it to ${n.name}.`;
63
+ }
64
+
65
+ /** True for an earlier setting name. */
66
+ export const isLegacySetting = (name) => currentEnvName(name) !== null;
67
+
68
+ /**
69
+ * A turn's variables under their earlier names too (BOTON_TOKEN also as the two earlier names), for one release,
70
+ * so an agent's own scripts and saved procedures keep working. The values are the same, so nothing more is exposed.
71
+ */
72
+ export function withLegacyTurnNames(env) {
73
+ const out = { ...env };
74
+ for (const [k, v] of Object.entries(env)) for (const old of earlierEnvNames(k)) if (out[old] === undefined) out[old] = v;
75
+ return out;
76
+ }
77
+
78
+ /**
79
+ * An env file's text with each earlier setting name written as its current one, and each path in `moves`
80
+ * ([from, to]) as its new path. Where a setting is set under more than one name, only the line the server would use
81
+ * stays (the current name, else the newest earlier one), so the file means what it meant.
82
+ */
83
+ export function rewriteEnvText(text, moves = []) {
84
+ const lines = text.split('\n');
85
+ const parse = (line) => /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line);
86
+ const valueOf = (raw) => (/^"(.*)"$/.test(raw) || /^'(.*)'$/.test(raw) ? raw.slice(1, -1) : raw);
87
+ // The line whose value the server uses for each setting: the current name's, else the newest earlier name's, and
88
+ // an empty value counts as unset (as applyLegacyEnv reads them). The last of the same name wins, as in a shell.
89
+ // A name assigned twice means its last assignment (an empty one too): only that line counts.
90
+ const last = new Map();
91
+ lines.forEach((line, i) => {
92
+ const m = parse(line);
93
+ if (m) last.set(m[1], i);
94
+ });
95
+ const winner = new Map();
96
+ lines.forEach((line, i) => {
97
+ const m = parse(line);
98
+ if (!m || last.get(m[1]) !== i) return;
99
+ const now = currentEnvName(m[1]) ?? m[1];
100
+ const r = currentEnvName(m[1]) ? rank(m[1]) : -1;
101
+ const set = isSet(valueOf(m[2]));
102
+ const best = winner.get(now);
103
+ const better = !best || (set && !best.set) || (set === best.set && (r < best.r || (r === best.r && i > best.i)));
104
+ if (better) winner.set(now, { i, r, set, legacy: (best?.legacy ?? false) || r >= 0 });
105
+ else best.legacy ||= r >= 0;
106
+ });
107
+ for (const [name] of last) if (currentEnvName(name)) winner.get(currentEnvName(name)).legacy = true;
108
+ const out = [];
109
+ lines.forEach((line, i) => {
110
+ const m = parse(line);
111
+ let next = line;
112
+ if (m) {
113
+ const now = currentEnvName(m[1]) ?? m[1];
114
+ // Only settings with an earlier name lose lines; anything else stays as it was written.
115
+ if (winner.get(now).legacy && winner.get(now).i !== i) return;
116
+ if (now !== m[1]) next = line.replace(m[1], now);
117
+ for (const [from, to] of moves) next = replacePathPrefix(next, from, to);
118
+ }
119
+ out.push(next);
120
+ });
121
+ return out.join('\n');
122
+ }
123
+
124
+ /** `text` with each whole-path occurrence of `from` (followed by /, a quote, a separator or the end) as `to`. */
125
+ function replacePathPrefix(text, from, to) {
126
+ const esc = from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
127
+ return text.replace(new RegExp(`${esc}(?=$|[/"':;\\s])`, 'g'), to);
128
+ }
129
+
130
+ /** Earlier data folders under the user's home, newest first. The current one is ~/.botons. */
131
+ export const DATA_DIRS = Object.freeze(['.tots', '.minions']);
132
+ /** Earlier settings folders under XDG_CONFIG_HOME (~/.config), each with an `env` file. */
133
+ export const CONFIG_DIRS = Object.freeze(['tots', 'minions']);
134
+ /** Earlier release roots under ~/.local/share (deploy's and the installer's). */
135
+ export const SHARE_DIRS = Object.freeze(['tots', 'minions']);
136
+ /** The database's earlier file name. */
137
+ export const DB_FILE = 'minions.db';
138
+ /** Earlier systemd user units: the server's, and its nightly backup's (a .service and a .timer). */
139
+ export const UNITS = Object.freeze([
140
+ { service: 'tots', backup: 'tots-backup' },
141
+ { service: 'minions', backup: 'minions-backup' },
142
+ ]);
143
+ /** Earlier names of the Botons command (the agents' CLI and the installer's admin command). */
144
+ export const CLI_NAMES = Object.freeze(['tots', 'minion']);
145
+ /** Earlier names in the marked PATH block the installer adds to a shell's rc file. */
146
+ export const PATH_BLOCK_NAMES = Object.freeze(['tots']);
147
+ /** Earlier names of check:public's private terms list in ~/.config/private-terms (outside the repo). */
148
+ export const PRIVATE_TERMS_FILES = Object.freeze(['minions.txt']);
149
+ /** Earlier names of the image's admin command (docker/botons); the Dockerfile links each to it. */
150
+ export const IMAGE_CLI_NAMES = Object.freeze(['tots', 'minions']);
151
+
152
+ /**
153
+ * Earlier names of what the server stores, sends and reads back, by their current name: an actor's kind (audit
154
+ * events, reactions, turn tokens), the managed browser's connector, export file formats, webhook and callback
155
+ * headers. Data under them is moved by the schema upgrade, read on import, accepted or sent beside the current one.
156
+ */
157
+ export const RENAMED = Object.freeze({
158
+ boton: 'minion',
159
+ botons_browser: 'minions_browser',
160
+ 'botons-config': 'minions-config',
161
+ 'botons-sidebar': 'minions-sidebar',
162
+ 'botons-reactions': 'minions-reactions',
163
+ 'x-boton-key': 'x-minion-key',
164
+ 'x-boton-signature': 'x-minion-signature',
165
+ 'x-boton-run': 'x-minion-run',
166
+ });
167
+ /** The earlier name of a renamed value, or null. */
168
+ export const earlierName = (now) => (Object.hasOwn(RENAMED, now) ? RENAMED[now] : null);
169
+ /** The current name of a value, if it is an earlier one; otherwise the value itself. */
170
+ export function currentName(value) {
171
+ for (const [now, old] of Object.entries(RENAMED)) if (value === old) return now;
172
+ return value;
173
+ }
174
+
175
+ /** Earlier keys of the web app's own storage on a device, by current key (or key prefix, ending in "."). */
176
+ const STORAGE = Object.freeze({
177
+ 'botons.deviceToken': 'minions.deviceToken',
178
+ 'botons.avatarMotion': 'minions.avatarMotion',
179
+ 'botons.webAccessSuggestion.': 'tots.webAccessSuggestion.',
180
+ 'botons.theme': 'tots.theme',
181
+ });
182
+ /** A storage key's earlier name, or null (for web/index.html's first paint, which runs before any module loads). */
183
+ export const earlierStorageKey = (key) => (Object.hasOwn(STORAGE, key) ? STORAGE[key] : null);
184
+ /**
185
+ * `key`'s value in `storage`, taken over from its earlier key the first time (written under `key`, the earlier one
186
+ * removed), so a device stays paired and keeps its choices. Null when neither is set.
187
+ */
188
+ export function storedValue(storage, key) {
189
+ const now = storage.getItem(key);
190
+ if (now !== null) return now;
191
+ const entry = Object.entries(STORAGE).find(([k]) => k === key || (k.endsWith('.') && key.startsWith(k)));
192
+ if (!entry) return null;
193
+ const old = entry[0] === key ? entry[1] : entry[1] + key.slice(entry[0].length);
194
+ const value = storage.getItem(old);
195
+ if (value === null) return null;
196
+ try {
197
+ storage.setItem(key, value);
198
+ storage.removeItem(old);
199
+ } catch {}
200
+ return value;
201
+ }
202
+
203
+ /**
204
+ * A configuration export made under an earlier name, with the names this server reads: its format, its Botons'
205
+ * list and the primary Boton's setting, its sidebar's format, a skill's author kind and the managed browser's
206
+ * connector id. Anything
207
+ * else is left as it is; a file under the current names comes back unchanged.
208
+ */
209
+ export function currentPortable(file) {
210
+ if (!file || typeof file !== 'object' || Array.isArray(file) || file.format !== RENAMED['botons-config']) return file;
211
+ const browser = `lib:${RENAMED.botons_browser}`;
212
+ const out = { ...file, format: 'botons-config' };
213
+ // An owner's own connector already named as the browser is now takes a free name first, with every grant of it.
214
+ const map = new Map([[browser, 'lib:botons_browser']]);
215
+ const connectors = Array.isArray(out.connectors) ? out.connectors : [];
216
+ if (connectors.some((c) => c && (c.id === 'lib:botons_browser' || c.name === 'botons_browser'))) {
217
+ const used = new Set(connectors.map((c) => c && c.name));
218
+ let n = 2;
219
+ while (used.has(`botons_browser_${n}`)) n++;
220
+ map.set('lib:botons_browser', `lib:botons_browser_${n}`);
221
+ out.connectors = connectors.map((c) => (c && (c.id === 'lib:botons_browser' || c.name === 'botons_browser') ? { ...c, id: `lib:botons_browser_${n}`, name: `botons_browser_${n}` } : c));
222
+ }
223
+ const ids = (v) => (Array.isArray(v) ? v.map((x) => map.get(x) ?? x) : v);
224
+ if (Array.isArray(out.connectors)) out.connectors = out.connectors.map((c) => (c && c.id === browser ? { ...c, id: 'lib:botons_browser', name: 'botons_browser' } : c));
225
+ if ('minions' in out) {
226
+ out.botons = Array.isArray(out.minions) ? out.minions.map((m) => (m && typeof m === 'object' && !Array.isArray(m) ? { ...m, connections: ids(m.connections) } : m)) : out.minions;
227
+ delete out.minions;
228
+ }
229
+ if (out.settings && typeof out.settings === 'object' && !Array.isArray(out.settings) && 'primaryMinion' in out.settings) {
230
+ const { primaryMinion, ...settings } = out.settings;
231
+ out.settings = { ...settings, primaryBoton: primaryMinion };
232
+ }
233
+ if (out.sidebar && typeof out.sidebar === 'object' && out.sidebar.format === RENAMED['botons-sidebar']) out.sidebar = { ...out.sidebar, format: 'botons-sidebar' };
234
+ if (Array.isArray(out.skills)) {
235
+ out.skills = out.skills.map((k) => (k && typeof k === 'object' && k.author && typeof k.author === 'object' && k.author.kind === RENAMED.boton ? { ...k, author: { ...k.author, kind: 'boton' } } : k));
236
+ }
237
+ if (Array.isArray(out.templates)) out.templates = out.templates.map((t) => (t && typeof t === 'object' && t.bot && typeof t.bot === 'object' ? { ...t, bot: { ...t.bot, connections: ids(t.bot.connections) } } : t));
238
+ return out;
239
+ }
240
+
241
+ /** The earlier names themselves, as words: check:public refuses them anywhere this module doesn't allow (oldNames). */
242
+ export const OLD_WORD = /^(minions?|tots?)$/i;
243
+ /** The changelog's one line that says what the product was called before. */
244
+ export const FORMERLY = /\(formerly Minions, and then Tots\)/;