@aria-cli/tools 1.0.9 → 1.0.11

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 (241) hide show
  1. package/package.json +9 -5
  2. package/src/__tests__/web-fetch-download.test.ts +0 -433
  3. package/src/__tests__/web-tools.test.ts +0 -619
  4. package/src/ask-user-interaction.ts +0 -33
  5. package/src/cache/web-cache.ts +0 -110
  6. package/src/definitions/arion.ts +0 -118
  7. package/src/definitions/browser/browser.ts +0 -502
  8. package/src/definitions/browser/index.ts +0 -5
  9. package/src/definitions/browser/pw-downloads.ts +0 -142
  10. package/src/definitions/browser/pw-interactions.ts +0 -282
  11. package/src/definitions/browser/pw-responses.ts +0 -98
  12. package/src/definitions/browser/pw-session.ts +0 -405
  13. package/src/definitions/browser/pw-shared.ts +0 -85
  14. package/src/definitions/browser/pw-snapshot.ts +0 -383
  15. package/src/definitions/browser/pw-state.ts +0 -101
  16. package/src/definitions/browser/types.ts +0 -203
  17. package/src/definitions/code-intelligence.ts +0 -526
  18. package/src/definitions/core.ts +0 -118
  19. package/src/definitions/delegation.ts +0 -567
  20. package/src/definitions/deploy.ts +0 -73
  21. package/src/definitions/filesystem.ts +0 -217
  22. package/src/definitions/frg.ts +0 -67
  23. package/src/definitions/index.ts +0 -28
  24. package/src/definitions/memory.ts +0 -150
  25. package/src/definitions/messaging.ts +0 -734
  26. package/src/definitions/meta.ts +0 -392
  27. package/src/definitions/network.ts +0 -179
  28. package/src/definitions/outlook.ts +0 -318
  29. package/src/definitions/patch/apply-patch.ts +0 -235
  30. package/src/definitions/patch/fuzzy-match.ts +0 -217
  31. package/src/definitions/patch/index.ts +0 -1
  32. package/src/definitions/patch/patch-parser.ts +0 -297
  33. package/src/definitions/patch/sandbox-paths.ts +0 -129
  34. package/src/definitions/process/index.ts +0 -5
  35. package/src/definitions/process/process-registry.ts +0 -303
  36. package/src/definitions/process/process.ts +0 -456
  37. package/src/definitions/process/pty-keys.ts +0 -298
  38. package/src/definitions/process/session-slug.ts +0 -147
  39. package/src/definitions/quip.ts +0 -225
  40. package/src/definitions/search.ts +0 -67
  41. package/src/definitions/session-history.ts +0 -79
  42. package/src/definitions/shell.ts +0 -202
  43. package/src/definitions/slack.ts +0 -211
  44. package/src/definitions/web.ts +0 -119
  45. package/src/executors/apply-patch.ts +0 -1035
  46. package/src/executors/arion.ts +0 -199
  47. package/src/executors/code-intelligence.ts +0 -1179
  48. package/src/executors/deploy.ts +0 -1066
  49. package/src/executors/filesystem.ts +0 -1428
  50. package/src/executors/frg-freshness.ts +0 -743
  51. package/src/executors/frg.ts +0 -394
  52. package/src/executors/index.ts +0 -280
  53. package/src/executors/learning-meta.ts +0 -1367
  54. package/src/executors/lsp-client.ts +0 -355
  55. package/src/executors/memory.ts +0 -978
  56. package/src/executors/meta.ts +0 -293
  57. package/src/executors/process-registry.ts +0 -570
  58. package/src/executors/pty-session-store.ts +0 -43
  59. package/src/executors/pty.ts +0 -342
  60. package/src/executors/restart.ts +0 -133
  61. package/src/executors/search-freshness.ts +0 -249
  62. package/src/executors/search-types.ts +0 -98
  63. package/src/executors/search.ts +0 -89
  64. package/src/executors/self-diagnose.ts +0 -552
  65. package/src/executors/session-history.ts +0 -435
  66. package/src/executors/shell-safety.ts +0 -519
  67. package/src/executors/shell.ts +0 -1243
  68. package/src/executors/utils.ts +0 -40
  69. package/src/executors/web.ts +0 -786
  70. package/src/extraction/content-extraction.ts +0 -281
  71. package/src/extraction/index.ts +0 -5
  72. package/src/headless-control-contract.ts +0 -1149
  73. package/src/index.ts +0 -788
  74. package/src/local-control-http-auth.ts +0 -2
  75. package/src/mcp/client.ts +0 -218
  76. package/src/mcp/connection.ts +0 -568
  77. package/src/mcp/index.ts +0 -11
  78. package/src/mcp/jsonrpc.ts +0 -195
  79. package/src/mcp/types.ts +0 -199
  80. package/src/network-control-adapter.ts +0 -88
  81. package/src/network-runtime/address-types.ts +0 -218
  82. package/src/network-runtime/db-owner-fencing.ts +0 -91
  83. package/src/network-runtime/delivery-receipts.ts +0 -372
  84. package/src/network-runtime/direct-endpoint-authority.ts +0 -35
  85. package/src/network-runtime/index.ts +0 -316
  86. package/src/network-runtime/local-control-contract.ts +0 -784
  87. package/src/network-runtime/node-store-contract.ts +0 -46
  88. package/src/network-runtime/pair-route-contract.ts +0 -97
  89. package/src/network-runtime/peer-capabilities.ts +0 -48
  90. package/src/network-runtime/peer-principal-ref.ts +0 -20
  91. package/src/network-runtime/peer-state-machine.ts +0 -160
  92. package/src/network-runtime/protocol-schemas.ts +0 -265
  93. package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
  94. package/src/outlook/desktop-session.ts +0 -409
  95. package/src/policy.ts +0 -171
  96. package/src/providers/brave.ts +0 -80
  97. package/src/providers/duckduckgo.ts +0 -199
  98. package/src/providers/exa.ts +0 -85
  99. package/src/providers/firecrawl.ts +0 -77
  100. package/src/providers/index.ts +0 -8
  101. package/src/providers/jina.ts +0 -70
  102. package/src/providers/router.ts +0 -121
  103. package/src/providers/search-provider.ts +0 -74
  104. package/src/providers/tavily.ts +0 -74
  105. package/src/quip/desktop-session.ts +0 -435
  106. package/src/registry/index.ts +0 -1
  107. package/src/registry/registry.ts +0 -905
  108. package/src/runtime-socket-local-control-client.ts +0 -632
  109. package/src/security/dns-normalization.ts +0 -34
  110. package/src/security/dns-pinning.ts +0 -138
  111. package/src/security/external-content.ts +0 -129
  112. package/src/security/ssrf.ts +0 -207
  113. package/src/slack/desktop-session.ts +0 -493
  114. package/src/tool-factory.ts +0 -91
  115. package/src/types.ts +0 -1341
  116. package/src/utils/retry.ts +0 -163
  117. package/src/utils/safe-parse-json.ts +0 -176
  118. package/src/utils/url.ts +0 -20
  119. package/tests/benchmarks/registry.bench.ts +0 -57
  120. package/tests/cache/web-cache.test.ts +0 -147
  121. package/tests/critical-integration.test.ts +0 -1465
  122. package/tests/definitions/apply-patch.test.ts +0 -586
  123. package/tests/definitions/browser.test.ts +0 -495
  124. package/tests/definitions/delegation-pause-resume.test.ts +0 -758
  125. package/tests/definitions/execution.test.ts +0 -671
  126. package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
  127. package/tests/definitions/messaging.test.ts +0 -1468
  128. package/tests/definitions/outlook.test.ts +0 -30
  129. package/tests/definitions/process.test.ts +0 -469
  130. package/tests/definitions/slack.test.ts +0 -28
  131. package/tests/definitions/tool-inventory.test.ts +0 -218
  132. package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
  133. package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
  134. package/tests/executors/apply-patch.test.ts +0 -538
  135. package/tests/executors/arion.test.ts +0 -309
  136. package/tests/executors/conversation-primitives.test.ts +0 -250
  137. package/tests/executors/deploy.test.ts +0 -746
  138. package/tests/executors/filesystem-tools.test.ts +0 -357
  139. package/tests/executors/filesystem.test.ts +0 -959
  140. package/tests/executors/frg-freshness.test.ts +0 -136
  141. package/tests/executors/frg-merge.test.ts +0 -70
  142. package/tests/executors/frg-session-content.test.ts +0 -40
  143. package/tests/executors/frg.test.ts +0 -56
  144. package/tests/executors/memory-bugfixes.test.ts +0 -257
  145. package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
  146. package/tests/executors/memory.test.ts +0 -853
  147. package/tests/executors/meta-tools.test.ts +0 -411
  148. package/tests/executors/meta.test.ts +0 -683
  149. package/tests/executors/path-containment.test.ts +0 -51
  150. package/tests/executors/process-registry.test.ts +0 -505
  151. package/tests/executors/pty.test.ts +0 -664
  152. package/tests/executors/quest-security.test.ts +0 -249
  153. package/tests/executors/read-file-media.test.ts +0 -230
  154. package/tests/executors/recall-knowledge-schema.test.ts +0 -209
  155. package/tests/executors/recall-tags.test.ts +0 -278
  156. package/tests/executors/remember-null-safety.contract.test.ts +0 -41
  157. package/tests/executors/restart.test.ts +0 -67
  158. package/tests/executors/search-unified.test.ts +0 -381
  159. package/tests/executors/session-history.test.ts +0 -340
  160. package/tests/executors/session-transcript.test.ts +0 -561
  161. package/tests/executors/shell-abort.test.ts +0 -416
  162. package/tests/executors/shell-env-blocklist.test.ts +0 -648
  163. package/tests/executors/shell-env-process.test.ts +0 -245
  164. package/tests/executors/shell-process-registry.test.ts +0 -334
  165. package/tests/executors/shell-tools.test.ts +0 -393
  166. package/tests/executors/shell.test.ts +0 -690
  167. package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
  168. package/tests/executors/web-integration.test.ts +0 -633
  169. package/tests/executors/web-symlink.test.ts +0 -18
  170. package/tests/executors/web.test.ts +0 -1400
  171. package/tests/executors/write-stdin.test.ts +0 -145
  172. package/tests/extraction/content-extraction.test.ts +0 -153
  173. package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
  174. package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
  175. package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
  176. package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
  177. package/tests/helpers/async-waits.ts +0 -53
  178. package/tests/integration/headless-control-contract.integration.test.ts +0 -153
  179. package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
  180. package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
  181. package/tests/integration/quest-round-trip.test.ts +0 -303
  182. package/tests/integration/registry-executor-flow.test.ts +0 -85
  183. package/tests/integration.test.ts +0 -177
  184. package/tests/loading-tier.test.ts +0 -126
  185. package/tests/mcp/client-reconnect.test.ts +0 -267
  186. package/tests/mcp/connection.test.ts +0 -846
  187. package/tests/mcp/injectable-logger.test.ts +0 -83
  188. package/tests/mcp/jsonrpc.test.ts +0 -109
  189. package/tests/mcp/lifecycle.test.ts +0 -879
  190. package/tests/network-runtime/address-types.contract.test.ts +0 -143
  191. package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
  192. package/tests/network-runtime/local-control-contract.test.ts +0 -869
  193. package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
  194. package/tests/network-runtime/node-store-contract.test.ts +0 -11
  195. package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
  196. package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
  197. package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
  198. package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
  199. package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
  200. package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
  201. package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
  202. package/tests/parallel.test.ts +0 -71
  203. package/tests/policy.test.ts +0 -184
  204. package/tests/print-default-test-lane.ts +0 -14
  205. package/tests/print-test-lane-manifest.ts +0 -22
  206. package/tests/providers/brave.test.ts +0 -159
  207. package/tests/providers/duckduckgo.test.ts +0 -207
  208. package/tests/providers/exa.test.ts +0 -175
  209. package/tests/providers/firecrawl.test.ts +0 -168
  210. package/tests/providers/jina.test.ts +0 -144
  211. package/tests/providers/router.test.ts +0 -328
  212. package/tests/providers/tavily.test.ts +0 -165
  213. package/tests/registry/discovery.test.ts +0 -154
  214. package/tests/registry/injectable-logger.test.ts +0 -230
  215. package/tests/registry/input-validation.test.ts +0 -361
  216. package/tests/registry/interface-completeness.test.ts +0 -85
  217. package/tests/registry/mcp-integration.test.ts +0 -103
  218. package/tests/registry/mcp-read-only-hint.test.ts +0 -60
  219. package/tests/registry/memoria-discovery.test.ts +0 -390
  220. package/tests/registry/nested-validation.test.ts +0 -283
  221. package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
  222. package/tests/registry/registration-lifecycle.test.ts +0 -133
  223. package/tests/registry-validation.test.ts +0 -424
  224. package/tests/registry.test.ts +0 -460
  225. package/tests/security/dns-pinning.test.ts +0 -162
  226. package/tests/security/external-content.test.ts +0 -144
  227. package/tests/security/ssrf.test.ts +0 -118
  228. package/tests/shell-safety-integration.test.ts +0 -32
  229. package/tests/shell-safety.test.ts +0 -365
  230. package/tests/slack/desktop-session.test.ts +0 -50
  231. package/tests/test-lane-manifest.ts +0 -440
  232. package/tests/test-utils.ts +0 -27
  233. package/tests/tool-factory.test.ts +0 -188
  234. package/tests/utils/retry.test.ts +0 -231
  235. package/tests/utils/url.test.ts +0 -63
  236. package/tsconfig.cjs.json +0 -24
  237. package/tsconfig.json +0 -12
  238. package/vitest.config.ts +0 -55
  239. package/vitest.e2e.config.ts +0 -24
  240. package/vitest.integration.config.ts +0 -24
  241. package/vitest.native.config.ts +0 -24
@@ -1,74 +0,0 @@
1
- export interface SearchOptions {
2
- limit?: number;
3
- topic?: "general" | "news";
4
- domains?: string[];
5
- excludeDomains?: string[];
6
- timeRange?: "day" | "week" | "month" | "year";
7
- signal?: AbortSignal;
8
- }
9
-
10
- export interface SearchResult {
11
- title: string;
12
- url: string;
13
- content: string;
14
- score?: number;
15
- publishedDate?: string;
16
- }
17
-
18
- export interface SearchProviderEnv {
19
- ARIA_SEARCH_PROVIDER?: string;
20
- BRAVE_API_KEY?: string;
21
- FIRECRAWL_API_KEY?: string;
22
- EXA_API_KEY?: string;
23
- TAVILY_API_KEY?: string;
24
- JINA_API_KEY?: string;
25
- }
26
-
27
- export function resolveSearchProviderEnv(
28
- env: SearchProviderEnv | Record<string, string | undefined> = process.env,
29
- ): SearchProviderEnv {
30
- return {
31
- ARIA_SEARCH_PROVIDER: env.ARIA_SEARCH_PROVIDER,
32
- BRAVE_API_KEY: env.BRAVE_API_KEY,
33
- FIRECRAWL_API_KEY: env.FIRECRAWL_API_KEY,
34
- EXA_API_KEY: env.EXA_API_KEY,
35
- TAVILY_API_KEY: env.TAVILY_API_KEY,
36
- JINA_API_KEY: env.JINA_API_KEY,
37
- };
38
- }
39
-
40
- export interface SearchProvider {
41
- readonly name: string;
42
- readonly requiresApiKey: boolean;
43
- readonly priority: number;
44
- isAvailable(): boolean;
45
- search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
46
- }
47
-
48
- export function createProviderAbortSignal(
49
- timeoutMs: number,
50
- callerSignal?: AbortSignal,
51
- ): { signal: AbortSignal; cleanup: () => void } {
52
- const controller = new AbortController();
53
- const onCallerAbort = () => controller.abort();
54
-
55
- if (callerSignal) {
56
- if (callerSignal.aborted) {
57
- controller.abort();
58
- } else {
59
- callerSignal.addEventListener("abort", onCallerAbort, { once: true });
60
- }
61
- }
62
-
63
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
64
-
65
- return {
66
- signal: controller.signal,
67
- cleanup: () => {
68
- clearTimeout(timeout);
69
- if (callerSignal) {
70
- callerSignal.removeEventListener("abort", onCallerAbort);
71
- }
72
- },
73
- };
74
- }
@@ -1,74 +0,0 @@
1
- import type {
2
- SearchProvider,
3
- SearchOptions,
4
- SearchResult,
5
- SearchProviderEnv,
6
- } from "./search-provider.js";
7
- import { createProviderAbortSignal, resolveSearchProviderEnv } from "./search-provider.js";
8
-
9
- export class TavilySearchProvider implements SearchProvider {
10
- readonly name = "tavily";
11
- readonly requiresApiKey = true;
12
- readonly priority = 4;
13
-
14
- constructor(
15
- private readonly env: SearchProviderEnv | Record<string, string | undefined> = process.env,
16
- ) {}
17
-
18
- isAvailable(): boolean {
19
- const apiKey = resolveSearchProviderEnv(this.env).TAVILY_API_KEY;
20
- return Boolean(apiKey && apiKey.trim().length > 0);
21
- }
22
-
23
- async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
24
- const apiKey = resolveSearchProviderEnv(this.env).TAVILY_API_KEY;
25
- if (!apiKey) {
26
- throw new Error("TAVILY_API_KEY environment variable is not set");
27
- }
28
-
29
- const limit = options?.limit ?? 5;
30
-
31
- const body = {
32
- api_key: apiKey,
33
- query,
34
- max_results: limit,
35
- ...(options?.topic && { topic: options.topic }),
36
- ...(options?.domains && { include_domains: options.domains }),
37
- ...(options?.excludeDomains && { exclude_domains: options.excludeDomains }),
38
- };
39
-
40
- const { signal, cleanup } = createProviderAbortSignal(30_000, options?.signal);
41
-
42
- try {
43
- const response = await fetch("https://api.tavily.com/search", {
44
- method: "POST",
45
- headers: {
46
- "Content-Type": "application/json",
47
- },
48
- body: JSON.stringify(body),
49
- signal,
50
- });
51
-
52
- if (!response.ok) {
53
- throw new Error(`Tavily Search API error: ${response.status} ${response.statusText}`);
54
- }
55
-
56
- const json = await response.json();
57
- const results = (json.results || []) as Array<{
58
- title: string;
59
- url: string;
60
- content: string;
61
- score?: number;
62
- }>;
63
-
64
- return results.map((r) => ({
65
- title: r.title,
66
- url: r.url,
67
- content: r.content,
68
- score: r.score,
69
- }));
70
- } finally {
71
- cleanup();
72
- }
73
- }
74
- }
@@ -1,435 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
-
4
- const execFileAsync = promisify(execFile);
5
-
6
- const QUIP_API_BASE = "https://platform.quip.com/1";
7
- const DEFAULT_BOOTSTRAP_TIMEOUT_MS = 30_000;
8
-
9
- // ── Result types ────────────────────────────────────────────────────
10
-
11
- export interface QuipDocumentSummary {
12
- threadId: string;
13
- title: string;
14
- link: string;
15
- updatedUsec: number;
16
- createdUsec: number;
17
- }
18
-
19
- export interface QuipDocumentListResult {
20
- documents: QuipDocumentSummary[];
21
- }
22
-
23
- export interface QuipDocumentDetail {
24
- threadId: string;
25
- title: string;
26
- link: string;
27
- markdown: string;
28
- updatedUsec: number;
29
- createdUsec: number;
30
- authorId: string;
31
- }
32
-
33
- export interface QuipCreateResult {
34
- threadId: string;
35
- title: string;
36
- link: string;
37
- }
38
-
39
- export interface QuipCommentView {
40
- id: string;
41
- authorId: string;
42
- authorName: string;
43
- text: string;
44
- createdUsec: number;
45
- }
46
-
47
- export interface QuipCommentResult {
48
- threadId: string;
49
- commentId: string;
50
- }
51
-
52
- export interface QuipCommentListResult {
53
- threadId: string;
54
- comments: QuipCommentView[];
55
- }
56
-
57
- // ── Client interface ────────────────────────────────────────────────
58
-
59
- export interface QuipDesktopClient {
60
- getUserId(): string;
61
-
62
- listRecentDocuments(input: { limit?: number }): Promise<QuipDocumentListResult>;
63
-
64
- searchDocuments(input: { query: string; limit?: number }): Promise<QuipDocumentListResult>;
65
-
66
- readDocument(input: { threadId: string }): Promise<QuipDocumentDetail>;
67
-
68
- createDocument(input: {
69
- title: string;
70
- content: string;
71
- format?: "markdown" | "html";
72
- folderId?: string;
73
- }): Promise<QuipCreateResult>;
74
-
75
- addComment(input: { threadId: string; content: string }): Promise<QuipCommentResult>;
76
-
77
- listComments(input: { threadId: string }): Promise<QuipCommentListResult>;
78
-
79
- close(): Promise<void>;
80
- }
81
-
82
- // ── HTML → Markdown conversion (regex-based, no deps) ───────────────
83
-
84
- function htmlToMarkdown(html: string): string {
85
- let md = html;
86
- // Block elements first
87
- md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n# $1\n");
88
- md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n## $1\n");
89
- md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n### $1\n");
90
- md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n#### $1\n");
91
- md = md.replace(/<br\s*\/?>/gi, "\n");
92
- md = md.replace(/<\/p>/gi, "\n\n");
93
- md = md.replace(/<p[^>]*>/gi, "");
94
- md = md.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "- $1\n");
95
- md = md.replace(/<\/?(ul|ol)[^>]*>/gi, "\n");
96
- md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_, content: string) =>
97
- content
98
- .split("\n")
99
- .map((line: string) => `> ${line}`)
100
- .join("\n"),
101
- );
102
- md = md.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, "\n```\n$1\n```\n");
103
- md = md.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, "`$1`");
104
- // Inline formatting
105
- md = md.replace(/<(b|strong)[^>]*>([\s\S]*?)<\/\1>/gi, "**$2**");
106
- md = md.replace(/<(i|em)[^>]*>([\s\S]*?)<\/\1>/gi, "*$2*");
107
- md = md.replace(/<(s|del|strike)[^>]*>([\s\S]*?)<\/\1>/gi, "~~$2~~");
108
- md = md.replace(/<a\s+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
109
- // Strip remaining tags
110
- md = md.replace(/<[^>]+>/g, "");
111
- // Decode common HTML entities
112
- md = md.replace(/&amp;/g, "&");
113
- md = md.replace(/&lt;/g, "<");
114
- md = md.replace(/&gt;/g, ">");
115
- md = md.replace(/&quot;/g, '"');
116
- md = md.replace(/&#39;/g, "'");
117
- md = md.replace(/&nbsp;/g, " ");
118
- // Collapse excessive blank lines
119
- md = md.replace(/\n{3,}/g, "\n\n");
120
- return md.trim();
121
- }
122
-
123
- // ── Token extraction ────────────────────────────────────────────────
124
-
125
- interface KeychainToken {
126
- token: string;
127
- userId: string;
128
- }
129
-
130
- async function extractKeychainDesktopToken(): Promise<KeychainToken | null> {
131
- try {
132
- // Use python3 to parse the bplist from Keychain — macOS ships python3
133
- // and plutil requires a temp file; python3 one-liner is cleaner.
134
- const { stdout } = await execFileAsync("python3", [
135
- "-c",
136
- `
137
- import subprocess, plistlib, sys
138
- from datetime import datetime, timedelta
139
- try:
140
- raw = subprocess.check_output([
141
- 'security', 'find-generic-password', '-s', 'Quip', '-a', 'MultiAccount', '-g'
142
- ], stderr=subprocess.STDOUT).decode()
143
- import re
144
- hex_match = re.search(r'password: 0x([0-9A-Fa-f]+)', raw)
145
- if not hex_match:
146
- sys.exit(1)
147
- data = bytes.fromhex(hex_match.group(1))
148
- plist = plistlib.loads(data)
149
- import json
150
- for uid, info in plist.get('accounts', {}).items():
151
- exp = info.get('session_expiration', 0)
152
- exp_date = datetime(2001, 1, 1) + timedelta(seconds=exp)
153
- if exp_date > datetime.now():
154
- json.dump({"token": info["session_id"], "userId": uid}, sys.stdout)
155
- sys.exit(0)
156
- sys.exit(1)
157
- except:
158
- sys.exit(1)
159
- `.trim(),
160
- ]);
161
- const parsed = JSON.parse(stdout.trim()) as { token: string; userId: string };
162
- if (parsed.token && parsed.userId) {
163
- return parsed;
164
- }
165
- } catch {
166
- // Keychain extraction failed — fall through
167
- }
168
- return null;
169
- }
170
-
171
- async function extractManualKeychainToken(): Promise<string | null> {
172
- try {
173
- const { stdout } = await execFileAsync("security", [
174
- "find-generic-password",
175
- "-a",
176
- "aria",
177
- "-s",
178
- "quip-token",
179
- "-w",
180
- ]);
181
- const token = stdout.trim();
182
- return token || null;
183
- } catch {
184
- return null;
185
- }
186
- }
187
-
188
- async function extractPlaywrightToken(timeoutMs: number): Promise<KeychainToken | null> {
189
- const playwright = await import("playwright");
190
- const browser = await playwright.chromium.launch({ headless: true });
191
- try {
192
- const context = await browser.newContext();
193
- const page = await context.newPage();
194
-
195
- const tokenPromise = new Promise<string>((resolve, reject) => {
196
- const timer = setTimeout(() => {
197
- page.off("request", handleRequest);
198
- reject(new Error("Timed out waiting for Quip API token via Playwright."));
199
- }, timeoutMs);
200
-
201
- const handleRequest = (request: { url(): string; headers(): Record<string, string> }) => {
202
- const url = request.url();
203
- if (!url.includes("platform.quip.com")) return;
204
- const authHeader = request.headers()["authorization"];
205
- if (!authHeader?.startsWith("Bearer ")) return;
206
- clearTimeout(timer);
207
- page.off("request", handleRequest);
208
- resolve(authHeader.slice(7));
209
- };
210
-
211
- page.on("request", handleRequest);
212
- });
213
-
214
- await page.goto("https://quip.com", {
215
- waitUntil: "domcontentloaded",
216
- timeout: timeoutMs,
217
- });
218
-
219
- const token = await tokenPromise;
220
-
221
- // Get user ID from /users/current
222
- const resp = await context.request.get(`${QUIP_API_BASE}/users/current`, {
223
- headers: { Authorization: `Bearer ${token}` },
224
- });
225
- const userData = (await resp.json()) as Record<string, unknown>;
226
- const userId = typeof userData.id === "string" ? userData.id : "unknown";
227
-
228
- await page.close().catch(() => undefined);
229
- await context.close().catch(() => undefined);
230
- await browser.close().catch(() => undefined);
231
-
232
- return { token, userId };
233
- } catch (error) {
234
- await browser.close().catch(() => undefined);
235
- throw error;
236
- }
237
- }
238
-
239
- // ── Thread/document helpers ─────────────────────────────────────────
240
-
241
- function toDocumentSummary(thread: Record<string, unknown>): QuipDocumentSummary {
242
- return {
243
- threadId: typeof thread.id === "string" ? thread.id : "",
244
- title: typeof thread.title === "string" ? thread.title : "",
245
- link: typeof thread.link === "string" ? thread.link : "",
246
- updatedUsec: typeof thread.updated_usec === "number" ? thread.updated_usec : 0,
247
- createdUsec: typeof thread.created_usec === "number" ? thread.created_usec : 0,
248
- };
249
- }
250
-
251
- // ── Client factory ──────────────────────────────────────────────────
252
-
253
- export async function createQuipDesktopClient(options?: {
254
- bootstrapTimeoutMs?: number;
255
- }): Promise<QuipDesktopClient> {
256
- const timeoutMs = options?.bootstrapTimeoutMs ?? DEFAULT_BOOTSTRAP_TIMEOUT_MS;
257
-
258
- // Priority 1: Keychain desktop token (auto from Quip desktop app)
259
- let keychainResult = await extractKeychainDesktopToken();
260
-
261
- // Priority 2: Playwright web session (SSO handles auth)
262
- if (!keychainResult) {
263
- try {
264
- keychainResult = await extractPlaywrightToken(timeoutMs);
265
- } catch {
266
- // Playwright extraction failed — fall through
267
- }
268
- }
269
-
270
- // Priority 3: Manual keychain token
271
- let token: string;
272
- let userId: string;
273
- if (keychainResult) {
274
- token = keychainResult.token;
275
- userId = keychainResult.userId;
276
- } else {
277
- const manualToken = await extractManualKeychainToken();
278
- if (!manualToken) {
279
- throw new Error(
280
- "No Quip token available. Install the Quip desktop app, or add a manual token:\n" +
281
- ' security add-generic-password -a aria -s quip-token -w "<token>"\n' +
282
- "Get a token from https://quip.com/dev/token",
283
- );
284
- }
285
- token = manualToken;
286
- userId = "unknown";
287
- }
288
-
289
- // Verify token and resolve userId if unknown
290
- const invokeApi = async (
291
- method: "GET" | "POST",
292
- endpoint: string,
293
- body?: Record<string, unknown>,
294
- ): Promise<Record<string, unknown>> => {
295
- const url = `${QUIP_API_BASE}${endpoint}`;
296
- const headers: Record<string, string> = {
297
- Authorization: `Bearer ${token}`,
298
- };
299
- const fetchOptions: RequestInit = { method, headers };
300
- if (body) {
301
- headers["Content-Type"] = "application/x-www-form-urlencoded";
302
- fetchOptions.body = new URLSearchParams(
303
- Object.entries(body).map(([k, v]) => [k, String(v)]),
304
- ).toString();
305
- }
306
- const resp = await fetch(url, fetchOptions);
307
- const text = await resp.text();
308
- let json: Record<string, unknown>;
309
- try {
310
- json = JSON.parse(text) as Record<string, unknown>;
311
- } catch {
312
- throw new Error(`Quip API ${endpoint} returned non-JSON (HTTP ${resp.status}).`);
313
- }
314
- if (!resp.ok) {
315
- const errorCode = typeof json.error_code === "number" ? json.error_code : resp.status;
316
- const errorMsg =
317
- typeof json.error_description === "string" ? json.error_description : `HTTP ${resp.status}`;
318
- throw new Error(`Quip API ${endpoint} failed (${errorCode}): ${errorMsg}`);
319
- }
320
- return json;
321
- };
322
-
323
- // Resolve userId on first use if unknown
324
- if (userId === "unknown") {
325
- const user = await invokeApi("GET", "/users/current");
326
- userId = typeof user.id === "string" ? user.id : "unknown";
327
- }
328
-
329
- return {
330
- getUserId: () => userId,
331
-
332
- listRecentDocuments: async ({ limit = 20 }) => {
333
- const data = await invokeApi("GET", `/threads/recent?count=${Math.min(limit, 50)}`);
334
- const threads = Array.isArray(data)
335
- ? data
336
- : typeof data === "object" && data !== null
337
- ? Object.values(data)
338
- : [];
339
- const documents: QuipDocumentSummary[] = [];
340
- for (const entry of threads) {
341
- if (!entry || typeof entry !== "object") continue;
342
- const thread =
343
- "thread" in (entry as Record<string, unknown>)
344
- ? ((entry as Record<string, unknown>).thread as Record<string, unknown>)
345
- : (entry as Record<string, unknown>);
346
- documents.push(toDocumentSummary(thread));
347
- }
348
- return { documents: documents.slice(0, limit) };
349
- },
350
-
351
- searchDocuments: async ({ query, limit = 20 }) => {
352
- const params = new URLSearchParams({ query, count: String(Math.min(limit, 50)) });
353
- const data = await invokeApi("GET", `/threads/search?${params}`);
354
- const threads = Array.isArray(data)
355
- ? data
356
- : typeof data === "object" && data !== null
357
- ? Object.values(data)
358
- : [];
359
- const documents: QuipDocumentSummary[] = [];
360
- for (const entry of threads) {
361
- if (!entry || typeof entry !== "object") continue;
362
- const thread =
363
- "thread" in (entry as Record<string, unknown>)
364
- ? ((entry as Record<string, unknown>).thread as Record<string, unknown>)
365
- : (entry as Record<string, unknown>);
366
- documents.push(toDocumentSummary(thread));
367
- }
368
- return { documents: documents.slice(0, limit) };
369
- },
370
-
371
- readDocument: async ({ threadId }) => {
372
- const data = await invokeApi("GET", `/threads/${threadId}`);
373
- const thread =
374
- typeof data.thread === "object" && data.thread !== null
375
- ? (data.thread as Record<string, unknown>)
376
- : data;
377
- const rawHtml = typeof data.html === "string" ? data.html : "";
378
- return {
379
- threadId: typeof thread.id === "string" ? thread.id : threadId,
380
- title: typeof thread.title === "string" ? thread.title : "",
381
- link: typeof thread.link === "string" ? thread.link : "",
382
- markdown: htmlToMarkdown(rawHtml),
383
- updatedUsec: typeof thread.updated_usec === "number" ? thread.updated_usec : 0,
384
- createdUsec: typeof thread.created_usec === "number" ? thread.created_usec : 0,
385
- authorId: typeof thread.author_id === "string" ? thread.author_id : "",
386
- };
387
- },
388
-
389
- createDocument: async ({ title, content, format = "markdown", folderId }) => {
390
- const body: Record<string, unknown> = { title, content, format };
391
- if (folderId) body.member_ids = folderId;
392
- const data = await invokeApi("POST", "/threads/new-document", body);
393
- const thread =
394
- typeof data.thread === "object" && data.thread !== null
395
- ? (data.thread as Record<string, unknown>)
396
- : data;
397
- return {
398
- threadId: typeof thread.id === "string" ? thread.id : "",
399
- title: typeof thread.title === "string" ? thread.title : title,
400
- link: typeof thread.link === "string" ? thread.link : "",
401
- };
402
- },
403
-
404
- addComment: async ({ threadId, content }) => {
405
- const data = await invokeApi("POST", "/messages/new", {
406
- thread_id: threadId,
407
- content,
408
- });
409
- return {
410
- threadId,
411
- commentId: typeof data.id === "string" ? data.id : "",
412
- };
413
- },
414
-
415
- listComments: async ({ threadId }) => {
416
- const data = await invokeApi("GET", `/messages/${threadId}`);
417
- const rawMessages = Array.isArray(data) ? data : [];
418
- const comments: QuipCommentView[] = rawMessages
419
- .filter((m): m is Record<string, unknown> => !!m && typeof m === "object")
420
- .map((m) => ({
421
- id: typeof m.id === "string" ? m.id : "",
422
- authorId: typeof m.author_id === "string" ? m.author_id : "",
423
- authorName: typeof m.author_name === "string" ? m.author_name : "",
424
- text: typeof m.text === "string" ? m.text : "",
425
- createdUsec: typeof m.created_usec === "number" ? m.created_usec : 0,
426
- }));
427
- return { threadId, comments };
428
- },
429
-
430
- close: async () => {
431
- // No persistent resources to clean up (unlike Slack which keeps a browser open).
432
- // Token-based client is stateless.
433
- },
434
- };
435
- }
@@ -1 +0,0 @@
1
- export { ToolRegistry, validateToolInput } from "./registry.js";