@takodotid/circuit 0.34.3 → 0.35.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 (240) hide show
  1. package/README.md +13 -14
  2. package/dist/chunks/index-20kcjxjg.js +174 -0
  3. package/dist/chunks/index-2r4bbhac.js +977 -0
  4. package/dist/chunks/index-n7w3tg5y.js +149 -0
  5. package/dist/chunks/new-w3zys5y4.js +172 -0
  6. package/dist/chunks/server-gzsf5me5.js +259 -0
  7. package/dist/cli/index.js +11345 -9024
  8. package/dist/helpers/index.js +594 -0
  9. package/dist/index.js +9 -28
  10. package/dist/types/core/{addresses.d.ts → address-space.d.ts} +23 -7
  11. package/dist/types/core/address.d.ts +44 -0
  12. package/dist/types/core/cabling.d.ts +41 -0
  13. package/dist/types/core/cross-checks.d.ts +28 -0
  14. package/dist/types/core/define.d.ts +25 -3
  15. package/dist/types/core/project-files.d.ts +12 -0
  16. package/dist/types/core/schema-docs.d.ts +13 -4
  17. package/dist/types/core/secrets.d.ts +62 -21
  18. package/dist/types/core/{graph.d.ts → topology.d.ts} +32 -12
  19. package/dist/types/core/utils.d.ts +8 -0
  20. package/dist/types/core/validator.d.ts +19 -0
  21. package/dist/types/helpers/bgp.d.ts +97 -0
  22. package/dist/types/helpers/checks.d.ts +32 -0
  23. package/dist/types/helpers/exchange.d.ts +49 -0
  24. package/dist/types/helpers/filters.d.ts +51 -0
  25. package/dist/types/helpers/index.d.ts +10 -0
  26. package/dist/types/helpers/route-server.d.ts +52 -0
  27. package/dist/types/index.d.ts +5 -3
  28. package/dist/types/integrations/librenms.d.ts +60 -0
  29. package/dist/types/integrations/netbox.d.ts +89 -0
  30. package/dist/types/integrations/peeringdb.d.ts +24 -0
  31. package/dist/types/integrations/prometheus.d.ts +24 -0
  32. package/dist/types/integrations/registries.d.ts +8 -0
  33. package/dist/types/platforms/bgp-wait.d.ts +18 -0
  34. package/dist/types/platforms/catalog.d.ts +109 -0
  35. package/dist/types/platforms/derive.d.ts +17 -0
  36. package/dist/types/platforms/fortios/firewall.d.ts +45 -0
  37. package/dist/types/platforms/fortios/index.d.ts +134 -0
  38. package/dist/types/platforms/fortios/names.d.ts +52 -0
  39. package/dist/types/platforms/fortios/plan.d.ts +104 -0
  40. package/dist/types/platforms/fortios/render.d.ts +75 -0
  41. package/dist/types/platforms/fortios/routing.d.ts +56 -0
  42. package/dist/types/platforms/fortios/tree.d.ts +37 -0
  43. package/dist/types/platforms/index.d.ts +13 -0
  44. package/dist/types/platforms/junos/bgp.d.ts +44 -0
  45. package/dist/types/platforms/junos/index.d.ts +114 -0
  46. package/dist/types/platforms/junos/render.d.ts +154 -0
  47. package/dist/types/platforms/line-diff.d.ts +67 -0
  48. package/dist/types/platforms/platform.d.ts +120 -0
  49. package/dist/types/platforms/raisecom-ros/index.d.ts +71 -0
  50. package/dist/types/platforms/raisecom-ros/render.d.ts +41 -0
  51. package/dist/types/platforms/routeros/export.d.ts +59 -0
  52. package/dist/types/platforms/routeros/index.d.ts +96 -0
  53. package/dist/types/{adapters/devices → platforms}/routeros/models.d.ts +4 -0
  54. package/dist/types/platforms/routeros/plan.d.ts +54 -0
  55. package/dist/types/platforms/routeros/render.d.ts +240 -0
  56. package/dist/types/platforms/vrp/index.d.ts +83 -0
  57. package/dist/types/platforms/vrp/render.d.ts +96 -0
  58. package/dist/types/schema/common.d.ts +12 -6
  59. package/dist/types/schema/index.d.ts +1 -0
  60. package/dist/types/schema/interfaces.d.ts +14 -6
  61. package/dist/types/schema/policy.d.ts +18 -10
  62. package/dist/types/schema/routing.d.ts +14 -1
  63. package/dist/types/schema/system.d.ts +16 -6
  64. package/dist/types/schema/time-zones.d.ts +2 -0
  65. package/dist/types/transport/console.d.ts +79 -23
  66. package/dist/types/transport/path-check.d.ts +32 -0
  67. package/dist/types/transport/ssh.d.ts +17 -15
  68. package/package.json +17 -11
  69. package/src/cli/graph-format.ts +91 -0
  70. package/src/cli/index.ts +1074 -484
  71. package/src/cli/new.ts +208 -175
  72. package/src/cli/rename.ts +97 -91
  73. package/src/core/address-space.ts +320 -0
  74. package/src/core/address.ts +202 -0
  75. package/src/core/cabling.ts +154 -0
  76. package/src/core/cross-checks.ts +364 -0
  77. package/src/core/define.ts +26 -3
  78. package/src/core/project-files.ts +35 -0
  79. package/src/core/schema-docs.ts +138 -130
  80. package/src/core/secrets.ts +170 -76
  81. package/src/core/topology.ts +656 -0
  82. package/src/core/utils.ts +37 -0
  83. package/src/core/validator.ts +407 -0
  84. package/src/helpers/bgp.ts +301 -0
  85. package/src/helpers/checks.ts +178 -0
  86. package/src/helpers/exchange.ts +117 -0
  87. package/src/helpers/filters.ts +107 -0
  88. package/src/helpers/index.ts +12 -0
  89. package/src/helpers/route-server.ts +212 -0
  90. package/src/index.ts +6 -3
  91. package/src/integrations/librenms.ts +291 -0
  92. package/src/integrations/netbox.ts +620 -0
  93. package/src/integrations/peeringdb.ts +173 -0
  94. package/src/integrations/prometheus.ts +90 -0
  95. package/src/integrations/registries.ts +25 -0
  96. package/src/platforms/bgp-wait.ts +41 -0
  97. package/src/platforms/catalog.ts +69 -0
  98. package/src/platforms/derive.ts +117 -0
  99. package/src/{adapters/devices → platforms}/fortios/TRAPS.md +6 -0
  100. package/src/platforms/fortios/firewall.ts +377 -0
  101. package/src/platforms/fortios/index.ts +1060 -0
  102. package/src/platforms/fortios/names.ts +130 -0
  103. package/src/platforms/fortios/plan.ts +929 -0
  104. package/src/platforms/fortios/render.ts +779 -0
  105. package/src/platforms/fortios/routing.ts +476 -0
  106. package/src/platforms/fortios/tree.ts +129 -0
  107. package/src/platforms/index.ts +36 -0
  108. package/src/{adapters/devices → platforms}/junos/TRAPS.md +8 -0
  109. package/src/platforms/junos/bgp.ts +293 -0
  110. package/src/platforms/junos/index.ts +1006 -0
  111. package/src/platforms/junos/render.ts +967 -0
  112. package/src/platforms/line-diff.ts +244 -0
  113. package/src/platforms/platform.ts +122 -0
  114. package/src/platforms/raisecom-ros/TRAPS.md +40 -0
  115. package/src/platforms/raisecom-ros/index.ts +731 -0
  116. package/src/platforms/raisecom-ros/render.ts +328 -0
  117. package/src/{adapters/devices → platforms}/routeros/TRAPS.md +16 -2
  118. package/src/platforms/routeros/export.ts +342 -0
  119. package/src/platforms/routeros/index.ts +636 -0
  120. package/src/{adapters/devices → platforms}/routeros/models.ts +2 -0
  121. package/src/platforms/routeros/plan.ts +444 -0
  122. package/src/platforms/routeros/render.ts +1483 -0
  123. package/src/platforms/vrp/TRAPS.md +45 -0
  124. package/src/platforms/vrp/index.ts +652 -0
  125. package/src/platforms/vrp/render.ts +615 -0
  126. package/src/schema/common.ts +15 -6
  127. package/src/schema/index.ts +1 -0
  128. package/src/schema/interfaces.ts +15 -5
  129. package/src/schema/policy.ts +22 -10
  130. package/src/schema/routing.ts +16 -1
  131. package/src/schema/system.ts +16 -6
  132. package/src/schema/time-zones.ts +482 -0
  133. package/src/studio/data.ts +172 -168
  134. package/src/studio/git.ts +95 -90
  135. package/src/studio/public/app.js +161 -150
  136. package/src/studio/public/compare.js +219 -212
  137. package/src/studio/public/diff.js +77 -75
  138. package/src/studio/public/dom.js +118 -108
  139. package/src/studio/public/evaluate.js +584 -545
  140. package/src/studio/public/export.js +120 -118
  141. package/src/studio/public/graph.js +1343 -1266
  142. package/src/studio/public/net.js +115 -109
  143. package/src/studio/public/tables.js +1135 -1083
  144. package/src/studio/public/tools.js +561 -529
  145. package/src/studio/server.ts +202 -196
  146. package/src/transport/console.ts +363 -271
  147. package/src/transport/path-check.ts +91 -0
  148. package/src/transport/ssh.ts +98 -94
  149. package/templates/colocation/checks.ts +11 -6
  150. package/templates/colocation/edge-01.ts +9 -9
  151. package/templates/colocation/routing.ts +2 -2
  152. package/templates/colocation/tenancy.ts +3 -3
  153. package/templates/colocation/tor-01.ts +3 -3
  154. package/templates/edge-router/checks.ts +4 -4
  155. package/templates/edge-router/edge-01.ts +5 -5
  156. package/templates/edge-router/routing.ts +2 -2
  157. package/templates/project/AGENTS.md +5 -1
  158. package/templates/single-site/checks.ts +2 -2
  159. package/dist/chunks/index-47debt16.js +0 -956
  160. package/dist/chunks/index-v8zgr7wm.js +0 -128
  161. package/dist/chunks/new-ny89s7y7.js +0 -166
  162. package/dist/chunks/server-tjbb3rqc.js +0 -251
  163. package/dist/presets/index.js +0 -394
  164. package/dist/types/adapters/devices/catalog.d.ts +0 -86
  165. package/dist/types/adapters/devices/derive.d.ts +0 -7
  166. package/dist/types/adapters/devices/fortios/firewall.d.ts +0 -23
  167. package/dist/types/adapters/devices/fortios/index.d.ts +0 -58
  168. package/dist/types/adapters/devices/fortios/names.d.ts +0 -48
  169. package/dist/types/adapters/devices/fortios/plan.d.ts +0 -28
  170. package/dist/types/adapters/devices/fortios/render.d.ts +0 -38
  171. package/dist/types/adapters/devices/fortios/routing.d.ts +0 -32
  172. package/dist/types/adapters/devices/fortios/tree.d.ts +0 -33
  173. package/dist/types/adapters/devices/index.d.ts +0 -3
  174. package/dist/types/adapters/devices/junos/bgp.d.ts +0 -11
  175. package/dist/types/adapters/devices/junos/index.d.ts +0 -42
  176. package/dist/types/adapters/devices/junos/render.d.ts +0 -52
  177. package/dist/types/adapters/devices/lines.d.ts +0 -56
  178. package/dist/types/adapters/devices/raisecom-ros/index.d.ts +0 -28
  179. package/dist/types/adapters/devices/routeros/export.d.ts +0 -39
  180. package/dist/types/adapters/devices/routeros/index.d.ts +0 -30
  181. package/dist/types/adapters/devices/routeros/plan.d.ts +0 -4
  182. package/dist/types/adapters/devices/routeros/render.d.ts +0 -100
  183. package/dist/types/adapters/devices/types.d.ts +0 -40
  184. package/dist/types/adapters/devices/vrp/index.d.ts +0 -27
  185. package/dist/types/adapters/registries/index.d.ts +0 -3
  186. package/dist/types/adapters/registries/librenms.d.ts +0 -15
  187. package/dist/types/adapters/registries/peeringdb.d.ts +0 -16
  188. package/dist/types/adapters/registries/prometheus.d.ts +0 -16
  189. package/dist/types/core/across.d.ts +0 -4
  190. package/dist/types/core/addr.d.ts +0 -34
  191. package/dist/types/core/files.d.ts +0 -6
  192. package/dist/types/core/merge.d.ts +0 -4
  193. package/dist/types/core/time.d.ts +0 -2
  194. package/dist/types/core/validate.d.ts +0 -2
  195. package/dist/types/presets/index.d.ts +0 -183
  196. package/src/adapters/devices/catalog.ts +0 -41
  197. package/src/adapters/devices/derive.ts +0 -59
  198. package/src/adapters/devices/fortios/firewall.ts +0 -362
  199. package/src/adapters/devices/fortios/index.ts +0 -923
  200. package/src/adapters/devices/fortios/names.ts +0 -115
  201. package/src/adapters/devices/fortios/plan.ts +0 -888
  202. package/src/adapters/devices/fortios/render.ts +0 -768
  203. package/src/adapters/devices/fortios/routing.ts +0 -444
  204. package/src/adapters/devices/fortios/tree.ts +0 -121
  205. package/src/adapters/devices/index.ts +0 -9
  206. package/src/adapters/devices/junos/bgp.ts +0 -269
  207. package/src/adapters/devices/junos/index.ts +0 -862
  208. package/src/adapters/devices/junos/render.ts +0 -872
  209. package/src/adapters/devices/lines.ts +0 -212
  210. package/src/adapters/devices/raisecom-ros/TRAPS.md +0 -26
  211. package/src/adapters/devices/raisecom-ros/index.ts +0 -700
  212. package/src/adapters/devices/routeros/export.ts +0 -321
  213. package/src/adapters/devices/routeros/index.ts +0 -346
  214. package/src/adapters/devices/routeros/plan.ts +0 -398
  215. package/src/adapters/devices/routeros/render.ts +0 -1490
  216. package/src/adapters/devices/types.ts +0 -38
  217. package/src/adapters/devices/vrp/TRAPS.md +0 -26
  218. package/src/adapters/devices/vrp/index.ts +0 -865
  219. package/src/adapters/registries/index.ts +0 -19
  220. package/src/adapters/registries/librenms.ts +0 -92
  221. package/src/adapters/registries/peeringdb.ts +0 -169
  222. package/src/adapters/registries/prometheus.ts +0 -71
  223. package/src/cli/graph.ts +0 -89
  224. package/src/core/across.ts +0 -350
  225. package/src/core/addr.ts +0 -174
  226. package/src/core/addresses.ts +0 -316
  227. package/src/core/files.ts +0 -32
  228. package/src/core/graph.ts +0 -640
  229. package/src/core/merge.ts +0 -19
  230. package/src/core/time.ts +0 -15
  231. package/src/core/validate.ts +0 -348
  232. package/src/presets/index.ts +0 -579
  233. /package/dist/types/{adapters/devices → platforms}/fortios/models.d.ts +0 -0
  234. /package/dist/types/{adapters/devices → platforms}/junos/models.d.ts +0 -0
  235. /package/dist/types/{adapters/devices → platforms}/raisecom-ros/models.d.ts +0 -0
  236. /package/dist/types/{adapters/devices → platforms}/vrp/models.d.ts +0 -0
  237. /package/src/{adapters/devices → platforms}/fortios/models.ts +0 -0
  238. /package/src/{adapters/devices → platforms}/junos/models.ts +0 -0
  239. /package/src/{adapters/devices → platforms}/raisecom-ros/models.ts +0 -0
  240. /package/src/{adapters/devices → platforms}/vrp/models.ts +0 -0
package/README.md CHANGED
@@ -9,16 +9,16 @@ Documentation: [circuit.tako.id](https://circuit.tako.id)
9
9
 
10
10
  ## What it does
11
11
 
12
- - **One file per device.** VLANs, ports, addresses, routing, firewall, users and services, written in TypeScript. Your editor checks every name you refer to as you type.
13
- - **The same words for every vendor.** MikroTik RouterOS 7, Huawei VRP, Raisecom ROS, Juniper Junos and Fortinet FortiOS each get their own commands. What a device cannot do is an error before anything is sent.
14
- - **The file is the whole truth.** A port, user or service the file does not mention is removed or turned off, so nothing set by hand stays behind.
15
- - **See every change first.** `diff` shows it offline, `apply` plans it against the live device, and nothing is sent without `--confirm`. On RouterOS, Junos and FortiOS, a change undoes itself if Circuit cannot log in again afterwards.
16
- - **History in git.** Each device's last snapshot is kept in `.circuit/state/`, so the repository's history is the network's history.
17
- - **Rules of your own.** Checks run on every change, and presets give ready-made filters, BGP policies and checks.
18
- - **Secrets stay out of the repository.** They come from `.env` or 1Password, and never appear in a plan or a snapshot.
19
- - **See the network.** Circuit Studio, started with `circuit studio`, draws the cables, VLANs, addresses, BGP sessions, OSPF neighbors and tunnels in your browser, and marks what is broken or differs from the device. Its tables list everything each device has, for anyone who does not read TypeScript, and it can run on a server for the whole team. `circuit graph` prints the drawing as Mermaid, DOT or JSON.
20
- - **Keeps the rest up to date.** Prefix lists fetched from a registry, your PeeringDB record, devices in LibreNMS, targets for Prometheus, WireGuard client configs, and your BGP communities for a looking glass.
21
- - **Safe with AI agents.** Every project gets an `AGENTS.md` that tells an agent what it may run, and the last step stays yours.
12
+ - **One file per device.** VLANs, ports, addresses, routing, firewall, users and services in TypeScript. Your editor checks every name as you type.
13
+ - **Same words for every vendor.** MikroTik RouterOS 7, Huawei VRP, Raisecom ROS, Juniper Junos and Fortinet FortiOS each get their own commands. Anything a device can't do is an error before anything is sent.
14
+ - **The file is the whole truth.** A port, user or service the file doesn't mention is removed or turned off, so nothing set by hand stays behind.
15
+ - **See every change first.** `diff` shows it offline, `apply` plans it against the live device, and nothing is sent without `--confirm`. On RouterOS, Junos and FortiOS a change undoes itself if Circuit can't log in again afterwards.
16
+ - **History in git.** Each device's last snapshot lives in `.circuit/state/`, so the repo's history is the network's history.
17
+ - **Your own rules.** Checks run on every change; helpers give ready-made filters, BGP policies and checks.
18
+ - **Secrets stay out of the repo.** They come from `.env` or 1Password and never show up in a plan or a snapshot.
19
+ - **See the network.** `circuit studio` draws cables, VLANs, addresses, BGP, OSPF and tunnels in your browser, marks what's broken or differs from the device, and lists everything in tables for people who don't read TypeScript. It can run on a server for the whole team. `circuit graph` prints the drawing as Mermaid, DOT or JSON.
20
+ - **Keeps the rest in step.** Prefix lists from a registry, your PeeringDB record, LibreNMS, NetBox, Prometheus targets, WireGuard client configs, and your BGP communities for a looking glass.
21
+ - **Safe with AI agents.** Every project gets an `AGENTS.md` that says what an agent may run; the last step stays yours.
22
22
 
23
23
  <picture>
24
24
  <source media="(prefers-color-scheme: dark)" srcset="docs/public/screenshots/studio-dark.png">
@@ -44,19 +44,18 @@ npx @takodotid/circuit new # or: pnpm dlx @takodotid/circuit new, bunx @t
44
44
 
45
45
  It asks which kind of network you have, creates a project with example devices, and installs Circuit. [Getting started](https://circuit.tako.id/guide/getting-started) walks through the first run.
46
46
 
47
- Circuit supports MikroTik RouterOS 7, Huawei VRP, Raisecom ROS, Juniper Junos and Fortinet FortiOS. Releases are on the [releases page](https://github.com/takodotid/circuit/releases), on npm and on GitHub Packages.
47
+ Circuit supports MikroTik RouterOS 7, Huawei VRP V200R005, Raisecom ROS 6.5, Juniper Junos 21 and Fortinet FortiOS 6.2, at the versions on the [platforms page](https://circuit.tako.id/platforms/). Releases are on the [releases page](https://github.com/takodotid/circuit/releases), on npm and on GitHub Packages.
48
48
 
49
49
  ## Working on Circuit
50
50
 
51
51
  ```bash
52
52
  bun install
53
- bun run typecheck
53
+ bun run check # everything CI runs: typecheck, tests, format, docs build, Node build
54
54
  bun run test # every example and template validates, and plans nothing against its own render
55
- bun run build # dist/, what Node, npm and pnpm run
56
55
  bun run docs:dev # the documentation site, from docs/
57
56
  ```
58
57
 
59
- [Contributing](https://circuit.tako.id/contributing) explains the layout, how to add a platform, and how releases are cut. `AGENTS.md` has the rules for changing the code.
58
+ [Contributing](https://circuit.tako.id/contributing) explains the layout, how to add a platform, and how releases are cut. `AGENTS.md` has the rules for changing the code, for people and AI agents alike; `.claude/skills/` has the step-by-step guides an agent loads, such as adding a device.
60
59
 
61
60
  ## License
62
61
 
@@ -0,0 +1,174 @@
1
+ // src/core/utils.ts
2
+ class Utils2 {
3
+ static merge(...records) {
4
+ const result = {};
5
+ for (const record of records) {
6
+ for (const [key, value] of Object.entries(record)) {
7
+ if (key in result)
8
+ throw new Error(`Utils.merge: ${key} is declared twice`);
9
+ result[key] = value;
10
+ }
11
+ }
12
+ return result;
13
+ }
14
+ static timeZoneOffset(zone) {
15
+ const name = new Intl.DateTimeFormat("en-US", {
16
+ timeZone: zone,
17
+ timeZoneName: "longOffset"
18
+ }).formatToParts(new Date).find((entry) => entry.type === "timeZoneName").value;
19
+ const found = /GMT([+-])(\d{2}):(\d{2})/.exec(name);
20
+ if (!found)
21
+ return 0;
22
+ const [, sign, hours, minutes] = found;
23
+ const sizeInMinutes = Number(hours) * 60 + Number(minutes);
24
+ return sign === "-" ? -sizeInMinutes : sizeInMinutes;
25
+ }
26
+ }
27
+
28
+ // src/core/secrets.ts
29
+ import { spawnSync } from "node:child_process";
30
+ import { existsSync, readFileSync } from "node:fs";
31
+ import { join } from "node:path";
32
+ import { parseEnv } from "node:util";
33
+
34
+ class Secrets2 {
35
+ static root = process.cwd();
36
+ static envFile;
37
+ static fromOnePassword = new Map;
38
+ static revealed = new Set;
39
+ static REDACTED = "<redacted>";
40
+ static SHORTEST_DOTENV_VALUE = 6;
41
+ static MARKER = /<secret:([A-Za-z0-9_./:-]+)>/g;
42
+ static useRoot(directory) {
43
+ Secrets2.root = directory;
44
+ Secrets2.envFile = undefined;
45
+ }
46
+ static dotenv() {
47
+ if (Secrets2.envFile)
48
+ return Secrets2.envFile;
49
+ const path = join(Secrets2.root, ".env");
50
+ const text = existsSync(path) ? readFileSync(path, "utf8") : "";
51
+ Secrets2.refuseCutValues(text);
52
+ Secrets2.envFile = parseEnv(text);
53
+ return Secrets2.envFile;
54
+ }
55
+ static refuseCutValues(text) {
56
+ for (const line of text.split(/\r?\n/)) {
57
+ const [, name, value] = /^\s*(?:export\s+)?([\w.-]+)\s*=\s*(.*)$/.exec(line) ?? [];
58
+ if (!name || !value || /^['"`]/.test(value) || !/\S#/.test(value))
59
+ continue;
60
+ throw new Error(`.env: the value of ${name} has a # in it, which starts a comment there; put the value in quotes`);
61
+ }
62
+ }
63
+ static readOnePassword(name, reference) {
64
+ const cached = Secrets2.fromOnePassword.get(reference);
65
+ if (cached !== undefined)
66
+ return cached;
67
+ const result = spawnSync("op", ["read", "--no-newline", reference], { encoding: "utf8" });
68
+ if (result.error)
69
+ throw new Error(`secret ${name} is a 1Password reference, and the 1Password CLI, op, is not installed`);
70
+ if (result.status !== 0) {
71
+ const reason = result.stderr.trim() || `op exited with ${result.status}`;
72
+ throw new Error(`secret ${name}: 1Password could not read ${reference}: ${reason}`);
73
+ }
74
+ const value = result.stdout;
75
+ Secrets2.fromOnePassword.set(reference, value);
76
+ return value;
77
+ }
78
+ static isSecret(value) {
79
+ return typeof value === "object" && value !== null && (("secret" in value) || ("secret_file" in value));
80
+ }
81
+ static referenceOf(secret) {
82
+ return "secret" in secret ? secret.secret : `file:${secret.secret_file}`;
83
+ }
84
+ static sourceOf(reference) {
85
+ if (reference.startsWith("file:"))
86
+ return existsSync(join(Secrets2.root, reference.slice("file:".length))) ? "a file" : undefined;
87
+ const fromFile = Secrets2.dotenv()[reference];
88
+ const value = process.env[reference] || fromFile;
89
+ if (!value)
90
+ return;
91
+ const where = process.env[reference] && process.env[reference] !== fromFile ? "the environment" : ".env";
92
+ return value.startsWith("op://") ? `1Password, through ${where}` : where;
93
+ }
94
+ static valueOf(reference) {
95
+ if (reference.startsWith("file:")) {
96
+ const path = reference.slice("file:".length);
97
+ const fullPath = join(Secrets2.root, path);
98
+ if (!existsSync(fullPath))
99
+ throw new Error(`secret file ${path} does not exist`);
100
+ const value = readFileSync(fullPath, "utf8").trimEnd();
101
+ if (!value)
102
+ throw new Error(`secret file ${path} is empty`);
103
+ Secrets2.revealed.add(value);
104
+ return value;
105
+ }
106
+ const fromFile = Secrets2.dotenv()[reference];
107
+ const value = process.env[reference] || fromFile;
108
+ if (!value)
109
+ throw new Error(`secret ${reference} is not set in the environment or in .env`);
110
+ const revealed = value.startsWith("op://") ? Secrets2.readOnePassword(reference, value) : value;
111
+ Secrets2.revealed.add(revealed);
112
+ return revealed;
113
+ }
114
+ static reveal(secret) {
115
+ return Secrets2.valueOf(typeof secret === "string" ? secret : Secrets2.referenceOf(secret));
116
+ }
117
+ static referencesIn(value, found = new Set) {
118
+ if (Secrets2.isSecret(value))
119
+ found.add(Secrets2.referenceOf(value));
120
+ else if (Array.isArray(value))
121
+ for (const item of value)
122
+ Secrets2.referencesIn(item, found);
123
+ else if (typeof value === "object" && value !== null)
124
+ for (const item of Object.values(value))
125
+ Secrets2.referencesIn(item, found);
126
+ return found;
127
+ }
128
+ static scrub(text, options = {}) {
129
+ const values = new Set(options.extra ?? []);
130
+ for (const value of Object.values(Secrets2.dotenv())) {
131
+ if (value.length >= Secrets2.SHORTEST_DOTENV_VALUE && !value.startsWith("op://"))
132
+ values.add(value);
133
+ }
134
+ for (const value of Secrets2.revealed)
135
+ values.add(value);
136
+ for (const reference of Secrets2.referencesIn(options.network)) {
137
+ const value = Secrets2.storedValueOf(reference);
138
+ if (value)
139
+ values.add(value);
140
+ }
141
+ const ordered = [...values].filter(Boolean).sort((a, b) => b.length - a.length);
142
+ return ordered.reduce((out, value) => out.split(value).join(Secrets2.REDACTED), text);
143
+ }
144
+ static storedValueOf(reference) {
145
+ if (reference.startsWith("file:")) {
146
+ const path = join(Secrets2.root, reference.slice("file:".length));
147
+ return existsSync(path) ? readFileSync(path, "utf8").trimEnd() : undefined;
148
+ }
149
+ const value = process.env[reference] || Secrets2.dotenv()[reference];
150
+ if (!value?.startsWith("op://"))
151
+ return value;
152
+ return Secrets2.fromOnePassword.get(value);
153
+ }
154
+ static loginOf(device) {
155
+ const user = device.users?.[device.connection.user];
156
+ if (!user)
157
+ throw new Error(`${device.name}: connection user ${device.connection.user} is not one of its users`);
158
+ return {
159
+ ...device.connection.host ? { host: device.connection.host } : {},
160
+ port: device.connection.port ?? 22,
161
+ user: device.connection.user,
162
+ password: Secrets2.reveal(user.password),
163
+ ...device.connection.console ? { console: device.connection.console } : {}
164
+ };
165
+ }
166
+ static marker(secret) {
167
+ return `<secret:${Secrets2.referenceOf(secret)}>`;
168
+ }
169
+ static substitute(text) {
170
+ return text.replace(Secrets2.MARKER, (_, reference) => Secrets2.valueOf(reference));
171
+ }
172
+ }
173
+
174
+ export { Utils2, Secrets2 };