@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.
- package/package.json +9 -5
- package/src/__tests__/web-fetch-download.test.ts +0 -433
- package/src/__tests__/web-tools.test.ts +0 -619
- package/src/ask-user-interaction.ts +0 -33
- package/src/cache/web-cache.ts +0 -110
- package/src/definitions/arion.ts +0 -118
- package/src/definitions/browser/browser.ts +0 -502
- package/src/definitions/browser/index.ts +0 -5
- package/src/definitions/browser/pw-downloads.ts +0 -142
- package/src/definitions/browser/pw-interactions.ts +0 -282
- package/src/definitions/browser/pw-responses.ts +0 -98
- package/src/definitions/browser/pw-session.ts +0 -405
- package/src/definitions/browser/pw-shared.ts +0 -85
- package/src/definitions/browser/pw-snapshot.ts +0 -383
- package/src/definitions/browser/pw-state.ts +0 -101
- package/src/definitions/browser/types.ts +0 -203
- package/src/definitions/code-intelligence.ts +0 -526
- package/src/definitions/core.ts +0 -118
- package/src/definitions/delegation.ts +0 -567
- package/src/definitions/deploy.ts +0 -73
- package/src/definitions/filesystem.ts +0 -217
- package/src/definitions/frg.ts +0 -67
- package/src/definitions/index.ts +0 -28
- package/src/definitions/memory.ts +0 -150
- package/src/definitions/messaging.ts +0 -734
- package/src/definitions/meta.ts +0 -392
- package/src/definitions/network.ts +0 -179
- package/src/definitions/outlook.ts +0 -318
- package/src/definitions/patch/apply-patch.ts +0 -235
- package/src/definitions/patch/fuzzy-match.ts +0 -217
- package/src/definitions/patch/index.ts +0 -1
- package/src/definitions/patch/patch-parser.ts +0 -297
- package/src/definitions/patch/sandbox-paths.ts +0 -129
- package/src/definitions/process/index.ts +0 -5
- package/src/definitions/process/process-registry.ts +0 -303
- package/src/definitions/process/process.ts +0 -456
- package/src/definitions/process/pty-keys.ts +0 -298
- package/src/definitions/process/session-slug.ts +0 -147
- package/src/definitions/quip.ts +0 -225
- package/src/definitions/search.ts +0 -67
- package/src/definitions/session-history.ts +0 -79
- package/src/definitions/shell.ts +0 -202
- package/src/definitions/slack.ts +0 -211
- package/src/definitions/web.ts +0 -119
- package/src/executors/apply-patch.ts +0 -1035
- package/src/executors/arion.ts +0 -199
- package/src/executors/code-intelligence.ts +0 -1179
- package/src/executors/deploy.ts +0 -1066
- package/src/executors/filesystem.ts +0 -1428
- package/src/executors/frg-freshness.ts +0 -743
- package/src/executors/frg.ts +0 -394
- package/src/executors/index.ts +0 -280
- package/src/executors/learning-meta.ts +0 -1367
- package/src/executors/lsp-client.ts +0 -355
- package/src/executors/memory.ts +0 -978
- package/src/executors/meta.ts +0 -293
- package/src/executors/process-registry.ts +0 -570
- package/src/executors/pty-session-store.ts +0 -43
- package/src/executors/pty.ts +0 -342
- package/src/executors/restart.ts +0 -133
- package/src/executors/search-freshness.ts +0 -249
- package/src/executors/search-types.ts +0 -98
- package/src/executors/search.ts +0 -89
- package/src/executors/self-diagnose.ts +0 -552
- package/src/executors/session-history.ts +0 -435
- package/src/executors/shell-safety.ts +0 -519
- package/src/executors/shell.ts +0 -1243
- package/src/executors/utils.ts +0 -40
- package/src/executors/web.ts +0 -786
- package/src/extraction/content-extraction.ts +0 -281
- package/src/extraction/index.ts +0 -5
- package/src/headless-control-contract.ts +0 -1149
- package/src/index.ts +0 -788
- package/src/local-control-http-auth.ts +0 -2
- package/src/mcp/client.ts +0 -218
- package/src/mcp/connection.ts +0 -568
- package/src/mcp/index.ts +0 -11
- package/src/mcp/jsonrpc.ts +0 -195
- package/src/mcp/types.ts +0 -199
- package/src/network-control-adapter.ts +0 -88
- package/src/network-runtime/address-types.ts +0 -218
- package/src/network-runtime/db-owner-fencing.ts +0 -91
- package/src/network-runtime/delivery-receipts.ts +0 -372
- package/src/network-runtime/direct-endpoint-authority.ts +0 -35
- package/src/network-runtime/index.ts +0 -316
- package/src/network-runtime/local-control-contract.ts +0 -784
- package/src/network-runtime/node-store-contract.ts +0 -46
- package/src/network-runtime/pair-route-contract.ts +0 -97
- package/src/network-runtime/peer-capabilities.ts +0 -48
- package/src/network-runtime/peer-principal-ref.ts +0 -20
- package/src/network-runtime/peer-state-machine.ts +0 -160
- package/src/network-runtime/protocol-schemas.ts +0 -265
- package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
- package/src/outlook/desktop-session.ts +0 -409
- package/src/policy.ts +0 -171
- package/src/providers/brave.ts +0 -80
- package/src/providers/duckduckgo.ts +0 -199
- package/src/providers/exa.ts +0 -85
- package/src/providers/firecrawl.ts +0 -77
- package/src/providers/index.ts +0 -8
- package/src/providers/jina.ts +0 -70
- package/src/providers/router.ts +0 -121
- package/src/providers/search-provider.ts +0 -74
- package/src/providers/tavily.ts +0 -74
- package/src/quip/desktop-session.ts +0 -435
- package/src/registry/index.ts +0 -1
- package/src/registry/registry.ts +0 -905
- package/src/runtime-socket-local-control-client.ts +0 -632
- package/src/security/dns-normalization.ts +0 -34
- package/src/security/dns-pinning.ts +0 -138
- package/src/security/external-content.ts +0 -129
- package/src/security/ssrf.ts +0 -207
- package/src/slack/desktop-session.ts +0 -493
- package/src/tool-factory.ts +0 -91
- package/src/types.ts +0 -1341
- package/src/utils/retry.ts +0 -163
- package/src/utils/safe-parse-json.ts +0 -176
- package/src/utils/url.ts +0 -20
- package/tests/benchmarks/registry.bench.ts +0 -57
- package/tests/cache/web-cache.test.ts +0 -147
- package/tests/critical-integration.test.ts +0 -1465
- package/tests/definitions/apply-patch.test.ts +0 -586
- package/tests/definitions/browser.test.ts +0 -495
- package/tests/definitions/delegation-pause-resume.test.ts +0 -758
- package/tests/definitions/execution.test.ts +0 -671
- package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
- package/tests/definitions/messaging.test.ts +0 -1468
- package/tests/definitions/outlook.test.ts +0 -30
- package/tests/definitions/process.test.ts +0 -469
- package/tests/definitions/slack.test.ts +0 -28
- package/tests/definitions/tool-inventory.test.ts +0 -218
- package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
- package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
- package/tests/executors/apply-patch.test.ts +0 -538
- package/tests/executors/arion.test.ts +0 -309
- package/tests/executors/conversation-primitives.test.ts +0 -250
- package/tests/executors/deploy.test.ts +0 -746
- package/tests/executors/filesystem-tools.test.ts +0 -357
- package/tests/executors/filesystem.test.ts +0 -959
- package/tests/executors/frg-freshness.test.ts +0 -136
- package/tests/executors/frg-merge.test.ts +0 -70
- package/tests/executors/frg-session-content.test.ts +0 -40
- package/tests/executors/frg.test.ts +0 -56
- package/tests/executors/memory-bugfixes.test.ts +0 -257
- package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
- package/tests/executors/memory.test.ts +0 -853
- package/tests/executors/meta-tools.test.ts +0 -411
- package/tests/executors/meta.test.ts +0 -683
- package/tests/executors/path-containment.test.ts +0 -51
- package/tests/executors/process-registry.test.ts +0 -505
- package/tests/executors/pty.test.ts +0 -664
- package/tests/executors/quest-security.test.ts +0 -249
- package/tests/executors/read-file-media.test.ts +0 -230
- package/tests/executors/recall-knowledge-schema.test.ts +0 -209
- package/tests/executors/recall-tags.test.ts +0 -278
- package/tests/executors/remember-null-safety.contract.test.ts +0 -41
- package/tests/executors/restart.test.ts +0 -67
- package/tests/executors/search-unified.test.ts +0 -381
- package/tests/executors/session-history.test.ts +0 -340
- package/tests/executors/session-transcript.test.ts +0 -561
- package/tests/executors/shell-abort.test.ts +0 -416
- package/tests/executors/shell-env-blocklist.test.ts +0 -648
- package/tests/executors/shell-env-process.test.ts +0 -245
- package/tests/executors/shell-process-registry.test.ts +0 -334
- package/tests/executors/shell-tools.test.ts +0 -393
- package/tests/executors/shell.test.ts +0 -690
- package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
- package/tests/executors/web-integration.test.ts +0 -633
- package/tests/executors/web-symlink.test.ts +0 -18
- package/tests/executors/web.test.ts +0 -1400
- package/tests/executors/write-stdin.test.ts +0 -145
- package/tests/extraction/content-extraction.test.ts +0 -153
- package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
- package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
- package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
- package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
- package/tests/helpers/async-waits.ts +0 -53
- package/tests/integration/headless-control-contract.integration.test.ts +0 -153
- package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
- package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
- package/tests/integration/quest-round-trip.test.ts +0 -303
- package/tests/integration/registry-executor-flow.test.ts +0 -85
- package/tests/integration.test.ts +0 -177
- package/tests/loading-tier.test.ts +0 -126
- package/tests/mcp/client-reconnect.test.ts +0 -267
- package/tests/mcp/connection.test.ts +0 -846
- package/tests/mcp/injectable-logger.test.ts +0 -83
- package/tests/mcp/jsonrpc.test.ts +0 -109
- package/tests/mcp/lifecycle.test.ts +0 -879
- package/tests/network-runtime/address-types.contract.test.ts +0 -143
- package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
- package/tests/network-runtime/local-control-contract.test.ts +0 -869
- package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
- package/tests/network-runtime/node-store-contract.test.ts +0 -11
- package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
- package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
- package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
- package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
- package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
- package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
- package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
- package/tests/parallel.test.ts +0 -71
- package/tests/policy.test.ts +0 -184
- package/tests/print-default-test-lane.ts +0 -14
- package/tests/print-test-lane-manifest.ts +0 -22
- package/tests/providers/brave.test.ts +0 -159
- package/tests/providers/duckduckgo.test.ts +0 -207
- package/tests/providers/exa.test.ts +0 -175
- package/tests/providers/firecrawl.test.ts +0 -168
- package/tests/providers/jina.test.ts +0 -144
- package/tests/providers/router.test.ts +0 -328
- package/tests/providers/tavily.test.ts +0 -165
- package/tests/registry/discovery.test.ts +0 -154
- package/tests/registry/injectable-logger.test.ts +0 -230
- package/tests/registry/input-validation.test.ts +0 -361
- package/tests/registry/interface-completeness.test.ts +0 -85
- package/tests/registry/mcp-integration.test.ts +0 -103
- package/tests/registry/mcp-read-only-hint.test.ts +0 -60
- package/tests/registry/memoria-discovery.test.ts +0 -390
- package/tests/registry/nested-validation.test.ts +0 -283
- package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
- package/tests/registry/registration-lifecycle.test.ts +0 -133
- package/tests/registry-validation.test.ts +0 -424
- package/tests/registry.test.ts +0 -460
- package/tests/security/dns-pinning.test.ts +0 -162
- package/tests/security/external-content.test.ts +0 -144
- package/tests/security/ssrf.test.ts +0 -118
- package/tests/shell-safety-integration.test.ts +0 -32
- package/tests/shell-safety.test.ts +0 -365
- package/tests/slack/desktop-session.test.ts +0 -50
- package/tests/test-lane-manifest.ts +0 -440
- package/tests/test-utils.ts +0 -27
- package/tests/tool-factory.test.ts +0 -188
- package/tests/utils/retry.test.ts +0 -231
- package/tests/utils/url.test.ts +0 -63
- package/tsconfig.cjs.json +0 -24
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -55
- package/vitest.e2e.config.ts +0 -24
- package/vitest.integration.config.ts +0 -24
- 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
|
-
}
|
package/src/providers/tavily.ts
DELETED
|
@@ -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(/&/g, "&");
|
|
113
|
-
md = md.replace(/</g, "<");
|
|
114
|
-
md = md.replace(/>/g, ">");
|
|
115
|
-
md = md.replace(/"/g, '"');
|
|
116
|
-
md = md.replace(/'/g, "'");
|
|
117
|
-
md = md.replace(/ /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
|
-
}
|
package/src/registry/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { ToolRegistry, validateToolInput } from "./registry.js";
|