openclaw-adspirer 0.2.0 → 0.2.1

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/LICENSE CHANGED
@@ -1,21 +1,8 @@
1
- MIT License
1
+ Copyright (c) 2026 Adspirer, Inc. All rights reserved.
2
2
 
3
- Copyright (c) 2026 Adspirer
3
+ This software is proprietary and confidential. Unauthorized copying, modification,
4
+ distribution, or use of this software, via any medium, is strictly prohibited.
4
5
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
6
+ This software is provided "as is" without warranty of any kind, express or implied.
11
7
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
8
+ For licensing inquiries, contact: support@adspirer.com
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "openclaw-adspirer",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin for Adspirer — manage Google, Meta, TikTok & LinkedIn ads via natural language",
6
- "license": "MIT",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "files": [
8
+ "src/",
9
+ "types/",
10
+ "openclaw.plugin.json",
11
+ "README.md",
12
+ "LICENSE",
13
+ "CHANGELOG.md"
14
+ ],
7
15
  "scripts": {
8
16
  "check-types": "tsc --noEmit",
9
17
  "test": "ESBUILD_BINARY_PATH=./node_modules/@esbuild/linux-x64/bin/esbuild tsx --test tests/*.test.ts",
@@ -1,75 +0,0 @@
1
- import { test, describe } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { generateCodeVerifier, generateCodeChallenge, buildAuthUrl } from "../src/auth.ts";
4
-
5
- describe("generateCodeVerifier", () => {
6
- test("length 43-128", () => {
7
- const v = generateCodeVerifier();
8
- assert.ok(v.length >= 43, `verifier too short: ${v.length}`);
9
- assert.ok(v.length <= 128, `verifier too long: ${v.length}`);
10
- });
11
-
12
- test("base64url charset only", () => {
13
- const v = generateCodeVerifier();
14
- assert.ok(/^[A-Za-z0-9\-_]+$/.test(v), `invalid chars in verifier: ${v}`);
15
- });
16
-
17
- test("unique each time", () => {
18
- const a = generateCodeVerifier();
19
- const b = generateCodeVerifier();
20
- assert.notEqual(a, b);
21
- });
22
- });
23
-
24
- describe("generateCodeChallenge", () => {
25
- test("produces base64url string", async () => {
26
- const v = generateCodeVerifier();
27
- const c = await generateCodeChallenge(v);
28
- assert.ok(/^[A-Za-z0-9\-_]+$/.test(c));
29
- });
30
-
31
- test("known vector: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", async () => {
32
- // RFC 7636 Appendix B test vector
33
- const verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
34
- const challenge = await generateCodeChallenge(verifier);
35
- assert.equal(challenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM");
36
- });
37
-
38
- test("deterministic", async () => {
39
- const v = "test_verifier_string";
40
- const a = await generateCodeChallenge(v);
41
- const b = await generateCodeChallenge(v);
42
- assert.equal(a, b);
43
- });
44
- });
45
-
46
- describe("buildAuthUrl", () => {
47
- test("contains all params using discovered authorize endpoint", () => {
48
- const url = buildAuthUrl({
49
- authorizeEndpoint: "https://mcp.adspirer.com/oauth/authorize",
50
- clientId: "openclaw-abc123",
51
- codeChallenge: "abc123",
52
- redirectUri: "http://localhost:52847/callback",
53
- scope: "ads:read ads:write accounts:read",
54
- state: "state123",
55
- });
56
- assert.ok(url.includes("client_id=openclaw-abc123"), "missing client_id");
57
- assert.ok(url.includes("redirect_uri=http%3A%2F%2Flocalhost%3A52847%2Fcallback"), "missing redirect_uri");
58
- assert.ok(url.includes("response_type=code"), "missing response_type");
59
- assert.ok(url.includes("code_challenge=abc123"), "missing code_challenge");
60
- assert.ok(url.includes("code_challenge_method=S256"), "missing code_challenge_method");
61
- assert.ok(url.includes("state=state123"), "missing state");
62
- });
63
-
64
- test("uses full authorize endpoint URL (not relative path)", () => {
65
- const url = buildAuthUrl({
66
- authorizeEndpoint: "https://auth.example.com/oauth/authorize",
67
- clientId: "test-client",
68
- codeChallenge: "challenge",
69
- redirectUri: "http://localhost:3000/callback",
70
- scope: "read",
71
- state: "s",
72
- });
73
- assert.ok(url.startsWith("https://auth.example.com/oauth/authorize?"), `unexpected URL prefix: ${url}`);
74
- });
75
- });
@@ -1,131 +0,0 @@
1
- import { test, describe, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { AdspirerMCPClient } from "../src/client.ts";
4
- import { parseConfig } from "../src/config.ts";
5
- import { installMockFetch, jsonResponse } from "./helpers/mock-fetch.ts";
6
-
7
- function makeClient(overrides: Record<string, unknown> = {}, fetchHandler?: (url: string | URL | Request, init?: RequestInit) => Promise<Response>) {
8
- const config = parseConfig({
9
- serverUrl: "https://test.example.com",
10
- testMode: true,
11
- ...overrides,
12
- });
13
- const logger = {
14
- info: () => {},
15
- warn: () => {},
16
- error: () => {},
17
- debug: () => {},
18
- };
19
- const client = new AdspirerMCPClient(
20
- config,
21
- async () => "test-token",
22
- () => {},
23
- async () => ({ accessToken: "new", refreshToken: "new", expiresIn: 3600, expiresAt: Date.now() + 3600000 }),
24
- logger,
25
- );
26
- return client;
27
- }
28
-
29
- describe("AdspirerMCPClient", () => {
30
- let restore: (() => void) | null = null;
31
- afterEach(() => { if (restore) { restore(); restore = null; } });
32
-
33
- test("callTool happy path", async () => {
34
- const client = makeClient();
35
- restore = installMockFetch(async () =>
36
- jsonResponse({
37
- jsonrpc: "2.0",
38
- result: { content: [{ type: "text", text: "hello" }] },
39
- id: "1",
40
- }),
41
- );
42
- const result = await client.callTool("echo_test", { message: "hi" });
43
- assert.equal(result.content[0].text, "hello");
44
- });
45
-
46
- test("callTool JSON-RPC error", async () => {
47
- const client = makeClient();
48
- restore = installMockFetch(async () =>
49
- jsonResponse({
50
- jsonrpc: "2.0",
51
- error: { code: -32602, message: "bad param" },
52
- id: "1",
53
- }),
54
- );
55
- const result = await client.callTool("echo_test", {});
56
- assert.ok(result.content[0].text.includes("bad param"));
57
- });
58
-
59
- test("callTool wraps non-content result", async () => {
60
- const client = makeClient();
61
- restore = installMockFetch(async () =>
62
- jsonResponse({
63
- jsonrpc: "2.0",
64
- result: { data: "raw" },
65
- id: "1",
66
- }),
67
- );
68
- const result = await client.callTool("echo_test", {});
69
- assert.ok(result.content[0].text.includes("raw"));
70
- });
71
-
72
- test("callTool 401 → refresh → retry (production mode)", async () => {
73
- let callCount = 0;
74
- const config = parseConfig({
75
- serverUrl: "https://test.example.com",
76
- testMode: false,
77
- accessToken: "old-token",
78
- refreshToken: "ref-token",
79
- tokenExpiresAt: Date.now() + 3600000,
80
- });
81
- const logger = { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} };
82
- let refreshCalled = false;
83
- const client = new AdspirerMCPClient(
84
- config,
85
- async () => "old-token",
86
- () => {},
87
- async () => { refreshCalled = true; return { accessToken: "new", refreshToken: "new", expiresIn: 3600, expiresAt: Date.now() + 3600000 }; },
88
- logger,
89
- );
90
-
91
- restore = installMockFetch(async () => {
92
- callCount++;
93
- if (callCount === 1) return new Response("Unauthorized", { status: 401 });
94
- return jsonResponse({ jsonrpc: "2.0", result: { content: [{ type: "text", text: "ok" }] }, id: "1" });
95
- });
96
-
97
- const result = await client.callTool("echo_test", {});
98
- assert.equal(refreshCalled, true);
99
- assert.equal(result.content[0].text, "ok");
100
- });
101
-
102
- test("callTool 402 → upgrade message", async () => {
103
- const client = makeClient({ testMode: false, accessToken: "tok", tokenExpiresAt: Date.now() + 3600000 });
104
- restore = installMockFetch(async () => new Response("Payment Required", { status: 402 }));
105
- const result = await client.callTool("premium_tool", {});
106
- assert.ok(result.content[0].text.includes("Upgrade"));
107
- });
108
-
109
- test("listTools happy path", async () => {
110
- const client = makeClient();
111
- restore = installMockFetch(async () =>
112
- jsonResponse({
113
- jsonrpc: "2.0",
114
- result: { tools: [{ name: "echo_test", description: "test", inputSchema: {} }] },
115
- id: "1",
116
- }),
117
- );
118
- const tools = await client.listTools();
119
- assert.equal(tools.length, 1);
120
- assert.equal(tools[0].name, "echo_test");
121
- });
122
-
123
- test("malformed response", async () => {
124
- const client = makeClient();
125
- restore = installMockFetch(async () =>
126
- jsonResponse({ invalid: true }),
127
- );
128
- const result = await client.callTool("echo_test", {});
129
- assert.ok(result.content[0].text.includes("Malformed"));
130
- });
131
- });
@@ -1,115 +0,0 @@
1
- import { test, describe } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { parseConfig, isAuthenticated, isTokenExpiringSoon, shouldRegisterTool } from "../src/config.ts";
4
-
5
- describe("parseConfig", () => {
6
- test("empty config → defaults", () => {
7
- const c = parseConfig({});
8
- assert.equal(c.serverUrl, "https://mcp.adspirer.com");
9
- assert.equal(c.requestTimeoutMs, 60000);
10
- assert.equal(c.testMode, false);
11
- assert.equal(c.enabledGroups.length, 6);
12
- assert.equal(c.enabledTools.length, 0);
13
- assert.equal(c.accessToken, undefined);
14
- });
15
-
16
- test("full config", () => {
17
- const c = parseConfig({
18
- serverUrl: "http://localhost:8000",
19
- testMode: true,
20
- testUserEmail: "test@test.com",
21
- accessToken: "tok",
22
- refreshToken: "ref",
23
- tokenExpiresAt: 99999999999999,
24
- defaultAccountId: "123",
25
- enabledGroups: ["google_ads"],
26
- enabledTools: ["echo_test"],
27
- requestTimeoutMs: 120000,
28
- });
29
- assert.equal(c.serverUrl, "http://localhost:8000");
30
- assert.equal(c.testMode, true);
31
- assert.equal(c.testUserEmail, "test@test.com");
32
- assert.equal(c.accessToken, "tok");
33
- assert.equal(c.refreshToken, "ref");
34
- assert.equal(c.enabledGroups.length, 1);
35
- assert.equal(c.enabledTools.length, 1);
36
- assert.equal(c.requestTimeoutMs, 120000);
37
- });
38
-
39
- test("partial config fills defaults", () => {
40
- const c = parseConfig({ serverUrl: "https://custom.com" });
41
- assert.equal(c.serverUrl, "https://custom.com");
42
- assert.equal(c.requestTimeoutMs, 60000);
43
- assert.equal(c.enabledGroups.length, 6);
44
- });
45
-
46
- test("null/undefined/non-object → defaults", () => {
47
- for (const v of [null, undefined, "string", 42, []]) {
48
- const c = parseConfig(v);
49
- assert.equal(c.serverUrl, "https://mcp.adspirer.com");
50
- }
51
- });
52
- });
53
-
54
- describe("isAuthenticated", () => {
55
- test("with valid token → true", () => {
56
- const c = parseConfig({ accessToken: "tok", tokenExpiresAt: Date.now() + 3600000 });
57
- assert.equal(isAuthenticated(c), true);
58
- });
59
-
60
- test("no token → false", () => {
61
- assert.equal(isAuthenticated(parseConfig({})), false);
62
- });
63
-
64
- test("expired token → false", () => {
65
- const c = parseConfig({ accessToken: "tok", tokenExpiresAt: Date.now() - 1000 });
66
- assert.equal(isAuthenticated(c), false);
67
- });
68
-
69
- test("testMode → true regardless", () => {
70
- const c = parseConfig({ testMode: true });
71
- assert.equal(isAuthenticated(c), true);
72
- });
73
- });
74
-
75
- describe("isTokenExpiringSoon", () => {
76
- test("expiring within 5min → true", () => {
77
- const c = parseConfig({ accessToken: "tok", tokenExpiresAt: Date.now() + 60000 });
78
- assert.equal(isTokenExpiringSoon(c), true);
79
- });
80
-
81
- test("not expiring soon → false", () => {
82
- const c = parseConfig({ accessToken: "tok", tokenExpiresAt: Date.now() + 600000 });
83
- assert.equal(isTokenExpiringSoon(c), false);
84
- });
85
-
86
- test("testMode → false", () => {
87
- const c = parseConfig({ testMode: true, tokenExpiresAt: Date.now() + 1000 });
88
- assert.equal(isTokenExpiringSoon(c), false);
89
- });
90
- });
91
-
92
- describe("shouldRegisterTool", () => {
93
- test("system tool → always true", () => {
94
- const c = parseConfig({ enabledGroups: ["google_ads"] });
95
- assert.equal(shouldRegisterTool("get_usage_status", "system", c), true);
96
- assert.equal(shouldRegisterTool("get_connections_status", "system", c), true);
97
- });
98
-
99
- test("group filter works", () => {
100
- const c = parseConfig({ enabledGroups: ["google_ads"] });
101
- assert.equal(shouldRegisterTool("get_campaign_performance", "google_ads", c), true);
102
- assert.equal(shouldRegisterTool("pause_meta_campaign", "meta_ads", c), false);
103
- });
104
-
105
- test("enabledTools override", () => {
106
- const c = parseConfig({ enabledTools: ["pause_meta_campaign"] });
107
- assert.equal(shouldRegisterTool("pause_meta_campaign", "meta_ads", c), true);
108
- assert.equal(shouldRegisterTool("get_campaign_performance", "google_ads", c), false);
109
- });
110
-
111
- test("default → all-on", () => {
112
- const c = parseConfig({});
113
- assert.equal(shouldRegisterTool("pause_meta_campaign", "meta_ads", c), true);
114
- });
115
- });
@@ -1,33 +0,0 @@
1
- import { test, describe } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { mapHTTPError, mapJSONRPCError, createErrorResult, isRetryableError } from "../src/errors.ts";
4
-
5
- describe("mapHTTPError", () => {
6
- test("401", () => assert.ok(mapHTTPError(401).includes("login")));
7
- test("402", () => assert.ok(mapHTTPError(402).includes("Upgrade")));
8
- test("403", () => assert.ok(mapHTTPError(403).includes("Access denied")));
9
- test("429", () => assert.ok(mapHTTPError(429).includes("Rate limit")));
10
- test("500", () => assert.ok(mapHTTPError(500).includes("service error")));
11
- });
12
-
13
- describe("mapJSONRPCError", () => {
14
- test("-32601", () => assert.ok(mapJSONRPCError({ code: -32601, message: "" }).includes("not found")));
15
- test("-32602", () => assert.ok(mapJSONRPCError({ code: -32602, message: "bad param" }).includes("bad param")));
16
- });
17
-
18
- describe("createErrorResult", () => {
19
- test("wraps in content format", () => {
20
- const r = createErrorResult("oops");
21
- assert.equal(r.content.length, 1);
22
- assert.equal(r.content[0].type, "text");
23
- assert.ok(r.content[0].text.includes("oops"));
24
- });
25
- });
26
-
27
- describe("isRetryableError", () => {
28
- test("401 → true", () => assert.equal(isRetryableError(401), true));
29
- test("429 → true", () => assert.equal(isRetryableError(429), true));
30
- test("503 → true", () => assert.equal(isRetryableError(503), true));
31
- test("500 → false", () => assert.equal(isRetryableError(500), false));
32
- test("200 → false", () => assert.equal(isRetryableError(200), false));
33
- });
@@ -1,12 +0,0 @@
1
- export function jsonResponse(body: unknown, status = 200, headers: Record<string, string> = {}): Response {
2
- return new Response(JSON.stringify(body), {
3
- status,
4
- headers: { "Content-Type": "application/json", ...headers },
5
- });
6
- }
7
-
8
- export function installMockFetch(handler: (url: string | URL | Request, init?: RequestInit) => Promise<Response>): () => void {
9
- const original = globalThis.fetch;
10
- globalThis.fetch = handler as typeof fetch;
11
- return () => { globalThis.fetch = original; };
12
- }
@@ -1,41 +0,0 @@
1
- import { test, describe } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import {
4
- ALL_TOOLS, GOOGLE_ADS_TOOLS, META_ADS_TOOLS, TIKTOK_ADS_TOOLS,
5
- LINKEDIN_ADS_TOOLS, MANUS_TOOLS, SYSTEM_TOOLS,
6
- getGroupForTool, getToolsForGroups,
7
- } from "../src/tool-groups.ts";
8
-
9
- describe("tool counts", () => {
10
- test("ALL_TOOLS = 104 (with 1 duplicate)", () => assert.equal(ALL_TOOLS.length, 104));
11
- test("Google Ads = 40", () => assert.equal(GOOGLE_ADS_TOOLS.length, 40));
12
- test("Meta Ads = 20", () => assert.equal(META_ADS_TOOLS.length, 20));
13
- test("TikTok Ads = 4", () => assert.equal(TIKTOK_ADS_TOOLS.length, 4));
14
- test("LinkedIn Ads = 28", () => assert.equal(LINKEDIN_ADS_TOOLS.length, 28));
15
- test("Manus = 8", () => assert.equal(MANUS_TOOLS.length, 8));
16
- test("System = 4", () => assert.equal(SYSTEM_TOOLS.length, 4));
17
-
18
- test("ALL_TOOLS has 104 entries (get_usage_status in google_ads and system)", () => {
19
- assert.equal(ALL_TOOLS.length, 104);
20
- const unique = new Set(ALL_TOOLS);
21
- assert.equal(unique.size, 103); // 103 unique tools
22
- });
23
- });
24
-
25
- describe("getGroupForTool", () => {
26
- test("known google tool", () => assert.equal(getGroupForTool("get_campaign_performance"), "google_ads"));
27
- test("known meta tool", () => assert.equal(getGroupForTool("pause_meta_campaign"), "meta_ads"));
28
- test("known system tool", () => assert.equal(getGroupForTool("switch_primary_account"), "system"));
29
- test("unknown tool", () => assert.equal(getGroupForTool("nonexistent_tool"), null));
30
- });
31
-
32
- describe("getToolsForGroups", () => {
33
- test("meta_ads + tiktok_ads = 24", () => {
34
- const tools = getToolsForGroups(["meta_ads", "tiktok_ads"]);
35
- assert.equal(tools.length, 24);
36
- });
37
-
38
- test("empty groups = 0", () => {
39
- assert.equal(getToolsForGroups([]).length, 0);
40
- });
41
- });
@@ -1,118 +0,0 @@
1
- import { test, describe, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { isWriteTool, fetchAndRegisterTools, registerStaticFallbacks, registerConnectTool, createToolExecutor } from "../src/tool-registry.ts";
4
- import { parseConfig } from "../src/config.ts";
5
- import { AdspirerMCPClient } from "../src/client.ts";
6
- import { installMockFetch, jsonResponse } from "./helpers/mock-fetch.ts";
7
-
8
- function makeMockApi() {
9
- const registered: { name: string; opts?: any }[] = [];
10
- return {
11
- pluginConfig: {},
12
- logger: { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} },
13
- registerTool: (tool: any, opts?: any) => { registered.push({ name: tool.name, opts }); },
14
- registerCommand: () => {},
15
- registerCli: () => {},
16
- registerService: () => {},
17
- on: () => {},
18
- updateConfig: () => {},
19
- _registered: registered,
20
- };
21
- }
22
-
23
- function makeClient(config = parseConfig({ testMode: true, serverUrl: "https://test.example.com" })) {
24
- return new AdspirerMCPClient(
25
- config,
26
- async () => "",
27
- () => {},
28
- async () => ({ accessToken: "", refreshToken: "", expiresIn: 3600, expiresAt: 0 }),
29
- { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} },
30
- );
31
- }
32
-
33
- describe("isWriteTool", () => {
34
- test("create_ prefix → write", () => assert.equal(isWriteTool("create_search_campaign"), true));
35
- test("update_ prefix → write", () => assert.equal(isWriteTool("update_campaign"), true));
36
- test("pause_ prefix → write", () => assert.equal(isWriteTool("pause_campaign"), true));
37
- test("get_ prefix → read", () => assert.equal(isWriteTool("get_campaign_performance"), false));
38
- test("list_ prefix → read", () => assert.equal(isWriteTool("list_campaigns"), false));
39
- test("analyze_ prefix → read", () => assert.equal(isWriteTool("analyze_wasted_spend"), false));
40
- test("explicit write: switch_primary_account", () => assert.equal(isWriteTool("switch_primary_account"), true));
41
- test("explicit write: infer_business_profile", () => assert.equal(isWriteTool("infer_business_profile"), true));
42
- test("echo_test → read", () => assert.equal(isWriteTool("echo_test"), false));
43
- });
44
-
45
- describe("fetchAndRegisterTools", () => {
46
- let restore: (() => void) | null = null;
47
- afterEach(() => { if (restore) { restore(); restore = null; } });
48
-
49
- test("registers tools from server", async () => {
50
- const api = makeMockApi();
51
- const config = parseConfig({ testMode: true, serverUrl: "https://test.example.com" });
52
- const client = makeClient(config);
53
-
54
- restore = installMockFetch(async () =>
55
- jsonResponse({
56
- jsonrpc: "2.0",
57
- result: {
58
- tools: [
59
- { name: "echo_test", description: "test", inputSchema: {} },
60
- { name: "create_search_campaign", description: "create", inputSchema: {} },
61
- ],
62
- },
63
- id: "1",
64
- }),
65
- );
66
-
67
- const count = await fetchAndRegisterTools(api as any, client, config);
68
- assert.equal(count, 2);
69
- assert.equal(api._registered.length, 2);
70
- // echo_test is read, no confirmation needed
71
- assert.equal(api._registered[0].opts, undefined);
72
- // create_search_campaign is write, needs confirmation
73
- assert.deepEqual(api._registered[1].opts, { needsConfirmation: true });
74
- });
75
-
76
- test("filters by enabledGroups", async () => {
77
- const api = makeMockApi();
78
- const config = parseConfig({ testMode: true, serverUrl: "https://test.example.com", enabledGroups: ["meta_ads"] });
79
- const client = makeClient(config);
80
-
81
- restore = installMockFetch(async () =>
82
- jsonResponse({
83
- jsonrpc: "2.0",
84
- result: {
85
- tools: [
86
- { name: "get_campaign_performance", description: "google", inputSchema: {} },
87
- { name: "get_meta_campaign_performance", description: "meta", inputSchema: {} },
88
- { name: "get_usage_status", description: "system", inputSchema: {} }, // system always
89
- ],
90
- },
91
- id: "1",
92
- }),
93
- );
94
-
95
- const count = await fetchAndRegisterTools(api as any, client, config);
96
- // get_campaign_performance filtered out (google_ads), meta + system stay
97
- assert.equal(count, 2);
98
- });
99
- });
100
-
101
- describe("registerStaticFallbacks", () => {
102
- test("registers 6 fallback tools", () => {
103
- const api = makeMockApi();
104
- const config = parseConfig({ testMode: true });
105
- const client = makeClient(config);
106
- registerStaticFallbacks(api as any, client, config);
107
- assert.equal(api._registered.length, 6);
108
- });
109
- });
110
-
111
- describe("registerConnectTool", () => {
112
- test("registers adspirer_connect", () => {
113
- const api = makeMockApi();
114
- registerConnectTool(api as any);
115
- assert.equal(api._registered.length, 1);
116
- assert.equal(api._registered[0].name, "adspirer_connect");
117
- });
118
- });
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ES2022",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "allowImportingTsExtensions": true,
12
- "noEmit": true,
13
- "rootDir": "."
14
- },
15
- "include": [
16
- "src/**/*.ts",
17
- "types/*.d.ts"
18
- ],
19
- "exclude": ["node_modules", "dist", "tests"]
20
- }
package/vitest.config.ts DELETED
@@ -1,9 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: "node",
7
- include: ["tests/**/*.test.ts"],
8
- },
9
- });