@the-librarian/cli 1.0.0-rc.21

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 (155) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +94 -0
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +15 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/commands/install.d.ts +28 -0
  8. package/dist/commands/install.d.ts.map +1 -0
  9. package/dist/commands/install.js +204 -0
  10. package/dist/commands/install.js.map +1 -0
  11. package/dist/commands/shared.d.ts +16 -0
  12. package/dist/commands/shared.d.ts.map +1 -0
  13. package/dist/commands/shared.js +30 -0
  14. package/dist/commands/shared.js.map +1 -0
  15. package/dist/commands/uninstall.d.ts +18 -0
  16. package/dist/commands/uninstall.d.ts.map +1 -0
  17. package/dist/commands/uninstall.js +67 -0
  18. package/dist/commands/uninstall.js.map +1 -0
  19. package/dist/commands/update.d.ts +21 -0
  20. package/dist/commands/update.d.ts.map +1 -0
  21. package/dist/commands/update.js +75 -0
  22. package/dist/commands/update.js.map +1 -0
  23. package/dist/config.d.ts +40 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +65 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/doctor.d.ts +23 -0
  28. package/dist/doctor.d.ts.map +1 -0
  29. package/dist/doctor.js +81 -0
  30. package/dist/doctor.js.map +1 -0
  31. package/dist/env.d.ts +39 -0
  32. package/dist/env.d.ts.map +1 -0
  33. package/dist/env.js +208 -0
  34. package/dist/env.js.map +1 -0
  35. package/dist/exec.d.ts +37 -0
  36. package/dist/exec.d.ts.map +1 -0
  37. package/dist/exec.js +81 -0
  38. package/dist/exec.js.map +1 -0
  39. package/dist/harnesses/claude.d.ts +3 -0
  40. package/dist/harnesses/claude.d.ts.map +1 -0
  41. package/dist/harnesses/claude.js +88 -0
  42. package/dist/harnesses/claude.js.map +1 -0
  43. package/dist/harnesses/cli.d.ts +4 -0
  44. package/dist/harnesses/cli.d.ts.map +1 -0
  45. package/dist/harnesses/cli.js +16 -0
  46. package/dist/harnesses/cli.js.map +1 -0
  47. package/dist/harnesses/codex.d.ts +3 -0
  48. package/dist/harnesses/codex.d.ts.map +1 -0
  49. package/dist/harnesses/codex.js +183 -0
  50. package/dist/harnesses/codex.js.map +1 -0
  51. package/dist/harnesses/hermes.d.ts +16 -0
  52. package/dist/harnesses/hermes.d.ts.map +1 -0
  53. package/dist/harnesses/hermes.js +158 -0
  54. package/dist/harnesses/hermes.js.map +1 -0
  55. package/dist/harnesses/index.d.ts +12 -0
  56. package/dist/harnesses/index.d.ts.map +1 -0
  57. package/dist/harnesses/index.js +29 -0
  58. package/dist/harnesses/index.js.map +1 -0
  59. package/dist/harnesses/opencode.d.ts +3 -0
  60. package/dist/harnesses/opencode.d.ts.map +1 -0
  61. package/dist/harnesses/opencode.js +141 -0
  62. package/dist/harnesses/opencode.js.map +1 -0
  63. package/dist/harnesses/pi.d.ts +3 -0
  64. package/dist/harnesses/pi.d.ts.map +1 -0
  65. package/dist/harnesses/pi.js +72 -0
  66. package/dist/harnesses/pi.js.map +1 -0
  67. package/dist/harnesses/types.d.ts +48 -0
  68. package/dist/harnesses/types.d.ts.map +1 -0
  69. package/dist/harnesses/types.js +22 -0
  70. package/dist/harnesses/types.js.map +1 -0
  71. package/dist/index.d.ts +11 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +15 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/machine.d.ts +10 -0
  76. package/dist/machine.d.ts.map +1 -0
  77. package/dist/machine.js +39 -0
  78. package/dist/machine.js.map +1 -0
  79. package/dist/parse-args.d.ts +12 -0
  80. package/dist/parse-args.d.ts.map +1 -0
  81. package/dist/parse-args.js +54 -0
  82. package/dist/parse-args.js.map +1 -0
  83. package/dist/paths.d.ts +32 -0
  84. package/dist/paths.d.ts.map +1 -0
  85. package/dist/paths.js +75 -0
  86. package/dist/paths.js.map +1 -0
  87. package/dist/prompt.d.ts +53 -0
  88. package/dist/prompt.d.ts.map +1 -0
  89. package/dist/prompt.js +165 -0
  90. package/dist/prompt.js.map +1 -0
  91. package/dist/runtime.d.ts +45 -0
  92. package/dist/runtime.d.ts.map +1 -0
  93. package/dist/runtime.js +370 -0
  94. package/dist/runtime.js.map +1 -0
  95. package/dist/semver.d.ts +13 -0
  96. package/dist/semver.d.ts.map +1 -0
  97. package/dist/semver.js +98 -0
  98. package/dist/semver.js.map +1 -0
  99. package/dist/server/admin.d.ts +31 -0
  100. package/dist/server/admin.d.ts.map +1 -0
  101. package/dist/server/admin.js +110 -0
  102. package/dist/server/admin.js.map +1 -0
  103. package/dist/server/boot.d.ts +51 -0
  104. package/dist/server/boot.d.ts.map +1 -0
  105. package/dist/server/boot.js +233 -0
  106. package/dist/server/boot.js.map +1 -0
  107. package/dist/server/deploy-state.d.ts +33 -0
  108. package/dist/server/deploy-state.d.ts.map +1 -0
  109. package/dist/server/deploy-state.js +79 -0
  110. package/dist/server/deploy-state.js.map +1 -0
  111. package/dist/server/docker.d.ts +37 -0
  112. package/dist/server/docker.d.ts.map +1 -0
  113. package/dist/server/docker.js +138 -0
  114. package/dist/server/docker.js.map +1 -0
  115. package/dist/server/down.d.ts +19 -0
  116. package/dist/server/down.d.ts.map +1 -0
  117. package/dist/server/down.js +62 -0
  118. package/dist/server/down.js.map +1 -0
  119. package/dist/server/index.d.ts +8 -0
  120. package/dist/server/index.d.ts.map +1 -0
  121. package/dist/server/index.js +48 -0
  122. package/dist/server/index.js.map +1 -0
  123. package/dist/server/logs.d.ts +50 -0
  124. package/dist/server/logs.d.ts.map +1 -0
  125. package/dist/server/logs.js +162 -0
  126. package/dist/server/logs.js.map +1 -0
  127. package/dist/server/preflight.d.ts +24 -0
  128. package/dist/server/preflight.d.ts.map +1 -0
  129. package/dist/server/preflight.js +58 -0
  130. package/dist/server/preflight.js.map +1 -0
  131. package/dist/server/redact.d.ts +2 -0
  132. package/dist/server/redact.d.ts.map +1 -0
  133. package/dist/server/redact.js +26 -0
  134. package/dist/server/redact.js.map +1 -0
  135. package/dist/server/status.d.ts +20 -0
  136. package/dist/server/status.d.ts.map +1 -0
  137. package/dist/server/status.js +103 -0
  138. package/dist/server/status.js.map +1 -0
  139. package/dist/server/up.d.ts +211 -0
  140. package/dist/server/up.d.ts.map +1 -0
  141. package/dist/server/up.js +625 -0
  142. package/dist/server/up.js.map +1 -0
  143. package/dist/server/update.d.ts +35 -0
  144. package/dist/server/update.d.ts.map +1 -0
  145. package/dist/server/update.js +308 -0
  146. package/dist/server/update.js.map +1 -0
  147. package/dist/status.d.ts +18 -0
  148. package/dist/status.d.ts.map +1 -0
  149. package/dist/status.js +113 -0
  150. package/dist/status.js.map +1 -0
  151. package/dist/version.d.ts +2 -0
  152. package/dist/version.d.ts.map +1 -0
  153. package/dist/version.js +20 -0
  154. package/dist/version.js.map +1 -0
  155. package/package.json +36 -0
@@ -0,0 +1,183 @@
1
+ // Codex harness.
2
+ //
3
+ // Prefers the native `codex` CLI; falls back to editing the config file
4
+ // directly only when `codex` isn't on PATH but the config is still
5
+ // writable (so a partial Codex setup can still be wired up).
6
+ //
7
+ // detect `~/.codex/config.toml` has a `[mcp_servers.librarian]` table
8
+ // install `codex mcp add librarian --url <U>
9
+ // --bearer-token-env-var LIBRARIAN_AGENT_TOKEN`
10
+ // (fallback: write the table into config.toml)
11
+ // uninstall `codex mcp remove librarian` (fallback: strip the table)
12
+ // update re-run install (idempotent)
13
+ //
14
+ // Token handling (spec §9): the token's *value* never enters config or the
15
+ // command line. Codex stores only the env-var NAME `LIBRARIAN_AGENT_TOKEN`;
16
+ // it resolves the value at request time. We pass `cfg.mcpUrl` (not the
17
+ // token) on the CLI, so nothing secret is ever logged.
18
+ import fs from "node:fs";
19
+ import path from "node:path";
20
+ import { run, which } from "../exec.js";
21
+ import { codexConfigPath } from "../paths.js";
22
+ const CLI = "codex";
23
+ const SERVER_ID = "librarian";
24
+ const TOKEN_ENV_VAR = "LIBRARIAN_AGENT_TOKEN";
25
+ const TABLE_HEADER = "[mcp_servers.librarian]";
26
+ // The config-shape version we stamp, so detect can report something even
27
+ // though TOML carries no native version for an MCP entry.
28
+ const CONFIG_VERSION = "1";
29
+ const VERSION_MARKER = "# librarian-config-version =";
30
+ /** Read config.toml, or "" if it doesn't exist yet. */
31
+ function readConfig() {
32
+ try {
33
+ return fs.readFileSync(codexConfigPath(), "utf8");
34
+ }
35
+ catch {
36
+ return "";
37
+ }
38
+ }
39
+ /** True iff the config text already declares our MCP server table. */
40
+ function hasTable(config) {
41
+ return config.split("\n").some((line) => line.trim() === TABLE_HEADER);
42
+ }
43
+ /** Parse the stamped config-shape version from a managed comment, if any. */
44
+ function parseConfigVersion(config) {
45
+ for (const line of config.split("\n")) {
46
+ const trimmed = line.trim();
47
+ if (trimmed.startsWith(VERSION_MARKER)) {
48
+ const v = trimmed
49
+ .slice(VERSION_MARKER.length)
50
+ .trim()
51
+ .replace(/^["']|["']$/g, "");
52
+ if (v)
53
+ return v;
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+ /** Append our managed table to existing config text (idempotent caller). */
59
+ function withTable(config, cfg) {
60
+ const block = [
61
+ `${VERSION_MARKER} "${CONFIG_VERSION}"`,
62
+ TABLE_HEADER,
63
+ `url = ${tomlString(cfg.mcpUrl)}`,
64
+ `bearer_token_env_var = ${tomlString(TOKEN_ENV_VAR)}`,
65
+ ].join("\n");
66
+ const base = config.length === 0 || config.endsWith("\n") ? config : `${config}\n`;
67
+ const sep = base.length === 0 ? "" : "\n";
68
+ return `${base}${sep}${block}\n`;
69
+ }
70
+ /** Strip our managed table (and its version marker) from config text. */
71
+ function withoutTable(config) {
72
+ const lines = config.split("\n");
73
+ const out = [];
74
+ let skipping = false;
75
+ for (const line of lines) {
76
+ const trimmed = line.trim();
77
+ if (trimmed === VERSION_MARKER || trimmed.startsWith(VERSION_MARKER)) {
78
+ // Drop a marker line immediately preceding our table.
79
+ continue;
80
+ }
81
+ if (trimmed === TABLE_HEADER) {
82
+ skipping = true;
83
+ continue;
84
+ }
85
+ if (skipping) {
86
+ // A new table header (or end of our keys) ends the skip region.
87
+ if (/^\[.+\]$/.test(trimmed)) {
88
+ skipping = false;
89
+ out.push(line);
90
+ continue;
91
+ }
92
+ if (trimmed === "" || /^[A-Za-z0-9_]+\s*=/.test(trimmed)) {
93
+ continue; // a key/blank inside our table — drop it
94
+ }
95
+ skipping = false;
96
+ }
97
+ out.push(line);
98
+ }
99
+ // Tidy trailing blank lines.
100
+ while (out.length > 0 && out[out.length - 1] === "")
101
+ out.pop();
102
+ return out.length === 0 ? "" : `${out.join("\n")}\n`;
103
+ }
104
+ function writeConfig(content) {
105
+ const file = codexConfigPath();
106
+ fs.mkdirSync(path.dirname(file), { recursive: true });
107
+ fs.writeFileSync(file, content, "utf8");
108
+ }
109
+ export const codex = {
110
+ id: "codex",
111
+ displayName: "Codex",
112
+ async detect() {
113
+ const config = readConfig();
114
+ if (!hasTable(config))
115
+ return { installed: false };
116
+ const version = parseConfigVersion(config) ?? CONFIG_VERSION;
117
+ return { installed: true, version };
118
+ },
119
+ async install(cfg) {
120
+ if (await which(CLI)) {
121
+ // Idempotent: if our table is already present, do nothing.
122
+ if (hasTable(readConfig()))
123
+ return;
124
+ const result = await run(CLI, [
125
+ "mcp",
126
+ "add",
127
+ SERVER_ID,
128
+ "--url",
129
+ cfg.mcpUrl,
130
+ "--bearer-token-env-var",
131
+ TOKEN_ENV_VAR,
132
+ ]);
133
+ if (result.code !== 0) {
134
+ throw new Error(`codex mcp add failed: ${oneLine(result.stderr || result.stdout)}`);
135
+ }
136
+ // Stamp our config-shape version alongside the CLI-written table so
137
+ // detect can report it (the CLI doesn't write the marker comment).
138
+ stampVersionIfMissing();
139
+ return;
140
+ }
141
+ // Fallback: no `codex` on PATH. We can still write the config file.
142
+ const config = readConfig();
143
+ if (hasTable(config))
144
+ return; // idempotent
145
+ writeConfig(withTable(config, cfg));
146
+ },
147
+ async uninstall() {
148
+ if (await which(CLI)) {
149
+ // `codex mcp remove` is a no-op when the server is absent.
150
+ await run(CLI, ["mcp", "remove", SERVER_ID]);
151
+ }
152
+ // Always clean the config text too (covers the CLI-absent path and the
153
+ // version marker the CLI doesn't manage).
154
+ const config = readConfig();
155
+ if (hasTable(config) || parseConfigVersion(config) !== undefined) {
156
+ writeConfig(withoutTable(config));
157
+ }
158
+ },
159
+ async update(cfg) {
160
+ // Re-applying is idempotent; install short-circuits when present.
161
+ await this.install(cfg);
162
+ },
163
+ };
164
+ /** If the table exists but our version marker doesn't, add the marker. */
165
+ function stampVersionIfMissing() {
166
+ const config = readConfig();
167
+ if (!hasTable(config) || parseConfigVersion(config) !== undefined)
168
+ return;
169
+ const lines = config.split("\n");
170
+ const idx = lines.findIndex((line) => line.trim() === TABLE_HEADER);
171
+ if (idx === -1)
172
+ return;
173
+ lines.splice(idx, 0, `${VERSION_MARKER} "${CONFIG_VERSION}"`);
174
+ writeConfig(lines.join("\n"));
175
+ }
176
+ /** Double-quote a TOML string value with minimal escaping. */
177
+ function tomlString(value) {
178
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
179
+ }
180
+ function oneLine(text) {
181
+ return text.replace(/\s+/g, " ").trim();
182
+ }
183
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/harnesses/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { HarnessModule } from "./types.js";
2
+ export declare const PINNED_REF: string;
3
+ /**
4
+ * Fetches the Hermes adapter for a pinned ref and returns the absolute
5
+ * path to a directory whose contents ARE the adapter (the equivalent of
6
+ * `integrations/hermes/librarian/`). The caller copies from there.
7
+ *
8
+ * Injectable so tests substitute a local fixture instead of the network.
9
+ */
10
+ export type AdapterFetcher = (ref: string) => Promise<string>;
11
+ /** Override the adapter fetcher (tests inject a local-fixture fetcher). */
12
+ export declare function setAdapterFetcher(next: AdapterFetcher): void;
13
+ /** Restore the default (network) adapter fetcher (tests). */
14
+ export declare function resetAdapterFetcher(): void;
15
+ export declare const hermes: HarnessModule;
16
+ //# sourceMappingURL=hermes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../src/harnesses/hermes.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AAQ/D,eAAO,MAAM,UAAU,QAAqB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAqC9D,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAE5D;AAED,6DAA6D;AAC7D,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAoDD,eAAO,MAAM,MAAM,EAAE,aA+CpB,CAAC"}
@@ -0,0 +1,158 @@
1
+ // Hermes harness.
2
+ //
3
+ // Hermes has no native install command for a memory provider, so we:
4
+ // - copy the adapter into `~/.hermes/plugins/librarian`, and
5
+ // - set `memory.provider = librarian` in `~/.hermes/config.json`.
6
+ //
7
+ // detect plugin dir present AND provider set to "librarian"
8
+ // version comes from the adapter's `plugin.yaml`
9
+ // uninstall remove the plugin dir + unset the provider key
10
+ // update re-fetch + re-copy the adapter (idempotent)
11
+ //
12
+ // ARTIFACT SOURCING. On a user's machine the adapter does NOT live in the
13
+ // repo — the CLI must fetch it from a pinned release. We download a tarball
14
+ // of the pinned ref from GitHub's codeload endpoint
15
+ // https://codeload.github.com/JimJafar/the-librarian/tar.gz/refs/tags/<ref>
16
+ // and extract `integrations/hermes/librarian/**` out of it with `tar`. The
17
+ // FETCH is injectable (`setAdapterFetcher`): tests inject a fetcher that
18
+ // returns a local fixture dir, so nothing touches the network. The pinned
19
+ // ref defaults to the repo's current release tag (PINNED_REF).
20
+ import fs from "node:fs";
21
+ import os from "node:os";
22
+ import path from "node:path";
23
+ import { run } from "../exec.js";
24
+ import { hermesConfigPath, hermesHomeDir, hermesPluginDir } from "../paths.js";
25
+ import { cliVersion } from "../version.js";
26
+ const PROVIDER_ID = "librarian";
27
+ // The pinned monorepo release we fetch the adapter from: the tag matching this
28
+ // CLI's own version. DERIVED (not hardcoded) so it can't drift — the published
29
+ // package's version is stamped from the root at publish (scripts/stamp-version.mjs),
30
+ // and the matching `vX.Y.Z` tag exists before the package is published. A
31
+ // hardcoded literal here silently froze at rc.5 once before.
32
+ export const PINNED_REF = `v${cliVersion()}`;
33
+ /** The default fetcher: codeload tarball → `tar` extract → adapter dir. */
34
+ const defaultFetcher = async (ref) => {
35
+ const url = `https://codeload.github.com/JimJafar/the-librarian/tar.gz/refs/tags/${ref}`;
36
+ const work = fs.mkdtempSync(path.join(os.tmpdir(), "librarian-hermes-fetch-"));
37
+ const tarball = path.join(work, "src.tar.gz");
38
+ const res = await fetch(url, { redirect: "error" });
39
+ if (!res.ok) {
40
+ throw new Error(`Failed to fetch Hermes adapter (${ref}): HTTP ${res.status}`);
41
+ }
42
+ const buf = Buffer.from(await res.arrayBuffer());
43
+ fs.writeFileSync(tarball, buf);
44
+ // codeload nests the adapter at
45
+ // the-librarian-<ref>/integrations/hermes/librarian/**
46
+ // — four leading path components. Strip all four so the adapter's own
47
+ // files (plugin.yaml, …) land at the extraction root, which is what the
48
+ // caller copies into `~/.hermes/plugins/librarian/`.
49
+ const out = path.join(work, "adapter");
50
+ fs.mkdirSync(out, { recursive: true });
51
+ const extract = await run("tar", [
52
+ "-xzf",
53
+ tarball,
54
+ "-C",
55
+ out,
56
+ "--strip-components=4",
57
+ "--wildcards",
58
+ "*/integrations/hermes/librarian/*",
59
+ ]);
60
+ if (extract.code !== 0) {
61
+ throw new Error(`Failed to extract Hermes adapter: ${(extract.stderr || "").trim()}`);
62
+ }
63
+ return out;
64
+ };
65
+ let fetcher = defaultFetcher;
66
+ /** Override the adapter fetcher (tests inject a local-fixture fetcher). */
67
+ export function setAdapterFetcher(next) {
68
+ fetcher = next;
69
+ }
70
+ /** Restore the default (network) adapter fetcher (tests). */
71
+ export function resetAdapterFetcher() {
72
+ fetcher = defaultFetcher;
73
+ }
74
+ function readHermesConfig() {
75
+ try {
76
+ const raw = fs.readFileSync(hermesConfigPath(), "utf8");
77
+ const parsed = JSON.parse(raw);
78
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
79
+ return parsed;
80
+ }
81
+ return null;
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ function writeHermesConfig(config) {
88
+ fs.mkdirSync(hermesHomeDir(), { recursive: true });
89
+ fs.writeFileSync(hermesConfigPath(), `${JSON.stringify(config, null, 2)}\n`, "utf8");
90
+ }
91
+ /** True iff `memory.provider` is set to our provider id. */
92
+ function providerSet() {
93
+ const config = readHermesConfig();
94
+ return config?.memory?.provider === PROVIDER_ID;
95
+ }
96
+ /** Parse `version:` from the installed adapter's plugin.yaml, if present. */
97
+ function readAdapterVersion() {
98
+ try {
99
+ const raw = fs.readFileSync(path.join(hermesPluginDir(), "plugin.yaml"), "utf8");
100
+ for (const line of raw.split("\n")) {
101
+ const m = /^\s*version\s*:\s*["']?([^"'#\s]+)/.exec(line);
102
+ if (m?.[1])
103
+ return m[1];
104
+ }
105
+ }
106
+ catch {
107
+ // dir/file gone — no version
108
+ }
109
+ return undefined;
110
+ }
111
+ /** Recursively copy a directory tree (Node 22 `fs.cpSync`). */
112
+ function copyDir(from, to) {
113
+ fs.rmSync(to, { recursive: true, force: true });
114
+ fs.mkdirSync(path.dirname(to), { recursive: true });
115
+ fs.cpSync(from, to, { recursive: true });
116
+ }
117
+ export const hermes = {
118
+ id: "hermes",
119
+ displayName: "Hermes",
120
+ async detect() {
121
+ const dirPresent = fs.existsSync(hermesPluginDir());
122
+ if (!dirPresent || !providerSet())
123
+ return { installed: false };
124
+ const version = readAdapterVersion();
125
+ return version === undefined ? { installed: true } : { installed: true, version };
126
+ },
127
+ async install(_cfg) {
128
+ // Fetch the adapter for the pinned ref (network by default; a local
129
+ // fixture in tests) and copy it into place — overwriting any prior copy
130
+ // so re-running is idempotent.
131
+ const adapterDir = await fetcher(PINNED_REF);
132
+ copyDir(adapterDir, hermesPluginDir());
133
+ // Set memory.provider = librarian, preserving any other config keys.
134
+ const config = readHermesConfig() ?? {};
135
+ const memory = (config.memory && typeof config.memory === "object" ? config.memory : {});
136
+ memory.provider = PROVIDER_ID;
137
+ config.memory = memory;
138
+ writeHermesConfig(config);
139
+ },
140
+ async uninstall() {
141
+ // Remove the plugin dir (no-op if already gone).
142
+ fs.rmSync(hermesPluginDir(), { recursive: true, force: true });
143
+ // Unset memory.provider only if it's ours, preserving other keys.
144
+ const config = readHermesConfig();
145
+ if (config?.memory && typeof config.memory === "object") {
146
+ if (config.memory.provider === PROVIDER_ID) {
147
+ delete config.memory.provider;
148
+ if (Object.keys(config.memory).length === 0)
149
+ delete config.memory;
150
+ writeHermesConfig(config);
151
+ }
152
+ }
153
+ },
154
+ async update(cfg) {
155
+ await this.install(cfg);
156
+ },
157
+ };
158
+ //# sourceMappingURL=hermes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes.js","sourceRoot":"","sources":["../../src/harnesses/hermes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { HarnessId, HarnessModule } from "./types.js";
2
+ /** Every harness module, keyed by id. */
3
+ export declare const registry: Record<HarnessId, HarnessModule>;
4
+ /** The harness ids in canonical display order. */
5
+ export declare const HARNESS_IDS: readonly HarnessId[];
6
+ /** All harness modules in canonical display order. */
7
+ export declare const allHarnesses: readonly HarnessModule[];
8
+ /** True iff `id` names a known harness. */
9
+ export declare function isHarnessId(id: string): id is HarnessId;
10
+ export type { HarnessConfig, HarnessModule, DetectResult, HarnessId } from "./types.js";
11
+ export { NotImplemented } from "./types.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3D,yCAAyC;AACzC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,aAAa,CAMrD,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAoD,CAAC;AAEjG,sDAAsD;AACtD,eAAO,MAAM,YAAY,EAAE,SAAS,aAAa,EAA0C,CAAC;AAE5F,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,SAAS,CAEvD;AAED,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,29 @@
1
+ // The harness registry: id → module.
2
+ //
3
+ // The single source of truth for "which harnesses exist". Commands iterate
4
+ // this in declaration order (the order the dashboard and prompts show them).
5
+ // The next wave swaps the stub modules for real implementations without
6
+ // touching this file.
7
+ import { claude } from "./claude.js";
8
+ import { codex } from "./codex.js";
9
+ import { hermes } from "./hermes.js";
10
+ import { opencode } from "./opencode.js";
11
+ import { pi } from "./pi.js";
12
+ /** Every harness module, keyed by id. */
13
+ export const registry = {
14
+ claude,
15
+ codex,
16
+ opencode,
17
+ hermes,
18
+ pi,
19
+ };
20
+ /** The harness ids in canonical display order. */
21
+ export const HARNESS_IDS = ["claude", "codex", "opencode", "hermes", "pi"];
22
+ /** All harness modules in canonical display order. */
23
+ export const allHarnesses = HARNESS_IDS.map((id) => registry[id]);
24
+ /** True iff `id` names a known harness. */
25
+ export function isHarnessId(id) {
26
+ return Object.prototype.hasOwnProperty.call(registry, id);
27
+ }
28
+ export { NotImplemented } from "./types.js";
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,sBAAsB;AAEtB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAG7B,yCAAyC;AACzC,MAAM,CAAC,MAAM,QAAQ,GAAqC;IACxD,MAAM;IACN,KAAK;IACL,QAAQ;IACR,MAAM;IACN,EAAE;CACH,CAAC;AAEF,kDAAkD;AAClD,MAAM,CAAC,MAAM,WAAW,GAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAEjG,sDAAsD;AACtD,MAAM,CAAC,MAAM,YAAY,GAA6B,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AAE5F,2CAA2C;AAC3C,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC5D,CAAC;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { HarnessModule } from "./types.js";
2
+ export declare const opencode: HarnessModule;
3
+ //# sourceMappingURL=opencode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/harnesses/opencode.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AA4E/D,eAAO,MAAM,QAAQ,EAAE,aAuEtB,CAAC"}
@@ -0,0 +1,141 @@
1
+ // OpenCode harness.
2
+ //
3
+ // OpenCode has no native install command, so we edit its global config
4
+ // (`~/.config/opencode/opencode.json`) idempotently, preserving every key
5
+ // the user already had. We add exactly two things:
6
+ //
7
+ // - `mcp.librarian`: a remote MCP block (type/url/enabled/headers). The
8
+ // Authorization header references the env var, not the token value —
9
+ // `Bearer {env:LIBRARIAN_AGENT_TOKEN}` — so the secret never lands in
10
+ // the file (spec §9).
11
+ // - the primer entry `<serverUrl>/primer.md` in the `instructions` array.
12
+ //
13
+ // We stamp a managed version marker (`mcp.librarian._librarianVersion`) so
14
+ // detect can report a version, plus the EXACT primer URL we added
15
+ // (`mcp.librarian._librarianPrimer`) so uninstall removes only our own entry
16
+ // from `instructions` and never a foreign `…/primer.md`. detect =
17
+ // `mcp.librarian` present. uninstall removes only the keys/entries we added,
18
+ // leaving the rest of the JSON intact.
19
+ import fs from "node:fs";
20
+ import path from "node:path";
21
+ import { opencodeConfigPath } from "../paths.js";
22
+ const SERVER_ID = "librarian";
23
+ const TOKEN_ENV_VAR = "LIBRARIAN_AGENT_TOKEN";
24
+ const MANAGED_VERSION = "1.0.0";
25
+ const VERSION_KEY = "_librarianVersion";
26
+ // The exact primer instruction we added, stamped into the managed block so
27
+ // uninstall can target only it (and not a foreign `…/primer.md`).
28
+ const PRIMER_KEY = "_librarianPrimer";
29
+ /** The primer instruction entry derived from the server URL. */
30
+ function primerEntry(serverUrl) {
31
+ return `${serverUrl.replace(/\/+$/, "")}/primer.md`;
32
+ }
33
+ /** Read + parse opencode.json, or null if absent / unreadable / invalid. */
34
+ function readConfig() {
35
+ let raw;
36
+ try {
37
+ raw = fs.readFileSync(opencodeConfigPath(), "utf8");
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ try {
43
+ const parsed = JSON.parse(raw);
44
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
45
+ return parsed;
46
+ }
47
+ return null;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ function writeConfig(config) {
54
+ const file = opencodeConfigPath();
55
+ fs.mkdirSync(path.dirname(file), { recursive: true });
56
+ fs.writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`, "utf8");
57
+ }
58
+ /** The managed remote block we install. */
59
+ function librarianBlock(cfg) {
60
+ return {
61
+ type: "remote",
62
+ url: cfg.mcpUrl,
63
+ enabled: true,
64
+ headers: { Authorization: `Bearer {env:${TOKEN_ENV_VAR}}` },
65
+ [VERSION_KEY]: MANAGED_VERSION,
66
+ // Stamp the exact primer entry we add so uninstall removes only ours.
67
+ [PRIMER_KEY]: primerEntry(cfg.serverUrl),
68
+ };
69
+ }
70
+ function getMcpEntry(config) {
71
+ const mcp = config?.mcp;
72
+ if (mcp && typeof mcp === "object" && SERVER_ID in mcp) {
73
+ return mcp[SERVER_ID];
74
+ }
75
+ return undefined;
76
+ }
77
+ export const opencode = {
78
+ id: "opencode",
79
+ displayName: "OpenCode",
80
+ async detect() {
81
+ const entry = getMcpEntry(readConfig());
82
+ if (!entry)
83
+ return { installed: false };
84
+ const version = typeof entry[VERSION_KEY] === "string" ? entry[VERSION_KEY] : undefined;
85
+ return version === undefined ? { installed: true } : { installed: true, version };
86
+ },
87
+ async install(cfg) {
88
+ const config = readConfig() ?? {};
89
+ // mcp.librarian — overwrite our managed block (idempotent), preserve
90
+ // every other mcp server.
91
+ const mcp = (config.mcp && typeof config.mcp === "object" ? config.mcp : {});
92
+ mcp[SERVER_ID] = librarianBlock(cfg);
93
+ config.mcp = mcp;
94
+ // instructions — ensure the primer entry is present exactly once,
95
+ // preserving any existing entries. Tolerate a missing/non-array value.
96
+ const entry = primerEntry(cfg.serverUrl);
97
+ const existing = Array.isArray(config.instructions)
98
+ ? config.instructions.filter((x) => typeof x === "string")
99
+ : [];
100
+ if (!existing.includes(entry))
101
+ existing.push(entry);
102
+ config.instructions = existing;
103
+ writeConfig(config);
104
+ },
105
+ async uninstall() {
106
+ const config = readConfig();
107
+ if (!config)
108
+ return; // nothing to do — no-op when absent
109
+ let changed = false;
110
+ // Read the EXACT primer entry we stamped before removing our block, so we
111
+ // can target only our own instruction and never a foreign `…/primer.md`.
112
+ const ourPrimer = getMcpEntry(config)?.[PRIMER_KEY];
113
+ // Remove our mcp.librarian key only.
114
+ if (config.mcp && typeof config.mcp === "object" && SERVER_ID in config.mcp) {
115
+ delete config.mcp[SERVER_ID];
116
+ changed = true;
117
+ // Drop an emptied mcp object so we don't leave `{"mcp":{}}` litter.
118
+ if (Object.keys(config.mcp).length === 0)
119
+ delete config.mcp;
120
+ }
121
+ // Remove only OUR exact primer entry from instructions, preserving the
122
+ // rest (including any foreign `…/primer.md` we didn't add).
123
+ if (typeof ourPrimer === "string" && Array.isArray(config.instructions)) {
124
+ const before = config.instructions;
125
+ const kept = before.filter((x) => x !== ourPrimer);
126
+ if (kept.length !== before.length) {
127
+ changed = true;
128
+ if (kept.length === 0)
129
+ delete config.instructions;
130
+ else
131
+ config.instructions = kept;
132
+ }
133
+ }
134
+ if (changed)
135
+ writeConfig(config);
136
+ },
137
+ async update(cfg) {
138
+ await this.install(cfg);
139
+ },
140
+ };
141
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/harnesses/opencode.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,mDAAmD;AACnD,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,0EAA0E;AAC1E,0BAA0B;AAC1B,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,kEAAkE;AAClE,6EAA6E;AAC7E,kEAAkE;AAClE,6EAA6E;AAC7E,uCAAuC;AAEvC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAC9C,MAAM,eAAe,GAAG,OAAO,CAAC;AAChC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AACxC,2EAA2E;AAC3E,kEAAkE;AAClE,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAiBtC,gEAAgE;AAChE,SAAS,WAAW,CAAC,SAAiB;IACpC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC;AACtD,CAAC;AAED,4EAA4E;AAC5E,SAAS,UAAU;IACjB,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAwB,CAAC;QAClC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,MAAsB;IACzC,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACzE,CAAC;AAED,2CAA2C;AAC3C,SAAS,cAAc,CAAC,GAAkB;IACxC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,GAAG,CAAC,MAAM;QACf,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,EAAE,aAAa,EAAE,eAAe,aAAa,GAAG,EAAE;QAC3D,CAAC,WAAW,CAAC,EAAE,eAAe;QAC9B,sEAAsE;QACtE,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAA6B;IAChD,MAAM,GAAG,GAAG,MAAM,EAAE,GAAG,CAAC;IACxB,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;QACvD,OAAO,GAAG,CAAC,SAAS,CAAsB,CAAC;IAC7C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAkB;IACrC,EAAE,EAAE,UAAU;IACd,WAAW,EAAE,UAAU;IAEvB,KAAK,CAAC,MAAM;QACV,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACxF,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACpF,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAkB;QAC9B,MAAM,MAAM,GAAmB,UAAU,EAAE,IAAI,EAAE,CAAC;QAElD,qEAAqE;QACrE,0BAA0B;QAC1B,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAG1E,CAAC;QACF,GAAG,CAAC,SAAS,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;QAEjB,kEAAkE;QAClE,uEAAuE;QACvE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;YACjD,CAAC,CAAE,MAAM,CAAC,YAA0B,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;YACtF,CAAC,CAAC,EAAE,CAAC;QACP,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,YAAY,GAAG,QAAQ,CAAC;QAE/B,WAAW,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,oCAAoC;QAEzD,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,0EAA0E;QAC1E,yEAAyE;QACzE,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;QAEpD,qCAAqC;QACrC,IAAI,MAAM,CAAC,GAAG,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;YAC5E,OAAQ,MAAM,CAAC,GAA+B,CAAC,SAAS,CAAC,CAAC;YAC1D,OAAO,GAAG,IAAI,CAAC;YACf,oEAAoE;YACpE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,MAAM,CAAC,GAAG,CAAC;QAC9D,CAAC;QAED,uEAAuE;QACvE,4DAA4D;QAC5D,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YACxE,MAAM,MAAM,GAAG,MAAM,CAAC,YAAyB,CAAC;YAChD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClC,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,MAAM,CAAC,YAAY,CAAC;;oBAC7C,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;YAClC,CAAC;QACH,CAAC;QAED,IAAI,OAAO;YAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAkB;QAC7B,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { HarnessModule } from "./types.js";
2
+ export declare const pi: HarnessModule;
3
+ //# sourceMappingURL=pi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../src/harnesses/pi.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,YAAY,CAAC;AAyB/D,eAAO,MAAM,EAAE,EAAE,aAgChB,CAAC"}