@xquik/tweetclaw 1.4.1 → 1.4.2
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/openclaw.plugin.json +2 -2
- package/package.json +10 -2
- package/src/mpp.ts +1 -1
- package/src/tools/executor.ts +125 -0
- package/src/tools/explore.ts +2 -3
- package/src/tools/tweetclaw.ts +2 -3
- package/src/tools/sandbox.ts +0 -58
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "tweetclaw",
|
|
3
3
|
"name": "TweetClaw",
|
|
4
|
-
"version": "1.4.
|
|
5
|
-
"description": "Post tweets, reply, like, retweet, follow, DM from your chat - full X/Twitter automation powered by Xquik. 120 endpoints, reads from $0.00015/call.",
|
|
4
|
+
"version": "1.4.1",
|
|
5
|
+
"description": "Post tweets, reply, like, retweet, follow, DM from your chat - full X/Twitter automation powered by Xquik. 120 endpoints, reads from $0.00015/call. Requires an Xquik API key or Tempo private key.",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xquik/tweetclaw",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "Post tweets, reply, like, retweet, follow, DM & more from OpenClaw - full X/Twitter automation via Xquik. 120 endpoints, reads from $0.00015/call.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,15 @@
|
|
|
18
18
|
"openclaw": {
|
|
19
19
|
"extensions": [
|
|
20
20
|
"./src/index.ts"
|
|
21
|
-
]
|
|
21
|
+
],
|
|
22
|
+
"compat": {
|
|
23
|
+
"pluginApi": ">=2026.3.28",
|
|
24
|
+
"minGatewayVersion": "2026.3.28"
|
|
25
|
+
},
|
|
26
|
+
"build": {
|
|
27
|
+
"openclawVersion": "2026.3.28",
|
|
28
|
+
"pluginSdkVersion": "2026.3.28"
|
|
29
|
+
}
|
|
22
30
|
},
|
|
23
31
|
"files": [
|
|
24
32
|
"src/",
|
package/src/mpp.ts
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import vm from 'node:vm';
|
|
2
|
+
import { API_SPEC } from '../api-spec.js';
|
|
3
|
+
import { truncateResponse } from '../truncate.js';
|
|
4
|
+
import type { ToolResult } from '../types.js';
|
|
5
|
+
|
|
6
|
+
const specEndpoints: ReadonlyArray<Readonly<Record<string, unknown>>> = API_SPEC.map(
|
|
7
|
+
(endpoint): Readonly<Record<string, unknown>> => ({ ...endpoint }),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
function extractErrorMessage(error: unknown): string {
|
|
11
|
+
if (error instanceof Error) {
|
|
12
|
+
return `${error.constructor.name}: ${error.message}`;
|
|
13
|
+
}
|
|
14
|
+
return String(error);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isAsyncFunctionConstructor(
|
|
18
|
+
value: unknown,
|
|
19
|
+
): value is new (...parameters: readonly string[]) => (...parameters: readonly unknown[]) => Promise<unknown> {
|
|
20
|
+
return typeof value === 'function';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getConstructorFromPrototype(proto: unknown): unknown {
|
|
24
|
+
if (typeof proto !== 'object' || proto === null) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return 'constructor' in proto ? proto.constructor : undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveAsyncFunctionConstructor(prototype?: unknown): new (
|
|
31
|
+
...parameters: readonly string[]
|
|
32
|
+
) => (...parameters: readonly unknown[]) => Promise<unknown> {
|
|
33
|
+
const asyncPrototype: unknown = prototype ?? Object.getPrototypeOf(async (): Promise<void> => {});
|
|
34
|
+
const candidate: unknown = getConstructorFromPrototype(asyncPrototype);
|
|
35
|
+
if (!isAsyncFunctionConstructor(candidate)) {
|
|
36
|
+
throw new Error('AsyncFunction constructor not found');
|
|
37
|
+
}
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const BLOCKED_PROPS: ReadonlySet<string | symbol> = new Set(['constructor', '__proto__', 'prototype']);
|
|
42
|
+
|
|
43
|
+
function isBlockedProperty(property: string | symbol): boolean {
|
|
44
|
+
return BLOCKED_PROPS.has(property);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function wrapValue(value: unknown): unknown {
|
|
48
|
+
if (value === null || value === undefined) return value;
|
|
49
|
+
if (typeof value !== 'object' && typeof value !== 'function') return value;
|
|
50
|
+
return createSafeProxy(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function wrapAsync(promise: Promise<unknown>): Promise<unknown> {
|
|
54
|
+
const resolved: unknown = await promise;
|
|
55
|
+
return wrapValue(resolved);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createCallableProxy(bound: (...a: readonly unknown[]) => unknown): unknown {
|
|
59
|
+
const handler: ProxyHandler<typeof bound> = {
|
|
60
|
+
apply(_target: typeof bound, _thisArgument: unknown, argumentsList: unknown[]): unknown {
|
|
61
|
+
const result: unknown = bound(...argumentsList);
|
|
62
|
+
if (result instanceof Promise) {
|
|
63
|
+
return wrapAsync(result);
|
|
64
|
+
}
|
|
65
|
+
return wrapValue(result);
|
|
66
|
+
},
|
|
67
|
+
get(_target: typeof bound, property: string | symbol): unknown {
|
|
68
|
+
if (isBlockedProperty(property)) return undefined;
|
|
69
|
+
return Reflect.get(_target, property);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
return new Proxy(bound, handler);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function createSafeProxy(target: unknown): unknown {
|
|
76
|
+
if (target === null || target === undefined) return target;
|
|
77
|
+
if (typeof target !== 'object' && typeof target !== 'function') return target;
|
|
78
|
+
|
|
79
|
+
const handler: ProxyHandler<Record<string | symbol, unknown>> = {
|
|
80
|
+
get(t: Record<string | symbol, unknown>, property: string | symbol): unknown {
|
|
81
|
+
if (isBlockedProperty(property)) return undefined;
|
|
82
|
+
const value: unknown = Reflect.get(t, property);
|
|
83
|
+
if (typeof value === 'function') {
|
|
84
|
+
const bound: (...a: readonly unknown[]) => unknown = value.bind(t);
|
|
85
|
+
return createCallableProxy(bound);
|
|
86
|
+
}
|
|
87
|
+
return wrapValue(value);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
return new Proxy(target as Record<string | symbol, unknown>, handler);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function runInSandbox(code: string, globals: Readonly<Record<string, unknown>>): unknown {
|
|
94
|
+
const rawContext: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
|
|
95
|
+
for (const key of Object.keys(globals)) {
|
|
96
|
+
const value: unknown = globals[key];
|
|
97
|
+
const safeValue: unknown = typeof value === 'object' && value !== null
|
|
98
|
+
? createSafeProxy(value)
|
|
99
|
+
: value;
|
|
100
|
+
Reflect.set(rawContext, key, safeValue);
|
|
101
|
+
}
|
|
102
|
+
const context: vm.Context = vm.createContext(rawContext);
|
|
103
|
+
return vm.runInNewContext(`(${code})()`, context);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function successResult(content: unknown): ToolResult {
|
|
107
|
+
return { content: [{ text: truncateResponse(content), type: 'text' as const }] };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function errorResult(error: unknown): ToolResult {
|
|
111
|
+
return { content: [{ text: extractErrorMessage(error), type: 'text' as const }], isError: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
BLOCKED_PROPS,
|
|
116
|
+
createSafeProxy,
|
|
117
|
+
errorResult,
|
|
118
|
+
extractErrorMessage,
|
|
119
|
+
getConstructorFromPrototype,
|
|
120
|
+
resolveAsyncFunctionConstructor,
|
|
121
|
+
runInSandbox,
|
|
122
|
+
specEndpoints,
|
|
123
|
+
successResult,
|
|
124
|
+
wrapValue,
|
|
125
|
+
};
|
package/src/tools/explore.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { API_SPEC } from '../api-spec.js';
|
|
2
|
-
import {
|
|
2
|
+
import { errorResult, runInSandbox, specEndpoints, successResult } from './executor.js';
|
|
3
3
|
import type { EndpointInfo, ToolResult } from '../types.js';
|
|
4
4
|
|
|
5
5
|
const categories = [...new Set(API_SPEC.map((endpoint) => endpoint.category))].toSorted((a, b) => a.localeCompare(b)).join(', ');
|
|
@@ -47,8 +47,7 @@ async () => {
|
|
|
47
47
|
|
|
48
48
|
async function handleExplore(code: string): Promise<ToolResult> {
|
|
49
49
|
try {
|
|
50
|
-
const
|
|
51
|
-
const result: unknown = await executor({ endpoints: specEndpoints });
|
|
50
|
+
const result: unknown = await runInSandbox(code, { spec: { endpoints: specEndpoints } });
|
|
52
51
|
return successResult(result);
|
|
53
52
|
} catch (error: unknown) {
|
|
54
53
|
return errorResult(error);
|
package/src/tools/tweetclaw.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createProxiedRequest } from '../request.js';
|
|
2
|
-
import {
|
|
2
|
+
import { errorResult, runInSandbox, specEndpoints, successResult } from './executor.js';
|
|
3
3
|
import type { FetchFunction, RequestFunction, ToolResult } from '../types.js';
|
|
4
4
|
|
|
5
5
|
const EXECUTE_DESCRIPTION = `Execute X (Twitter) API calls: post tweets, reply, like, retweet, follow, DM, update profile, upload media, search tweets, look up users, extract data, run giveaways, monitor accounts, compose tweets, and more. Write an async arrow function.
|
|
@@ -325,10 +325,9 @@ async function handleTweetclaw(options: Readonly<TweetclawOptions>): Promise<Too
|
|
|
325
325
|
const { apiKey, baseUrl, code, fetchFunction, timeoutMs = EXECUTION_TIMEOUT_MS } = options;
|
|
326
326
|
try {
|
|
327
327
|
const request: RequestFunction = createProxiedRequest(baseUrl, apiKey, fetchFunction);
|
|
328
|
-
const executor = new AsyncFunction('xquik', 'spec', `return (${code})()`);
|
|
329
328
|
|
|
330
329
|
const result: unknown = await Promise.race([
|
|
331
|
-
|
|
330
|
+
runInSandbox(code, { xquik: { request }, spec: { endpoints: specEndpoints } }),
|
|
332
331
|
new Promise<never>((_resolve, reject) => {
|
|
333
332
|
setTimeout(() => {
|
|
334
333
|
reject(new Error(`Execution timed out after ${String(timeoutMs / MS_PER_SECOND)}s`));
|
package/src/tools/sandbox.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { API_SPEC } from '../api-spec.js';
|
|
2
|
-
import { truncateResponse } from '../truncate.js';
|
|
3
|
-
import type { ToolResult } from '../types.js';
|
|
4
|
-
|
|
5
|
-
const specEndpoints: ReadonlyArray<Readonly<Record<string, unknown>>> = API_SPEC.map(
|
|
6
|
-
(endpoint): Readonly<Record<string, unknown>> => ({ ...endpoint }),
|
|
7
|
-
);
|
|
8
|
-
|
|
9
|
-
function extractErrorMessage(error: unknown): string {
|
|
10
|
-
if (error instanceof Error) {
|
|
11
|
-
return `${error.constructor.name}: ${error.message}`;
|
|
12
|
-
}
|
|
13
|
-
return String(error);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isAsyncFunctionConstructor(
|
|
17
|
-
value: unknown,
|
|
18
|
-
): value is new (...parameters: readonly string[]) => (...parameters: readonly unknown[]) => Promise<unknown> {
|
|
19
|
-
return typeof value === 'function';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getConstructorFromPrototype(proto: unknown): unknown {
|
|
23
|
-
if (typeof proto !== 'object' || proto === null) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
return 'constructor' in proto ? proto.constructor : undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function resolveAsyncFunctionConstructor(prototype?: unknown): new (
|
|
30
|
-
...parameters: readonly string[]
|
|
31
|
-
) => (...parameters: readonly unknown[]) => Promise<unknown> {
|
|
32
|
-
const asyncPrototype: unknown = prototype ?? Object.getPrototypeOf(async (): Promise<void> => {});
|
|
33
|
-
const candidate: unknown = getConstructorFromPrototype(asyncPrototype);
|
|
34
|
-
if (!isAsyncFunctionConstructor(candidate)) {
|
|
35
|
-
throw new Error('AsyncFunction constructor not found');
|
|
36
|
-
}
|
|
37
|
-
return candidate;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const AsyncFunction = resolveAsyncFunctionConstructor();
|
|
41
|
-
|
|
42
|
-
function successResult(content: unknown): ToolResult {
|
|
43
|
-
return { content: [{ text: truncateResponse(content), type: 'text' as const }] };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function errorResult(error: unknown): ToolResult {
|
|
47
|
-
return { content: [{ text: extractErrorMessage(error), type: 'text' as const }], isError: true };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export {
|
|
51
|
-
AsyncFunction,
|
|
52
|
-
errorResult,
|
|
53
|
-
extractErrorMessage,
|
|
54
|
-
getConstructorFromPrototype,
|
|
55
|
-
resolveAsyncFunctionConstructor,
|
|
56
|
-
specEndpoints,
|
|
57
|
-
successResult,
|
|
58
|
-
};
|