Skip to content

Commit d5c1ae5

Browse files
authored
Merge pull request #6 from superagents-lab/agent/add-plugin-sdk
Expose reusable Search1API SDK
2 parents 28e06be + 4be8293 commit d5c1ae5

10 files changed

Lines changed: 314 additions & 72 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,27 @@ s1 update
165165
s1 update --force # reinstall even if already on the latest version
166166
```
167167

168+
## SDK
169+
170+
Plugin and integration packages can reuse the CLI's authentication, OAuth
171+
refresh, request handling, and typed Search1API methods instead of maintaining
172+
a second client:
173+
174+
```ts
175+
import { search, crawl } from "search1api-cli/sdk";
176+
177+
const results = await search("OpenCode plugins", {
178+
maxResults: 5,
179+
searchService: "github",
180+
});
181+
182+
const page = await crawl("https://example.com");
183+
```
184+
185+
The SDK uses `SEARCH1API_KEY`, the shared `s1 login` OAuth session, or the
186+
optional per-call `apiKey`. Every method also accepts an `AbortSignal` so host
187+
applications can cancel tool calls.
188+
168189
## Agent skill and plugins
169190

170191
This repo includes an Agent Skill plus compatibility manifests for Claude Code,

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
{
22
"name": "search1api-cli",
3-
"version": "1.2.3",
3+
"version": "1.3.0",
44
"description": "CLI tool for Search1API - web search, news, crawl, sitemap, and trending",
55
"type": "module",
6+
"exports": {
7+
".": {
8+
"types": "./dist/sdk.d.ts",
9+
"import": "./dist/sdk.js"
10+
},
11+
"./sdk": {
12+
"types": "./dist/sdk.d.ts",
13+
"import": "./dist/sdk.js"
14+
}
15+
},
616
"bin": {
717
"search1api": "./dist/index.js",
818
"s1": "./dist/index.js"

src/api.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
import { fetchWithAuth } from "./auth.js";
22
import { API_BASE } from "./config.js";
33

4+
export interface RequestOptions {
5+
apiKey?: string;
6+
signal?: AbortSignal;
7+
}
8+
49
export async function request<T>(
510
path: string,
6-
body: Record<string, unknown>
11+
body: Record<string, unknown>,
12+
options: RequestOptions = {}
713
): Promise<T> {
814
const url = `${API_BASE}${path}`;
9-
10-
const res = await fetchWithAuth(url, {
15+
const init: RequestInit = {
1116
method: "POST",
1217
headers: {
1318
"Content-Type": "application/json",
1419
},
1520
body: JSON.stringify(body),
16-
});
21+
signal: options.signal,
22+
};
23+
const res = options.apiKey
24+
? await fetch(url, {
25+
...init,
26+
headers: {
27+
...Object.fromEntries(new Headers(init.headers).entries()),
28+
Authorization: `Bearer ${options.apiKey}`,
29+
},
30+
})
31+
: await fetchWithAuth(url, init);
1732

1833
if (!res.ok) {
1934
const text = await res.text();

src/commands/crawl.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
import { Command } from "commander";
2-
import { request } from "../api.js";
2+
import { crawl } from "../sdk.js";
33
import { printCrawlResult, printJson } from "../output.js";
44

5-
interface CrawlResponse {
6-
results: {
7-
title: string;
8-
link: string;
9-
content: string;
10-
};
11-
}
12-
135
export function registerCrawlCommand(program: Command): void {
146
program
157
.command("crawl <url>")
168
.description("Extract content from a URL")
179
.option("--json", "output raw JSON")
1810
.action(async (url: string, opts) => {
19-
const data = await request<CrawlResponse>("/crawl", { url });
11+
const data = await crawl(url);
2012

2113
if (opts.json) {
2214
printJson(data);

src/commands/news.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
import { Command } from "commander";
2-
import { request } from "../api.js";
2+
import { news, type NewsOptions } from "../sdk.js";
33
import { printSearchResults, printJson } from "../output.js";
44

55
const NEWS_SERVICES = ["google", "bing", "duckduckgo", "yahoo", "hackernews"];
66

7-
interface NewsResponse {
8-
results: Array<{
9-
title: string;
10-
link: string;
11-
snippet: string;
12-
content?: string;
13-
}>;
14-
}
15-
167
export function registerNewsCommand(program: Command): void {
178
program
189
.command("news <query>")
@@ -25,17 +16,16 @@ export function registerNewsCommand(program: Command): void {
2516
.option("-t, --time <range>", "time range: day, month, year")
2617
.option("--json", "output raw JSON")
2718
.action(async (query: string, opts) => {
28-
const body: Record<string, unknown> = {
29-
query,
30-
max_results: parseInt(opts.maxResults),
31-
search_service: opts.service,
32-
crawl_results: parseInt(opts.crawl),
19+
const options: NewsOptions = {
20+
maxResults: parseInt(opts.maxResults),
21+
searchService: opts.service,
22+
crawlResults: parseInt(opts.crawl),
3323
};
34-
if (opts.include) body.include_sites = opts.include;
35-
if (opts.exclude) body.exclude_sites = opts.exclude;
36-
if (opts.time) body.time_range = opts.time;
24+
if (opts.include) options.includeSites = opts.include;
25+
if (opts.exclude) options.excludeSites = opts.exclude;
26+
if (opts.time) options.timeRange = opts.time;
3727

38-
const data = await request<NewsResponse>("/news", body);
28+
const data = await news(query, options);
3929

4030
if (opts.json) {
4131
printJson(data);

src/commands/search.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
import { Command } from "commander";
2-
import { request } from "../api.js";
2+
import { search, type SearchOptions } from "../sdk.js";
33
import { printSearchResults, printJson } from "../output.js";
44

55
const SEARCH_SERVICES = [
66
"google", "bing", "duckduckgo", "yahoo", "x", "reddit",
77
"github", "youtube", "arxiv", "wechat", "bilibili", "imdb", "wikipedia",
88
];
99

10-
interface SearchResponse {
11-
results: Array<{
12-
title: string;
13-
link: string;
14-
snippet: string;
15-
content?: string;
16-
}>;
17-
}
18-
1910
export function registerSearchCommand(program: Command): void {
2011
program
2112
.command("search <query>")
@@ -28,17 +19,16 @@ export function registerSearchCommand(program: Command): void {
2819
.option("-t, --time <range>", "time range: day, month, year")
2920
.option("--json", "output raw JSON")
3021
.action(async (query: string, opts) => {
31-
const body: Record<string, unknown> = {
32-
query,
33-
max_results: parseInt(opts.maxResults),
34-
search_service: opts.service,
35-
crawl_results: parseInt(opts.crawl),
22+
const options: SearchOptions = {
23+
maxResults: parseInt(opts.maxResults),
24+
searchService: opts.service,
25+
crawlResults: parseInt(opts.crawl),
3626
};
37-
if (opts.include) body.include_sites = opts.include;
38-
if (opts.exclude) body.exclude_sites = opts.exclude;
39-
if (opts.time) body.time_range = opts.time;
27+
if (opts.include) options.includeSites = opts.include;
28+
if (opts.exclude) options.excludeSites = opts.exclude;
29+
if (opts.time) options.timeRange = opts.time;
4030

41-
const data = await request<SearchResponse>("/search", body);
31+
const data = await search(query, options);
4232

4333
if (opts.json) {
4434
printJson(data);

src/commands/sitemap.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
import { Command } from "commander";
2-
import { request } from "../api.js";
2+
import { sitemap } from "../sdk.js";
33
import { printSitemapLinks, printJson } from "../output.js";
44

5-
interface SitemapResponse {
6-
links: string[];
7-
}
8-
95
export function registerSitemapCommand(program: Command): void {
106
program
117
.command("sitemap <url>")
128
.description("Get related links from a URL")
139
.option("--json", "output raw JSON")
1410
.action(async (url: string, opts) => {
15-
const data = await request<SitemapResponse>("/sitemap", { url });
11+
const data = await sitemap(url);
1612

1713
if (opts.json) {
1814
printJson(data);

src/commands/trending.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
import { Command } from "commander";
2-
import { request } from "../api.js";
2+
import { trending, type TrendingService } from "../sdk.js";
33
import { printTrendingResults, printJson } from "../output.js";
44

55
const TRENDING_SERVICES = ["github", "hackernews"];
66

7-
interface TrendingResponse {
8-
results: Array<{
9-
title: string;
10-
url: string;
11-
description?: string;
12-
}>;
13-
}
14-
157
export function registerTrendingCommand(program: Command): void {
168
program
179
.command("trending <service>")
1810
.description(`Get trending topics (${TRENDING_SERVICES.join(", ")})`)
1911
.option("-n, --max-results <number>", "max results (1-50)", "10")
2012
.option("--json", "output raw JSON")
2113
.action(async (service: string, opts) => {
22-
const data = await request<TrendingResponse>("/trending", {
23-
search_service: service,
24-
max_results: parseInt(opts.maxResults),
25-
});
14+
const data = await trending(
15+
service as TrendingService,
16+
{ maxResults: parseInt(opts.maxResults) }
17+
);
2618

2719
if (opts.json) {
2820
printJson(data);

0 commit comments

Comments
 (0)