imprint-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/LICENSE +21 -0
  3. package/README.md +322 -0
  4. package/examples/discoverandgo/README.md +57 -0
  5. package/examples/discoverandgo/book_discoverandgo_museum_pass/cron.json +8 -0
  6. package/examples/discoverandgo/book_discoverandgo_museum_pass/index.ts +89 -0
  7. package/examples/discoverandgo/book_discoverandgo_museum_pass/workflow.json +39 -0
  8. package/examples/echo/README.md +37 -0
  9. package/examples/echo/echo_test/index.ts +31 -0
  10. package/examples/google-flights/search_google_flights/index.ts +101 -0
  11. package/examples/google-flights/search_google_flights/parser.test.ts +140 -0
  12. package/examples/google-flights/search_google_flights/parser.ts +189 -0
  13. package/examples/google-flights/search_google_flights/playbook.yaml +130 -0
  14. package/examples/google-flights/search_google_flights/workflow.json +48 -0
  15. package/examples/google-hotels/search_google_hotels/index.ts +194 -0
  16. package/examples/google-hotels/search_google_hotels/parser.test.ts +168 -0
  17. package/examples/google-hotels/search_google_hotels/parser.ts +330 -0
  18. package/examples/google-hotels/search_google_hotels/playbook.yaml +125 -0
  19. package/examples/google-hotels/search_google_hotels/workflow.json +111 -0
  20. package/examples/namecheap-domains/search_namecheap_domains/index.ts +144 -0
  21. package/examples/namecheap-domains/search_namecheap_domains/parser.ts +380 -0
  22. package/examples/namecheap-domains/search_namecheap_domains/playbook.yaml +50 -0
  23. package/examples/namecheap-domains/search_namecheap_domains/request-transform.ts +136 -0
  24. package/examples/namecheap-domains/search_namecheap_domains/workflow.json +97 -0
  25. package/examples/southwest/README.md +81 -0
  26. package/examples/southwest/search_southwest_flights/backends.json +23 -0
  27. package/examples/southwest/search_southwest_flights/cron.json +19 -0
  28. package/examples/southwest/search_southwest_flights/index.ts +110 -0
  29. package/examples/southwest/search_southwest_flights/playbook.yaml +46 -0
  30. package/examples/southwest/search_southwest_flights/workflow.json +54 -0
  31. package/package.json +78 -0
  32. package/prompts/compile-agent.md +580 -0
  33. package/prompts/intent-detection.md +198 -0
  34. package/prompts/playbook-compilation.md +279 -0
  35. package/prompts/request-triage.md +74 -0
  36. package/prompts/tool-candidate-detection.md +104 -0
  37. package/src/cli.ts +1287 -0
  38. package/src/imprint/agent.ts +468 -0
  39. package/src/imprint/app-api-hosts.ts +53 -0
  40. package/src/imprint/backend-ladder.ts +568 -0
  41. package/src/imprint/check.ts +136 -0
  42. package/src/imprint/chromium.ts +211 -0
  43. package/src/imprint/claude-cli-compile.ts +640 -0
  44. package/src/imprint/cli-credential.ts +394 -0
  45. package/src/imprint/codex-cli-compile.ts +712 -0
  46. package/src/imprint/compile-agent-types.ts +40 -0
  47. package/src/imprint/compile-agent.ts +404 -0
  48. package/src/imprint/compile-tools.ts +1389 -0
  49. package/src/imprint/compile.ts +720 -0
  50. package/src/imprint/cookie-jar.ts +246 -0
  51. package/src/imprint/credential-bundle.ts +195 -0
  52. package/src/imprint/credential-extract.ts +290 -0
  53. package/src/imprint/credential-store.ts +707 -0
  54. package/src/imprint/cron.ts +312 -0
  55. package/src/imprint/doctor.ts +223 -0
  56. package/src/imprint/emit.ts +154 -0
  57. package/src/imprint/etld.ts +134 -0
  58. package/src/imprint/freeform-redact.ts +216 -0
  59. package/src/imprint/inject-listener.ts +137 -0
  60. package/src/imprint/install.ts +795 -0
  61. package/src/imprint/integrations.ts +385 -0
  62. package/src/imprint/is-compiled.ts +2 -0
  63. package/src/imprint/json-path.ts +100 -0
  64. package/src/imprint/llm.ts +998 -0
  65. package/src/imprint/load-json.ts +54 -0
  66. package/src/imprint/log.ts +33 -0
  67. package/src/imprint/login.ts +166 -0
  68. package/src/imprint/mcp-compile-server.ts +282 -0
  69. package/src/imprint/mcp-maintenance.ts +1790 -0
  70. package/src/imprint/mcp-server.ts +350 -0
  71. package/src/imprint/multi-progress.ts +69 -0
  72. package/src/imprint/notify.ts +155 -0
  73. package/src/imprint/paths.ts +64 -0
  74. package/src/imprint/playbook-parser.ts +21 -0
  75. package/src/imprint/playbook-runner.ts +465 -0
  76. package/src/imprint/probe-backends.ts +251 -0
  77. package/src/imprint/progress.ts +28 -0
  78. package/src/imprint/record.ts +470 -0
  79. package/src/imprint/redact.ts +550 -0
  80. package/src/imprint/replay-capture.ts +387 -0
  81. package/src/imprint/request-context.ts +66 -0
  82. package/src/imprint/runtime-link.ts +73 -0
  83. package/src/imprint/runtime.ts +942 -0
  84. package/src/imprint/sensitive-keys.ts +156 -0
  85. package/src/imprint/session-diff.ts +409 -0
  86. package/src/imprint/session-merge.ts +198 -0
  87. package/src/imprint/session-writer.ts +149 -0
  88. package/src/imprint/sites.ts +27 -0
  89. package/src/imprint/stealth-fetch.ts +434 -0
  90. package/src/imprint/teach-state.ts +235 -0
  91. package/src/imprint/teach.ts +2120 -0
  92. package/src/imprint/tool-candidates.ts +423 -0
  93. package/src/imprint/tool-loader.ts +186 -0
  94. package/src/imprint/tool-selection.ts +70 -0
  95. package/src/imprint/tracing.ts +508 -0
  96. package/src/imprint/types.ts +472 -0
  97. package/src/imprint/version.ts +21 -0
@@ -0,0 +1,211 @@
1
+ /** Launch real Chromium with CDP debugging open. Prefers Playwright's
2
+ * bundled Chromium (unmanaged) over system Chrome (corporate policy
3
+ * often blocks --remote-debugging-port). $CHROMIUM_PATH overrides. */
4
+
5
+ import { type ChildProcess, spawn } from 'node:child_process';
6
+ import { existsSync, readdirSync, statSync } from 'node:fs';
7
+ import { createServer } from 'node:net';
8
+ import { homedir, tmpdir } from 'node:os';
9
+ import { join as pathJoin } from 'node:path';
10
+ import { setTimeout as sleep } from 'node:timers/promises';
11
+ import { isDebug } from './log.ts';
12
+
13
+ interface LaunchOptions {
14
+ /** CDP port. If omitted, picks a free ephemeral port. */
15
+ port?: number;
16
+ /** Initial URL to open. Defaults to about:blank. */
17
+ url?: string;
18
+ /** Launch headless. Default false (recording = visible browser the user drives). */
19
+ headless?: boolean;
20
+ /** Persist cookies + login by passing an explicit path; otherwise a throwaway tmp dir. */
21
+ userDataDir?: string;
22
+ /** Extra Chromium flags (advanced). */
23
+ extraArgs?: string[];
24
+ }
25
+
26
+ interface LaunchedChromium {
27
+ process: ChildProcess;
28
+ port: number;
29
+ userDataDir: string;
30
+ /** Resolves once Chromium is accepting CDP connections, or rejects after timeout. */
31
+ ready: Promise<void>;
32
+ close(): Promise<void>;
33
+ }
34
+
35
+ const MAC_CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
36
+ const LINUX_CANDIDATES = [
37
+ '/usr/bin/google-chrome',
38
+ '/usr/bin/chromium',
39
+ '/usr/bin/chromium-browser',
40
+ ];
41
+
42
+ /** Find Playwright's "Google Chrome for Testing" — newest version wins
43
+ * if multiple are installed. */
44
+ function findPlaywrightChromium(): string | null {
45
+ const cacheRoots = [
46
+ pathJoin(homedir(), 'Library/Caches/ms-playwright'),
47
+ pathJoin(homedir(), '.cache/ms-playwright'),
48
+ ];
49
+ for (const root of cacheRoots) {
50
+ if (!existsSync(root)) continue;
51
+ let dirs: string[];
52
+ try {
53
+ dirs = readdirSync(root)
54
+ .filter((d) => /^chromium-\d+$/.test(d))
55
+ .sort((a, b) => {
56
+ const an = Number.parseInt(a.split('-')[1] ?? '0', 10);
57
+ const bn = Number.parseInt(b.split('-')[1] ?? '0', 10);
58
+ return bn - an; // newest first
59
+ });
60
+ } catch {
61
+ continue;
62
+ }
63
+ for (const dir of dirs) {
64
+ const candidates = [
65
+ // macOS arm64 layout
66
+ pathJoin(
67
+ root,
68
+ dir,
69
+ 'chrome-mac-arm64',
70
+ 'Google Chrome for Testing.app',
71
+ 'Contents',
72
+ 'MacOS',
73
+ 'Google Chrome for Testing',
74
+ ),
75
+ // macOS x64 layout
76
+ pathJoin(
77
+ root,
78
+ dir,
79
+ 'chrome-mac',
80
+ 'Google Chrome for Testing.app',
81
+ 'Contents',
82
+ 'MacOS',
83
+ 'Google Chrome for Testing',
84
+ ),
85
+ // Linux layout
86
+ pathJoin(root, dir, 'chrome-linux', 'chrome'),
87
+ ];
88
+ for (const c of candidates) {
89
+ try {
90
+ if (existsSync(c) && statSync(c).isFile()) return c;
91
+ } catch {
92
+ // ignore
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+
100
+ export function findChromium(): string {
101
+ const explicit = process.env.CHROMIUM_PATH;
102
+ if (explicit && existsSync(explicit)) return explicit;
103
+
104
+ // Prefer Playwright's bundled Chromium — never blocked by corporate policy.
105
+ const pw = findPlaywrightChromium();
106
+ if (pw) return pw;
107
+
108
+ if (process.platform === 'darwin' && existsSync(MAC_CHROME)) return MAC_CHROME;
109
+ if (process.platform === 'linux') {
110
+ for (const candidate of LINUX_CANDIDATES) {
111
+ if (existsSync(candidate)) return candidate;
112
+ }
113
+ }
114
+ throw new Error(
115
+ [
116
+ 'Could not locate Chromium.',
117
+ '',
118
+ 'Fix:',
119
+ ' bunx playwright install chromium # installs an unmanaged Chromium',
120
+ ' export CHROMIUM_PATH=/path/to/chromium # explicit override',
121
+ '',
122
+ 'Or run `imprint doctor` to see exactly which prerequisites are missing.',
123
+ '',
124
+ 'On corporate-managed devices the system Chrome usually has a policy that',
125
+ "disallows `--remote-debugging-port`. Playwright's bundled Chromium isn't",
126
+ 'managed and is the recommended path.',
127
+ ].join('\n'),
128
+ );
129
+ }
130
+
131
+ async function pickFreePort(): Promise<number> {
132
+ return new Promise((resolve, reject) => {
133
+ const server = createServer();
134
+ server.unref();
135
+ server.on('error', reject);
136
+ server.listen(0, () => {
137
+ const address = server.address();
138
+ if (!address || typeof address === 'string') {
139
+ reject(new Error('Could not determine assigned port'));
140
+ return;
141
+ }
142
+ const { port } = address;
143
+ server.close(() => resolve(port));
144
+ });
145
+ });
146
+ }
147
+
148
+ async function waitForCdp(port: number, timeoutMs = 10_000): Promise<void> {
149
+ const deadline = Date.now() + timeoutMs;
150
+ let lastError: unknown;
151
+ while (Date.now() < deadline) {
152
+ try {
153
+ const res = await fetch(`http://127.0.0.1:${port}/json/version`);
154
+ if (res.ok) return;
155
+ } catch (err) {
156
+ lastError = err;
157
+ }
158
+ await sleep(100);
159
+ }
160
+ throw new Error(
161
+ `Chromium did not open CDP on port ${port} within ${timeoutMs}ms (${String(lastError)})`,
162
+ );
163
+ }
164
+
165
+ export async function launchChromium(opts: LaunchOptions = {}): Promise<LaunchedChromium> {
166
+ const exe = findChromium();
167
+ const port = opts.port ?? (await pickFreePort());
168
+ const userDataDir =
169
+ opts.userDataDir ?? pathJoin(tmpdir(), `imprint-chrome-${Date.now()}-${process.pid}`);
170
+
171
+ const args = [
172
+ `--remote-debugging-port=${port}`,
173
+ `--user-data-dir=${userDataDir}`,
174
+ '--no-first-run',
175
+ '--no-default-browser-check',
176
+ '--disable-features=Translate,MediaRouter',
177
+ '--disable-popup-blocking',
178
+ '--use-mock-keychain',
179
+ ];
180
+ if (opts.headless) args.push('--headless=new');
181
+ if (opts.extraArgs) args.push(...opts.extraArgs);
182
+ args.push(opts.url ?? 'about:blank');
183
+
184
+ const child = spawn(exe, args, {
185
+ stdio: ['ignore', 'ignore', 'pipe'],
186
+ detached: false,
187
+ });
188
+
189
+ // Chromium is noisy — only surface stderr under IMPRINT_DEBUG.
190
+ if (isDebug()) {
191
+ child.stderr?.on('data', (chunk) => process.stderr.write(chunk));
192
+ }
193
+
194
+ const ready = waitForCdp(port);
195
+
196
+ let closed = false;
197
+ const close = async (): Promise<void> => {
198
+ if (closed) return;
199
+ closed = true;
200
+ if (child.exitCode === null && child.signalCode === null) {
201
+ child.kill('SIGTERM');
202
+ await Promise.race([
203
+ new Promise<void>((resolve) => child.once('exit', () => resolve())),
204
+ sleep(2000),
205
+ ]);
206
+ if (child.exitCode === null) child.kill('SIGKILL');
207
+ }
208
+ };
209
+
210
+ return { process: child, port, userDataDir, ready, close };
211
+ }