@xquik/tweetclaw 1.6.5 → 1.6.6

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 (53) hide show
  1. package/README.md +22 -13
  2. package/dist/api-spec.d.ts +3 -0
  3. package/dist/api-spec.js +1427 -0
  4. package/dist/api-spec.js.map +1 -0
  5. package/dist/commands/xstatus.d.ts +17 -0
  6. package/dist/commands/xstatus.js +52 -0
  7. package/dist/commands/xstatus.js.map +1 -0
  8. package/dist/commands/xtrends.d.ts +16 -0
  9. package/dist/commands/xtrends.js +39 -0
  10. package/dist/commands/xtrends.js.map +1 -0
  11. package/dist/index.d.ts +107 -0
  12. package/dist/index.js +249 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/mpp.d.ts +7 -0
  15. package/dist/mpp.js +39 -0
  16. package/dist/mpp.js.map +1 -0
  17. package/dist/request.d.ts +7 -0
  18. package/dist/request.js +88 -0
  19. package/dist/request.js.map +1 -0
  20. package/dist/services/event-poller.d.ts +7 -0
  21. package/dist/services/event-poller.js +69 -0
  22. package/dist/services/event-poller.js.map +1 -0
  23. package/dist/tools/catalog.d.ts +17 -0
  24. package/dist/tools/catalog.js +110 -0
  25. package/dist/tools/catalog.js.map +1 -0
  26. package/dist/tools/explore.d.ts +5 -0
  27. package/dist/tools/explore.js +28 -0
  28. package/dist/tools/explore.js.map +1 -0
  29. package/dist/tools/result.d.ts +5 -0
  30. package/dist/tools/result.js +15 -0
  31. package/dist/tools/result.js.map +1 -0
  32. package/dist/tools/tweetclaw.d.ts +13 -0
  33. package/dist/tools/tweetclaw.js +62 -0
  34. package/dist/tools/tweetclaw.js.map +1 -0
  35. package/dist/truncate.d.ts +3 -0
  36. package/dist/truncate.js +25 -0
  37. package/dist/truncate.js.map +1 -0
  38. package/dist/types.d.ts +64 -0
  39. package/dist/types.js +2 -0
  40. package/dist/types.js.map +1 -0
  41. package/openclaw.plugin.json +7 -8
  42. package/package.json +17 -17
  43. package/skills/tweetclaw/SKILL.md +33 -42
  44. package/src/api-spec.ts +480 -12
  45. package/src/index.ts +135 -36
  46. package/src/mpp.ts +9 -11
  47. package/src/request.ts +27 -2
  48. package/src/tools/catalog.ts +145 -0
  49. package/src/tools/explore.ts +18 -44
  50. package/src/tools/result.ts +19 -0
  51. package/src/tools/tweetclaw.ts +49 -296
  52. package/src/types.ts +19 -0
  53. package/src/tools/executor.ts +0 -125
package/dist/mpp.js ADDED
@@ -0,0 +1,39 @@
1
+ function isRecord(value) {
2
+ return typeof value === 'object' && value !== null;
3
+ }
4
+ function isCallable(value) {
5
+ return typeof value === 'function';
6
+ }
7
+ async function loadDynamicModule(name) {
8
+ const mod = await import(name);
9
+ if (!isRecord(mod)) {
10
+ throw new Error(`Failed to load ${name}`);
11
+ }
12
+ return mod;
13
+ }
14
+ function createModuleLoader() {
15
+ return loadDynamicModule;
16
+ }
17
+ async function initMpp(tempoSigningKey, loadModule) {
18
+ const load = loadModule ?? createModuleLoader();
19
+ const mppxMod = await load('mppx/client').catch(() => {
20
+ throw new Error('MPP requires mppx package. Run: npm i mppx viem');
21
+ });
22
+ const viemMod = await load('viem/accounts').catch(() => {
23
+ throw new Error('MPP requires viem package. Run: npm i mppx viem');
24
+ });
25
+ if (!isCallable(viemMod.privateKeyToAccount))
26
+ throw new Error('viem missing privateKeyToAccount');
27
+ if (!isCallable(mppxMod.tempo))
28
+ throw new Error('mppx missing tempo');
29
+ if (!isRecord(mppxMod.Mppx))
30
+ throw new Error('mppx missing Mppx');
31
+ const createMethod = mppxMod.Mppx.create;
32
+ if (!isCallable(createMethod))
33
+ throw new Error('mppx Mppx.create is not a function');
34
+ const account = viemMod.privateKeyToAccount(tempoSigningKey);
35
+ const method = mppxMod.tempo({ account });
36
+ createMethod({ methods: [method] });
37
+ }
38
+ export { createModuleLoader, initMpp, isCallable, isRecord };
39
+ //# sourceMappingURL=mpp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mpp.js","sourceRoot":"","sources":["../src/mpp.ts"],"names":[],"mappings":"AAEA,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAC3C,MAAM,GAAG,GAAY,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,eAAuB,EAAE,UAAyB;IACvE,MAAM,IAAI,GAAG,UAAU,IAAI,kBAAkB,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAU,EAAE;QAC1D,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,GAAU,EAAE;QAC5D,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,mBAAmB,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAClG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACtE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAClE,MAAM,YAAY,GAAY,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;IAClD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACrF,MAAM,OAAO,GAAY,OAAO,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACtE,MAAM,MAAM,GAAY,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { FetchFunction, RequestFunction } from './types.js';
2
+ declare function buildAuthHeader(credential: string): Record<string, string>;
3
+ declare function buildFetchHeaders(credential: string, hasBody: boolean): Record<string, string>;
4
+ declare function buildFetchUrl(baseUrl: string, path: string, query?: Readonly<Record<string, string>>): string;
5
+ declare function isProhibitedRequest(method: string, path: string): boolean;
6
+ declare function createProxiedRequest(baseUrl: string, apiKey: string, fetchFunction?: FetchFunction): RequestFunction;
7
+ export { buildAuthHeader, buildFetchHeaders, buildFetchUrl, createProxiedRequest, isProhibitedRequest };
@@ -0,0 +1,88 @@
1
+ const FETCH_TIMEOUT_MS = 30_000;
2
+ const CONTENT_TYPE_HEADER = 'content-type';
3
+ const API_KEY_HEADER = 'x-api-key';
4
+ const AUTHORIZATION_HEADER = 'authorization';
5
+ const BEARER_PREFIX = 'Bearer ';
6
+ const API_KEY_PREFIX = 'xq_';
7
+ const API_V1_PREFIX = '/api/v1/';
8
+ const SUPPORT_TICKETS_PREFIX = '/api/v1/support/tickets';
9
+ function buildAuthHeader(credential) {
10
+ if (credential.startsWith(API_KEY_PREFIX)) {
11
+ return { [API_KEY_HEADER]: credential };
12
+ }
13
+ return { [AUTHORIZATION_HEADER]: `${BEARER_PREFIX}${credential}` };
14
+ }
15
+ function buildFetchHeaders(credential, hasBody) {
16
+ const auth = credential === '' ? {} : buildAuthHeader(credential);
17
+ if (hasBody) {
18
+ return { ...auth, [CONTENT_TYPE_HEADER]: 'application/json' };
19
+ }
20
+ return auth;
21
+ }
22
+ function buildFetchUrl(baseUrl, path, query) {
23
+ const url = new URL(path, baseUrl);
24
+ if (query !== undefined) {
25
+ for (const [key, value] of Object.entries(query)) {
26
+ url.searchParams.set(key, value);
27
+ }
28
+ }
29
+ return url.toString();
30
+ }
31
+ const PROHIBITED_PATHS = [
32
+ ['PATCH', '/api/v1/account'],
33
+ ['PUT', '/api/v1/account/x-identity'],
34
+ ['GET', '/api/v1/api-keys'],
35
+ ['POST', '/api/v1/api-keys'],
36
+ ['POST', '/api/v1/credits/topup'],
37
+ ['GET', '/api/v1/credits/topup/status'],
38
+ ['POST', '/api/v1/credits/quick-topup'],
39
+ ['POST', '/api/v1/subscribe'],
40
+ ['POST', '/api/v1/x/accounts'],
41
+ ['POST', '/api/v1/x/accounts/'],
42
+ ['POST', '/api/v1/x/accounts/bulk-retry'],
43
+ ];
44
+ const PROHIBITED_PATH_PATTERNS = [
45
+ ['DELETE', /^\/api\/v1\/api-keys\/[^/]+\/?$/u],
46
+ ['DELETE', /^\/api\/v1\/x\/accounts\/[^/]+\/?$/u],
47
+ ['GET', /^\/api\/v1\/x\/accounts\/[^/]+\/?$/u],
48
+ ['POST', /^\/api\/v1\/x\/accounts\/[^/]+\/reauth\/?$/u],
49
+ ];
50
+ const SUPPORT_TICKET_METHODS = new Set(['GET', 'PATCH', 'POST']);
51
+ function isSupportTicketPath(method, path) {
52
+ return SUPPORT_TICKET_METHODS.has(method)
53
+ && (path === SUPPORT_TICKETS_PREFIX || path.startsWith(`${SUPPORT_TICKETS_PREFIX}/`));
54
+ }
55
+ function isProhibitedRequest(method, path) {
56
+ const upperMethod = method.toUpperCase();
57
+ const matchesStaticPath = PROHIBITED_PATHS.some(([blockedMethod, blockedPath]) => upperMethod === blockedMethod && path === blockedPath);
58
+ const matchesPattern = PROHIBITED_PATH_PATTERNS.some(([blockedMethod, pattern]) => upperMethod === blockedMethod && pattern.test(path));
59
+ return matchesStaticPath || matchesPattern || isSupportTicketPath(upperMethod, path);
60
+ }
61
+ function validateRequestPath(method, path) {
62
+ if (!path.startsWith(API_V1_PREFIX)) {
63
+ throw new Error(`Path must start with /api/v1/ but got: ${path}`);
64
+ }
65
+ if (isProhibitedRequest(method, path)) {
66
+ throw new Error('Agent-prohibited endpoint. Account connection and re-authentication must be done through the Xquik dashboard at dashboard.xquik.com, not through the agent.');
67
+ }
68
+ }
69
+ function createProxiedRequest(baseUrl, apiKey, fetchFunction = fetch) {
70
+ return async (path, options) => {
71
+ const method = options?.method ?? 'GET';
72
+ validateRequestPath(method, path);
73
+ const hasBody = options?.body !== undefined;
74
+ const response = await fetchFunction(buildFetchUrl(baseUrl, path, options?.query), {
75
+ ...(hasBody ? { body: JSON.stringify(options.body) } : {}),
76
+ headers: buildFetchHeaders(apiKey, hasBody),
77
+ method,
78
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
79
+ });
80
+ const json = await response.json();
81
+ if (!response.ok) {
82
+ throw new Error(`API request failed: ${String(response.status)} ${response.statusText} - ${JSON.stringify(json)}`);
83
+ }
84
+ return json;
85
+ };
86
+ }
87
+ export { buildAuthHeader, buildFetchHeaders, buildFetchUrl, createProxiedRequest, isProhibitedRequest };
88
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAC3C,MAAM,cAAc,GAAG,WAAW,CAAC;AACnC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC;AAChC,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,aAAa,GAAG,UAAU,CAAC;AACjC,MAAM,sBAAsB,GAAG,yBAAyB,CAAC;AAEzD,SAAS,eAAe,CAAC,UAAkB;IACzC,IAAI,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC,cAAc,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1C,CAAC;IACD,OAAO,EAAE,CAAC,oBAAoB,CAAC,EAAE,GAAG,aAAa,GAAG,UAAU,EAAE,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAkB,EAAE,OAAgB;IAC7D,MAAM,IAAI,GAAG,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IAClE,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,OAAe,EAAE,IAAY,EAAE,KAAwC;IAC5F,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,gBAAgB,GAA6C;IACjE,CAAC,OAAO,EAAE,iBAAiB,CAAC;IAC5B,CAAC,KAAK,EAAE,4BAA4B,CAAC;IACrC,CAAC,KAAK,EAAE,kBAAkB,CAAC;IAC3B,CAAC,MAAM,EAAE,kBAAkB,CAAC;IAC5B,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACjC,CAAC,KAAK,EAAE,8BAA8B,CAAC;IACvC,CAAC,MAAM,EAAE,6BAA6B,CAAC;IACvC,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAC7B,CAAC,MAAM,EAAE,oBAAoB,CAAC;IAC9B,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAC/B,CAAC,MAAM,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,MAAM,wBAAwB,GAA6C;IACzE,CAAC,QAAQ,EAAE,kCAAkC,CAAC;IAC9C,CAAC,QAAQ,EAAE,qCAAqC,CAAC;IACjD,CAAC,KAAK,EAAE,qCAAqC,CAAC;IAC9C,CAAC,MAAM,EAAE,6CAA6C,CAAC;CACxD,CAAC;AAEF,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtF,SAAS,mBAAmB,CAAC,MAAc,EAAE,IAAY;IACvD,OAAO,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC;WACpC,CAAC,IAAI,KAAK,sBAAsB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,sBAAsB,GAAG,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAc,EAAE,IAAY;IACvD,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,IAAI,CAC7C,CAAC,CAAC,aAAa,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,WAAW,KAAK,aAAa,IAAI,IAAI,KAAK,WAAW,CACxF,CAAC;IACF,MAAM,cAAc,GAAG,wBAAwB,CAAC,IAAI,CAClD,CAAC,CAAC,aAAa,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,WAAW,KAAK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAClF,CAAC;IACF,OAAO,iBAAiB,IAAI,cAAc,IAAI,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAc,EAAE,IAAY;IACvD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,6JAA6J,CAC9J,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAC3B,OAAe,EACf,MAAc,EACd,gBAA+B,KAAK;IAEpC,OAAO,KAAK,EAAE,IAAY,EAAE,OAAkC,EAAoB,EAAE;QAClF,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC;QACxC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,OAAO,EAAE,IAAI,KAAK,SAAS,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE;YACjF,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,OAAO,EAAE,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC;YAC3C,MAAM;YACN,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC;SAC9C,CAAC,CAAC;QACH,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,uBAAuB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAClG,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { EventPollerOptions } from '../types.js';
2
+ interface EventPollerHandle {
3
+ readonly start: () => void;
4
+ readonly stop: () => void;
5
+ }
6
+ declare function createEventPoller(options: EventPollerOptions): EventPollerHandle;
7
+ export { createEventPoller };
@@ -0,0 +1,69 @@
1
+ const MAX_BACKOFF_SECONDS = 300;
2
+ const BACKOFF_BASE = 2;
3
+ const MS_PER_SECOND = 1000;
4
+ function isEventsResponse(value) {
5
+ return typeof value === 'object' && value !== null && 'events' in value;
6
+ }
7
+ function extractCursor(events) {
8
+ const lastEvent = events.at(-1);
9
+ return typeof lastEvent?.id === 'string' ? lastEvent.id : undefined;
10
+ }
11
+ function createEventPoller(options) {
12
+ let timer = undefined;
13
+ let cursor = undefined;
14
+ let consecutiveErrors = 0;
15
+ let stopped = false;
16
+ async function poll() {
17
+ try {
18
+ const query = {};
19
+ if (cursor !== undefined) {
20
+ query.after = cursor;
21
+ }
22
+ const hasQuery = Object.keys(query).length > 0;
23
+ const result = await options.request('/api/v1/events', hasQuery ? { query } : undefined);
24
+ if (!isEventsResponse(result)) {
25
+ return;
26
+ }
27
+ if (result.events.length > 0) {
28
+ options.onEvents(result.events);
29
+ const newCursor = extractCursor(result.events);
30
+ if (newCursor !== undefined) {
31
+ cursor = newCursor;
32
+ }
33
+ }
34
+ consecutiveErrors = 0;
35
+ }
36
+ catch {
37
+ consecutiveErrors += 1;
38
+ }
39
+ }
40
+ function getNextInterval() {
41
+ if (consecutiveErrors === 0) {
42
+ return options.intervalSeconds * MS_PER_SECOND;
43
+ }
44
+ const backoffSeconds = Math.min(Math.pow(BACKOFF_BASE, consecutiveErrors) * options.intervalSeconds, MAX_BACKOFF_SECONDS);
45
+ return backoffSeconds * MS_PER_SECOND;
46
+ }
47
+ async function loop() {
48
+ await poll();
49
+ if (stopped) {
50
+ return;
51
+ }
52
+ timer = setTimeout(() => { void loop(); }, getNextInterval());
53
+ }
54
+ return {
55
+ start() {
56
+ stopped = false;
57
+ timer = setTimeout(() => { void loop(); }, options.intervalSeconds * MS_PER_SECOND);
58
+ },
59
+ stop() {
60
+ stopped = true;
61
+ if (timer !== undefined) {
62
+ clearTimeout(timer);
63
+ timer = undefined;
64
+ }
65
+ },
66
+ };
67
+ }
68
+ export { createEventPoller };
69
+ //# sourceMappingURL=event-poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-poller.js","sourceRoot":"","sources":["../../src/services/event-poller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { EndpointInfo, ExploreParams, TweetclawParams } from '../types.js';
2
+ declare const specEndpoints: readonly EndpointInfo[];
3
+ declare function normalizeMethod(method?: string): string;
4
+ declare function matchesEndpointPath(endpointPath: string, requestPath: string): boolean;
5
+ declare function findEndpoint(method: string, path: string): EndpointInfo | undefined;
6
+ declare function requestNeedsApproval(method: string, path: string): boolean;
7
+ declare function resolveCatalogRequest(params: Readonly<TweetclawParams>, options?: Readonly<{
8
+ mppMode?: boolean;
9
+ }>): {
10
+ readonly body?: unknown;
11
+ readonly endpoint: EndpointInfo;
12
+ readonly method: string;
13
+ readonly path: string;
14
+ readonly query?: Readonly<Record<string, string>>;
15
+ };
16
+ declare function exploreCatalog(params?: Readonly<ExploreParams>): readonly EndpointInfo[];
17
+ export { exploreCatalog, findEndpoint, matchesEndpointPath, normalizeMethod, requestNeedsApproval, resolveCatalogRequest, specEndpoints, };
@@ -0,0 +1,110 @@
1
+ import { API_SPEC } from '../api-spec.js';
2
+ const API_V1_PREFIX = '/api/v1/';
3
+ const DEFAULT_EXPLORE_LIMIT = 25;
4
+ const MAX_EXPLORE_LIMIT = 100;
5
+ const specEndpoints = API_SPEC.filter((endpoint) => endpoint.agentProhibited !== true);
6
+ function normalizeMethod(method) {
7
+ return (method ?? 'GET').toUpperCase();
8
+ }
9
+ function normalizeLimit(limit) {
10
+ if (limit === undefined || !Number.isFinite(limit)) {
11
+ return DEFAULT_EXPLORE_LIMIT;
12
+ }
13
+ return Math.min(Math.max(Math.trunc(limit), 1), MAX_EXPLORE_LIMIT);
14
+ }
15
+ function pathSegments(path) {
16
+ const normalized = path.endsWith('/') ? path.slice(0, -1) : path;
17
+ return normalized.split('/');
18
+ }
19
+ function matchesEndpointPath(endpointPath, requestPath) {
20
+ if (endpointPath === requestPath)
21
+ return true;
22
+ const endpointSegments = pathSegments(endpointPath);
23
+ const requestSegments = pathSegments(requestPath);
24
+ if (endpointSegments.length !== requestSegments.length)
25
+ return false;
26
+ return endpointSegments.every((segment, index) => {
27
+ const requestSegment = String(requestSegments.at(index));
28
+ return segment.startsWith(':') ? requestSegment.length > 0 : segment === requestSegment;
29
+ });
30
+ }
31
+ function assertSafePath(path) {
32
+ if (!path.startsWith(API_V1_PREFIX)) {
33
+ throw new Error(`Path must start with /api/v1/ but got: ${path}`);
34
+ }
35
+ if (path.includes('?') || path.includes('#')) {
36
+ throw new Error('Pass query parameters through the query object, not in the path.');
37
+ }
38
+ }
39
+ function findEndpoint(method, path) {
40
+ return specEndpoints.find((endpoint) => endpoint.method === method && matchesEndpointPath(endpoint.path, path));
41
+ }
42
+ function normalizeQuery(query) {
43
+ if (query === undefined)
44
+ return undefined;
45
+ return Object.fromEntries(Object.entries(query).map(([key, value]) => [key, String(value)]));
46
+ }
47
+ function requestNeedsApproval(method, path) {
48
+ if (method !== 'GET') {
49
+ return true;
50
+ }
51
+ return path.startsWith('/api/v1/events')
52
+ || path.startsWith('/api/v1/webhooks')
53
+ || path === '/api/v1/x/accounts'
54
+ || path.startsWith('/api/v1/x/accounts/')
55
+ || path.startsWith('/api/v1/x/bookmarks')
56
+ || path.startsWith('/api/v1/x/dm/')
57
+ || path.startsWith('/api/v1/x/notifications')
58
+ || path.startsWith('/api/v1/x/timeline');
59
+ }
60
+ function resolveCatalogRequest(params, options) {
61
+ const method = normalizeMethod(params.method);
62
+ const { body, path } = params;
63
+ assertSafePath(path);
64
+ const endpoint = findEndpoint(method, path);
65
+ if (endpoint === undefined) {
66
+ throw new Error(`Endpoint is not in the TweetClaw catalog: ${method} ${path}`);
67
+ }
68
+ if (options?.mppMode === true && endpoint.mpp === undefined) {
69
+ throw new Error(`Endpoint is not available in MPP mode: ${method} ${endpoint.path}`);
70
+ }
71
+ const query = normalizeQuery(params.query);
72
+ if (query === undefined) {
73
+ return { body, endpoint, method, path };
74
+ }
75
+ return { body, endpoint, method, path, query };
76
+ }
77
+ function endpointMatchesQuery(endpoint, query) {
78
+ const normalized = query.toLowerCase();
79
+ const { category, method, parameters, path, responseShape, summary } = endpoint;
80
+ const haystack = [
81
+ category,
82
+ method,
83
+ path,
84
+ responseShape,
85
+ summary,
86
+ ...(parameters ?? []).flatMap((parameter) => [
87
+ parameter.description,
88
+ parameter.name,
89
+ parameter.type,
90
+ ]),
91
+ ].join(' ').toLowerCase();
92
+ return haystack.includes(normalized);
93
+ }
94
+ function exploreCatalog(params = {}) {
95
+ const method = params.method === undefined ? undefined : normalizeMethod(params.method);
96
+ const query = params.query?.trim();
97
+ const category = params.category?.trim().toLowerCase();
98
+ const path = params.path?.trim();
99
+ const limit = normalizeLimit(params.limit);
100
+ return specEndpoints
101
+ .filter((endpoint) => method === undefined || endpoint.method === method)
102
+ .filter((endpoint) => category === undefined || endpoint.category.toLowerCase() === category)
103
+ .filter((endpoint) => params.free === undefined || endpoint.free === params.free)
104
+ .filter((endpoint) => params.mpp === undefined || (endpoint.mpp !== undefined) === params.mpp)
105
+ .filter((endpoint) => path === undefined || matchesEndpointPath(endpoint.path, path) || endpoint.path.includes(path))
106
+ .filter((endpoint) => query === undefined || query.length === 0 || endpointMatchesQuery(endpoint, query))
107
+ .slice(0, limit);
108
+ }
109
+ export { exploreCatalog, findEndpoint, matchesEndpointPath, normalizeMethod, requestNeedsApproval, resolveCatalogRequest, specEndpoints, };
110
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/tools/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { EndpointInfo, ExploreParams, ToolResult } from '../types.js';
2
+ declare const SEARCH_DESCRIPTION: string;
3
+ declare function handleExplore(params?: Readonly<ExploreParams>): Promise<ToolResult>;
4
+ export { handleExplore, SEARCH_DESCRIPTION };
5
+ export type { EndpointInfo };
@@ -0,0 +1,28 @@
1
+ import { exploreCatalog, specEndpoints } from './catalog.js';
2
+ import { errorResult, successResult } from './result.js';
3
+ const categories = [...new Set(specEndpoints.map((endpoint) => endpoint.category))]
4
+ .toSorted((a, b) => a.localeCompare(b))
5
+ .join(', ');
6
+ const SEARCH_DESCRIPTION = `Search the X (Twitter) API endpoint catalog. No network calls and no code execution.
7
+
8
+ Use structured filters:
9
+ - query: keyword search across summaries, paths, response shapes, and parameters
10
+ - category: one of ${categories}
11
+ - method: GET, POST, PATCH, PUT, or DELETE
12
+ - path: exact or partial API path
13
+ - free: true for free endpoints, false for paid endpoints
14
+ - mpp: true for MPP-eligible endpoints only
15
+ - limit: 1-100 results, default 25
16
+
17
+ Returns endpoint descriptors with method, path, summary, category, parameters, cost, and response shape.`;
18
+ async function handleExplore(params = {}) {
19
+ try {
20
+ const result = await Promise.resolve(exploreCatalog(params));
21
+ return successResult(result);
22
+ }
23
+ catch (error) {
24
+ return errorResult(error);
25
+ }
26
+ }
27
+ export { handleExplore, SEARCH_DESCRIPTION };
28
+ //# sourceMappingURL=explore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explore.js","sourceRoot":"","sources":["../../src/tools/explore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;KAChF,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;KACtC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEd,MAAM,kBAAkB,GAAG;;;;qBAIN,UAAU;;;;;;;yGAO0E,CAAC;AAE1G,KAAK,UAAU,aAAa,CAAC,SAAkC,EAAE;IAC/D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ToolResult } from '../types.js';
2
+ declare function extractErrorMessage(error: unknown): string;
3
+ declare function successResult(content: unknown): ToolResult;
4
+ declare function errorResult(error: unknown): ToolResult;
5
+ export { errorResult, extractErrorMessage, successResult };
@@ -0,0 +1,15 @@
1
+ import { truncateResponse } from '../truncate.js';
2
+ function extractErrorMessage(error) {
3
+ if (error instanceof Error) {
4
+ return `${error.constructor.name}: ${error.message}`;
5
+ }
6
+ return String(error);
7
+ }
8
+ function successResult(content) {
9
+ return { content: [{ text: truncateResponse(content), type: 'text' }] };
10
+ }
11
+ function errorResult(error) {
12
+ return { content: [{ text: extractErrorMessage(error), type: 'text' }], isError: true };
13
+ }
14
+ export { errorResult, extractErrorMessage, successResult };
15
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,aAAa,CAAC,OAAgB;IACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1F,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { specEndpoints } from './catalog.js';
2
+ import type { FetchFunction, ToolResult, TweetclawParams } from '../types.js';
3
+ declare const EXECUTE_DESCRIPTION = "Invoke one Xquik API endpoint from the bundled TweetClaw catalog.\n\nUse \"explore\" first to find the endpoint, then call this tool with structured parameters:\n- path: concrete /api/v1/... path\n- method: GET, POST, PATCH, PUT, or DELETE\n- query: query parameters as an object\n- body: JSON request body\n\nAuth is injected automatically. Never pass API keys, signing keys, passwords, cookies, or TOTP secrets.\n\n## Important rules\n- Only endpoints listed in the bundled catalog can be invoked. Unknown paths are rejected.\n- The plugin only calls the configured Xquik API base URL and only /api/v1 paths.\n- Account connection, re-authentication, API-key administration, subscription checkout, credit top-up, and support-ticket actions are dashboard-only.\n- TWEET ACTIONS: SENDING a tweet (\"tweet this\", \"post this\") uses POST /api/v1/x/tweets. DRAFTING a tweet (\"help me write\", \"compose\") uses the compose flow.\n- WRITE ACTIONS: Show the exact endpoint and payload to the user before approval. All write-like calls trigger an OpenClaw approval prompt.\n- MPP MODE: When configured with a signing key and no API key, only MPP-eligible read endpoints are allowed.\n- CURRENT EVENTS: Use /api/v1/radar for curated trends.\n\n## Example: Send a tweet\n{\n \"path\": \"/api/v1/x/tweets\",\n \"method\": \"POST\",\n \"body\": { \"account\": \"@myaccount\", \"text\": \"Hello world!\" }\n}\n\n## Example: Search tweets\n{\n \"path\": \"/api/v1/x/tweets/search\",\n \"method\": \"GET\",\n \"query\": { \"q\": \"AI agents\", \"limit\": 50 }\n}";
4
+ interface TweetclawOptions {
5
+ readonly apiKey: string;
6
+ readonly baseUrl: string;
7
+ readonly fetchFunction?: FetchFunction | undefined;
8
+ readonly mppMode?: boolean | undefined;
9
+ readonly params: Readonly<TweetclawParams>;
10
+ readonly timeoutMs?: number | undefined;
11
+ }
12
+ declare function handleTweetclaw(options: Readonly<TweetclawOptions>): Promise<ToolResult>;
13
+ export { EXECUTE_DESCRIPTION, handleTweetclaw, specEndpoints };
@@ -0,0 +1,62 @@
1
+ import { createProxiedRequest } from '../request.js';
2
+ import { resolveCatalogRequest, specEndpoints } from './catalog.js';
3
+ import { errorResult, successResult } from './result.js';
4
+ const EXECUTE_DESCRIPTION = `Invoke one Xquik API endpoint from the bundled TweetClaw catalog.
5
+
6
+ Use "explore" first to find the endpoint, then call this tool with structured parameters:
7
+ - path: concrete /api/v1/... path
8
+ - method: GET, POST, PATCH, PUT, or DELETE
9
+ - query: query parameters as an object
10
+ - body: JSON request body
11
+
12
+ Auth is injected automatically. Never pass API keys, signing keys, passwords, cookies, or TOTP secrets.
13
+
14
+ ## Important rules
15
+ - Only endpoints listed in the bundled catalog can be invoked. Unknown paths are rejected.
16
+ - The plugin only calls the configured Xquik API base URL and only /api/v1 paths.
17
+ - Account connection, re-authentication, API-key administration, subscription checkout, credit top-up, and support-ticket actions are dashboard-only.
18
+ - TWEET ACTIONS: SENDING a tweet ("tweet this", "post this") uses POST /api/v1/x/tweets. DRAFTING a tweet ("help me write", "compose") uses the compose flow.
19
+ - WRITE ACTIONS: Show the exact endpoint and payload to the user before approval. All write-like calls trigger an OpenClaw approval prompt.
20
+ - MPP MODE: When configured with a signing key and no API key, only MPP-eligible read endpoints are allowed.
21
+ - CURRENT EVENTS: Use /api/v1/radar for curated trends.
22
+
23
+ ## Example: Send a tweet
24
+ {
25
+ "path": "/api/v1/x/tweets",
26
+ "method": "POST",
27
+ "body": { "account": "@myaccount", "text": "Hello world!" }
28
+ }
29
+
30
+ ## Example: Search tweets
31
+ {
32
+ "path": "/api/v1/x/tweets/search",
33
+ "method": "GET",
34
+ "query": { "q": "AI agents", "limit": 50 }
35
+ }`;
36
+ const EXECUTION_TIMEOUT_MS = 30_000;
37
+ const MS_PER_SECOND = 1000;
38
+ async function handleTweetclaw(options) {
39
+ const { apiKey, baseUrl, fetchFunction, mppMode = false, params, timeoutMs = EXECUTION_TIMEOUT_MS, } = options;
40
+ try {
41
+ const requestInfo = resolveCatalogRequest(params, { mppMode });
42
+ const request = createProxiedRequest(baseUrl, apiKey, fetchFunction);
43
+ const result = await Promise.race([
44
+ request(requestInfo.path, {
45
+ ...(requestInfo.body === undefined ? {} : { body: requestInfo.body }),
46
+ method: requestInfo.method,
47
+ ...(requestInfo.query === undefined ? {} : { query: requestInfo.query }),
48
+ }),
49
+ new Promise((_resolve, reject) => {
50
+ setTimeout(() => {
51
+ reject(new Error(`Execution timed out after ${String(timeoutMs / MS_PER_SECOND)}s`));
52
+ }, timeoutMs);
53
+ }),
54
+ ]);
55
+ return successResult(result);
56
+ }
57
+ catch (error) {
58
+ return errorResult(error);
59
+ }
60
+ }
61
+ export { EXECUTE_DESCRIPTION, handleTweetclaw, specEndpoints };
62
+ //# sourceMappingURL=tweetclaw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tweetclaw.js","sourceRoot":"","sources":["../../src/tools/tweetclaw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B1B,CAAC;AAEH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,aAAa,GAAG,IAAI,CAAC;AAW3B,KAAK,UAAU,eAAe,CAAC,OAAmC;IAChE,MAAM,EACJ,MAAM,EACN,OAAO,EACP,aAAa,EACb,OAAO,GAAG,KAAK,EACf,MAAM,EACN,SAAS,GAAG,oBAAoB,GACjC,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,qBAAqB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAoB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACtF,MAAM,MAAM,GAAY,MAAM,OAAO,CAAC,IAAI,CAAC;YACzC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE;gBACxB,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;gBACrE,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,GAAG,CAAC,WAAW,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC;aACzE,CAAC;YACF,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;gBACtC,UAAU,CAAC,GAAG,EAAE;oBACd,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,MAAM,CAAC,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvF,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare function truncateText(text: string): string;
2
+ declare function truncateResponse(content: unknown): string;
3
+ export { truncateResponse, truncateText };
@@ -0,0 +1,25 @@
1
+ const MAX_RESPONSE_CHARS = 24_000;
2
+ const CHARS_PER_TOKEN = 4;
3
+ const MAX_TOKENS = 6000;
4
+ function truncateText(text) {
5
+ if (text.length <= MAX_RESPONSE_CHARS) {
6
+ return text;
7
+ }
8
+ const approximateTokens = Math.ceil(text.length / CHARS_PER_TOKEN);
9
+ const formatted = approximateTokens.toLocaleString('en-US');
10
+ return `${text.slice(0, MAX_RESPONSE_CHARS)}\n\n--- TRUNCATED ---\nResponse was ~${formatted} tokens (limit: ${MAX_TOKENS.toLocaleString('en-US')}). Use more specific queries or filters to reduce response size.`;
11
+ }
12
+ function stringifyContent(content) {
13
+ if (typeof content === 'string') {
14
+ return content;
15
+ }
16
+ if (content === undefined) {
17
+ return 'undefined';
18
+ }
19
+ return JSON.stringify(content, undefined, 2);
20
+ }
21
+ function truncateResponse(content) {
22
+ return truncateText(stringifyContent(content));
23
+ }
24
+ export { truncateResponse, truncateText };
25
+ //# sourceMappingURL=truncate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncate.js","sourceRoot":"","sources":["../src/truncate.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,IAAI,CAAC,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,iBAAiB,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5D,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,wCAAwC,SAAS,mBAAmB,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,kEAAkE,CAAC;AACtN,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACxC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACxC,OAAO,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,64 @@
1
+ interface EndpointParameter {
2
+ readonly description: string;
3
+ readonly in: 'body' | 'path' | 'query';
4
+ readonly name: string;
5
+ readonly required: boolean;
6
+ readonly type: string;
7
+ }
8
+ interface EndpointInfo {
9
+ readonly agentProhibited?: true;
10
+ readonly category: string;
11
+ readonly free: boolean;
12
+ readonly method: string;
13
+ readonly mpp?: {
14
+ readonly intent: string;
15
+ readonly price: string;
16
+ };
17
+ readonly parameters?: readonly EndpointParameter[];
18
+ readonly path: string;
19
+ readonly responseShape?: string;
20
+ readonly sensitive?: true;
21
+ readonly summary: string;
22
+ }
23
+ interface RequestOptions {
24
+ readonly body?: unknown;
25
+ readonly method?: string;
26
+ readonly query?: Readonly<Record<string, string>>;
27
+ }
28
+ type RequestFunction = (path: string, options?: Readonly<RequestOptions>) => Promise<unknown>;
29
+ type FetchFunction = typeof fetch;
30
+ interface ExploreParams {
31
+ readonly category?: string;
32
+ readonly free?: boolean;
33
+ readonly limit?: number;
34
+ readonly method?: string;
35
+ readonly mpp?: boolean;
36
+ readonly path?: string;
37
+ readonly query?: string;
38
+ }
39
+ interface TweetclawParams {
40
+ readonly body?: unknown;
41
+ readonly method?: string;
42
+ readonly path: string;
43
+ readonly query?: Readonly<Record<string, boolean | number | string>>;
44
+ }
45
+ interface ToolResult {
46
+ readonly content: ReadonlyArray<{
47
+ readonly text: string;
48
+ readonly type: 'text';
49
+ }>;
50
+ readonly isError?: true;
51
+ }
52
+ interface PluginConfig {
53
+ readonly apiKey?: string;
54
+ readonly baseUrl?: string;
55
+ readonly pollingEnabled?: boolean;
56
+ readonly pollingInterval?: number;
57
+ readonly tempoSigningKey?: string;
58
+ }
59
+ interface EventPollerOptions {
60
+ readonly intervalSeconds: number;
61
+ readonly onEvents: (events: ReadonlyArray<Readonly<Record<string, unknown>>>) => void;
62
+ readonly request: RequestFunction;
63
+ }
64
+ export type { EndpointInfo, EndpointParameter, EventPollerOptions, ExploreParams, FetchFunction, PluginConfig, RequestFunction, RequestOptions, TweetclawParams, ToolResult, };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}