@phantom/phantom-openclaw-plugin 0.2.0 → 0.2.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/README.md +137 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +453 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +430 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ See [Prerequisites](#prerequisites) below for detailed setup instructions.
|
|
|
69
69
|
- **Automatic Authentication**: Handles OAuth flow and session management automatically
|
|
70
70
|
- **Type-Safe**: Full TypeScript support with proper type definitions
|
|
71
71
|
- **Simple Setup**: Minimal configuration - just enable the plugin and use
|
|
72
|
+
- **Perpetuals Trading**: Full Hyperliquid perps support — swap and deposit funds, manage positions, and trade perpetuals
|
|
72
73
|
|
|
73
74
|
## Prerequisites
|
|
74
75
|
|
|
@@ -354,6 +355,142 @@ Fetch a swap quote from Phantom's routing engine. Supports same-chain Solana, sa
|
|
|
354
355
|
|
|
355
356
|
**⚠️ Warning:** When `execute: true`, this tool submits transactions immediately and irreversibly.
|
|
356
357
|
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### Perpetuals Tools (Hyperliquid)
|
|
361
|
+
|
|
362
|
+
The plugin exposes 12 tools for perpetuals trading on Hyperliquid via Phantom's backend. All signing uses the wallet's EVM key (Arbitrum EIP-712).
|
|
363
|
+
|
|
364
|
+
#### Read-only
|
|
365
|
+
|
|
366
|
+
##### `get_perp_account`
|
|
367
|
+
|
|
368
|
+
Returns perp account balance: `accountValue`, `availableBalance`, `availableToTrade`.
|
|
369
|
+
|
|
370
|
+
**Parameters:** `walletId` (optional), `derivationIndex` (optional, default 0)
|
|
371
|
+
|
|
372
|
+
##### `get_perp_markets`
|
|
373
|
+
|
|
374
|
+
Returns all available perpetual markets with current price, funding rate, open interest, 24h volume, and max leverage.
|
|
375
|
+
|
|
376
|
+
**Parameters:** `walletId` (optional)
|
|
377
|
+
|
|
378
|
+
##### `get_perp_positions`
|
|
379
|
+
|
|
380
|
+
Returns all open positions with direction, size, entry price, leverage, unrealized PnL, and liquidation price.
|
|
381
|
+
|
|
382
|
+
**Parameters:** `walletId` (optional), `derivationIndex` (optional, default 0)
|
|
383
|
+
|
|
384
|
+
##### `get_perp_orders`
|
|
385
|
+
|
|
386
|
+
Returns all open orders (limit, take-profit, stop-loss) with order ID, type, price, size, and reduce-only flag.
|
|
387
|
+
|
|
388
|
+
**Parameters:** `walletId` (optional), `derivationIndex` (optional, default 0)
|
|
389
|
+
|
|
390
|
+
##### `get_perp_trade_history`
|
|
391
|
+
|
|
392
|
+
Returns historical trades with price, size, trade value, fee, and closed PnL.
|
|
393
|
+
|
|
394
|
+
**Parameters:** `walletId` (optional), `derivationIndex` (optional, default 0)
|
|
395
|
+
|
|
396
|
+
#### Write
|
|
397
|
+
|
|
398
|
+
##### `deposit_to_hyperliquid`
|
|
399
|
+
|
|
400
|
+
Swaps tokens to USDC via Phantom's routing engine and transfers the USDC into the Hyperliquid perp account.
|
|
401
|
+
|
|
402
|
+
**Parameters:**
|
|
403
|
+
|
|
404
|
+
- `sourceChainId` (string, required): Source chain — `"solana:mainnet"`, `"eip155:42161"`, `"eip155:8453"`, `"eip155:1"`, or `"eip155:137"`
|
|
405
|
+
- `amount` (string, required): Amount to deposit in human-readable units
|
|
406
|
+
- `tokenAddress` (string, optional): ERC-20/SPL token address — omit for native SOL or default USDC per chain
|
|
407
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
408
|
+
|
|
409
|
+
**⚠️ Warning:** Submits transactions immediately and irreversibly.
|
|
410
|
+
|
|
411
|
+
##### `open_perp_position`
|
|
412
|
+
|
|
413
|
+
Opens a perpetual position. Market orders use 10% slippage (IOC). Limit orders rest on the book (GTC).
|
|
414
|
+
|
|
415
|
+
**Parameters:**
|
|
416
|
+
|
|
417
|
+
- `market` (string, required): Market symbol (e.g. `"BTC"`, `"ETH"`, `"SOL"`)
|
|
418
|
+
- `direction` (string, required): `"long"` or `"short"`
|
|
419
|
+
- `sizeUsd` (string, required): Notional position size in USD (e.g. `"500"`)
|
|
420
|
+
- `leverage` (number, required): Leverage multiplier (e.g. `10` for 10x)
|
|
421
|
+
- `orderType` (string, required): `"market"` or `"limit"`
|
|
422
|
+
- `limitPrice` (string, optional): Required for limit orders
|
|
423
|
+
- `reduceOnly` (boolean, optional): Default false
|
|
424
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
425
|
+
|
|
426
|
+
**⚠️ Warning:** Submits transactions immediately and irreversibly.
|
|
427
|
+
|
|
428
|
+
##### `close_perp_position`
|
|
429
|
+
|
|
430
|
+
Closes an open position using a market IOC order. Defaults to 100% close.
|
|
431
|
+
|
|
432
|
+
**Parameters:**
|
|
433
|
+
|
|
434
|
+
- `market` (string, required): Market symbol (e.g. `"BTC"`)
|
|
435
|
+
- `sizePercent` (number, optional): Percentage to close (1–100, default 100)
|
|
436
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
437
|
+
|
|
438
|
+
**⚠️ Warning:** Submits transactions immediately and irreversibly.
|
|
439
|
+
|
|
440
|
+
##### `cancel_perp_order`
|
|
441
|
+
|
|
442
|
+
Cancels an open order by ID. Use `get_perp_orders` to retrieve order IDs.
|
|
443
|
+
|
|
444
|
+
**Parameters:**
|
|
445
|
+
|
|
446
|
+
- `market` (string, required): Market symbol
|
|
447
|
+
- `orderId` (number, required): Order ID from `get_perp_orders`
|
|
448
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
449
|
+
|
|
450
|
+
##### `update_perp_leverage`
|
|
451
|
+
|
|
452
|
+
Updates leverage and margin type for a market. Takes effect on new orders.
|
|
453
|
+
|
|
454
|
+
**Parameters:**
|
|
455
|
+
|
|
456
|
+
- `market` (string, required): Market symbol
|
|
457
|
+
- `leverage` (number, required): New leverage multiplier
|
|
458
|
+
- `marginType` (string, required): `"isolated"` or `"cross"`
|
|
459
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
460
|
+
|
|
461
|
+
##### `transfer_spot_to_perps`
|
|
462
|
+
|
|
463
|
+
Moves USDC **within Hypercore** from the spot account to the perp account. Use when USDC is already on Hyperliquid. Does not bridge from external chains.
|
|
464
|
+
|
|
465
|
+
**Parameters:**
|
|
466
|
+
|
|
467
|
+
- `amountUsdc` (string, required): Amount of USDC to transfer
|
|
468
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
469
|
+
|
|
470
|
+
##### `withdraw_from_perps`
|
|
471
|
+
|
|
472
|
+
Moves USDC from the perp account back to the Hyperliquid spot account.
|
|
473
|
+
|
|
474
|
+
**Parameters:**
|
|
475
|
+
|
|
476
|
+
- `amountUsdc` (string, required): Amount of USDC to withdraw
|
|
477
|
+
- `walletId` (string, optional), `derivationIndex` (number, optional, default 0)
|
|
478
|
+
|
|
479
|
+
#### Typical Agent Workflow
|
|
480
|
+
|
|
481
|
+
```text
|
|
482
|
+
1. get_perp_markets → find market, check price
|
|
483
|
+
2. get_token_balances → verify USDC balance on source chain
|
|
484
|
+
3. deposit_to_hyperliquid → swap to USDC and deposit to perp account
|
|
485
|
+
4. get_perp_account → confirm balance in perp account
|
|
486
|
+
5. open_perp_position → open long at 10x leverage
|
|
487
|
+
6. get_perp_positions → monitor position
|
|
488
|
+
7. close_perp_position → close when done
|
|
489
|
+
8. withdraw_from_perps → move USDC back to spot
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
357
494
|
## Network IDs Reference
|
|
358
495
|
|
|
359
496
|
Network identifiers follow the CAIP-2/CAIP-10 format. Here are the supported networks:
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Plugin API types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* OpenClaw Plugin API interface
|
|
6
|
+
*/
|
|
7
|
+
type OpenClawApi = {
|
|
8
|
+
/** OpenClaw config payload (can be full openclaw.json, not only plugin-scoped config) */
|
|
9
|
+
config?: Record<string, unknown>;
|
|
10
|
+
/** Register a tool with OpenClaw */
|
|
11
|
+
registerTool: (definition: {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
parameters: unknown;
|
|
15
|
+
execute: (id: string, params: Record<string, unknown>) => Promise<{
|
|
16
|
+
content: unknown;
|
|
17
|
+
isError?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
}) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Phantom OpenClaw Plugin
|
|
24
|
+
*
|
|
25
|
+
* Integrates Phantom wallet operations directly with OpenClaw agents
|
|
26
|
+
* by wrapping the Phantom MCP Server tools.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Plugin registration function
|
|
31
|
+
*/
|
|
32
|
+
declare function register(api: OpenClawApi): Promise<void>;
|
|
33
|
+
|
|
34
|
+
export { register as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
default: () => register
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/session.ts
|
|
28
|
+
var import_mcp_server = require("@phantom/mcp-server");
|
|
29
|
+
var PluginSession = class {
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.initialized = false;
|
|
32
|
+
this.initializingPromise = null;
|
|
33
|
+
this.sessionManager = new import_mcp_server.SessionManager({
|
|
34
|
+
appId: options.appId ?? "phantom-openclaw",
|
|
35
|
+
callbackPort: options.callbackPort,
|
|
36
|
+
sessionDir: options.sessionDir
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize the session (authenticate if needed)
|
|
41
|
+
* Thread-safe: concurrent calls will await the same initialization promise
|
|
42
|
+
*/
|
|
43
|
+
async initialize() {
|
|
44
|
+
if (this.initialized) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (this.initializingPromise) {
|
|
48
|
+
return this.initializingPromise;
|
|
49
|
+
}
|
|
50
|
+
this.initializingPromise = this.sessionManager.initialize().then(() => {
|
|
51
|
+
this.initialized = true;
|
|
52
|
+
}).catch((error) => {
|
|
53
|
+
this.initializingPromise = null;
|
|
54
|
+
throw error;
|
|
55
|
+
});
|
|
56
|
+
return this.initializingPromise;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the authenticated PhantomClient
|
|
60
|
+
*/
|
|
61
|
+
getClient() {
|
|
62
|
+
if (!this.initialized) {
|
|
63
|
+
throw new Error("Session not initialized. Call initialize() first.");
|
|
64
|
+
}
|
|
65
|
+
return this.sessionManager.getClient();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the current session data
|
|
69
|
+
*/
|
|
70
|
+
getSession() {
|
|
71
|
+
if (!this.initialized) {
|
|
72
|
+
throw new Error("Session not initialized. Call initialize() first.");
|
|
73
|
+
}
|
|
74
|
+
return this.sessionManager.getSession();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/tools/register-tools.ts
|
|
79
|
+
var import_typebox = require("@sinclair/typebox");
|
|
80
|
+
var import_mcp_server2 = require("@phantom/mcp-server");
|
|
81
|
+
var SCHEMA_OPTION_KEYS = ["description", "title", "default"];
|
|
82
|
+
var JSON_SCHEMA_TYPES = [
|
|
83
|
+
"string",
|
|
84
|
+
"number",
|
|
85
|
+
"integer",
|
|
86
|
+
"boolean",
|
|
87
|
+
"null",
|
|
88
|
+
"object",
|
|
89
|
+
"array"
|
|
90
|
+
];
|
|
91
|
+
function isRecord(value) {
|
|
92
|
+
return typeof value === "object" && value !== null;
|
|
93
|
+
}
|
|
94
|
+
function isJsonSchemaType(value) {
|
|
95
|
+
return typeof value === "string" && JSON_SCHEMA_TYPES.includes(value);
|
|
96
|
+
}
|
|
97
|
+
function pickOptions(schema, keys) {
|
|
98
|
+
const options = {};
|
|
99
|
+
for (const key of keys) {
|
|
100
|
+
const value = schema[key];
|
|
101
|
+
if (value !== void 0) {
|
|
102
|
+
options[key] = value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return options;
|
|
106
|
+
}
|
|
107
|
+
function isPrimitiveLiteral(value) {
|
|
108
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
|
|
109
|
+
}
|
|
110
|
+
function literalSchema(value, options = {}) {
|
|
111
|
+
if (value === null) {
|
|
112
|
+
return import_typebox.Type.Null(options);
|
|
113
|
+
}
|
|
114
|
+
return import_typebox.Type.Literal(value, options);
|
|
115
|
+
}
|
|
116
|
+
function toJsonSchema(value) {
|
|
117
|
+
if (!isRecord(value)) {
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
const schema = {};
|
|
121
|
+
if (isJsonSchemaType(value.type)) {
|
|
122
|
+
schema.type = value.type;
|
|
123
|
+
} else if (Array.isArray(value.type)) {
|
|
124
|
+
const types = value.type.filter(isJsonSchemaType);
|
|
125
|
+
if (types.length > 0) {
|
|
126
|
+
schema.type = types;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (typeof value.description === "string") {
|
|
130
|
+
schema.description = value.description;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value.title === "string") {
|
|
133
|
+
schema.title = value.title;
|
|
134
|
+
}
|
|
135
|
+
if ("default" in value) {
|
|
136
|
+
schema.default = value.default;
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(value.enum)) {
|
|
139
|
+
schema.enum = value.enum;
|
|
140
|
+
}
|
|
141
|
+
if ("const" in value) {
|
|
142
|
+
schema.const = value.const;
|
|
143
|
+
}
|
|
144
|
+
if (isRecord(value.properties)) {
|
|
145
|
+
schema.properties = Object.fromEntries(
|
|
146
|
+
Object.entries(value.properties).map(([key, prop]) => [key, toJsonSchema(prop)])
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (Array.isArray(value.required)) {
|
|
150
|
+
schema.required = value.required.filter((item) => typeof item === "string");
|
|
151
|
+
}
|
|
152
|
+
if (value.items !== void 0) {
|
|
153
|
+
schema.items = toJsonSchema(value.items);
|
|
154
|
+
}
|
|
155
|
+
if (typeof value.minimum === "number") {
|
|
156
|
+
schema.minimum = value.minimum;
|
|
157
|
+
}
|
|
158
|
+
if (typeof value.maximum === "number") {
|
|
159
|
+
schema.maximum = value.maximum;
|
|
160
|
+
}
|
|
161
|
+
if (typeof value.exclusiveMinimum === "number") {
|
|
162
|
+
schema.exclusiveMinimum = value.exclusiveMinimum;
|
|
163
|
+
}
|
|
164
|
+
if (typeof value.exclusiveMaximum === "number") {
|
|
165
|
+
schema.exclusiveMaximum = value.exclusiveMaximum;
|
|
166
|
+
}
|
|
167
|
+
if (typeof value.minLength === "number") {
|
|
168
|
+
schema.minLength = value.minLength;
|
|
169
|
+
}
|
|
170
|
+
if (typeof value.maxLength === "number") {
|
|
171
|
+
schema.maxLength = value.maxLength;
|
|
172
|
+
}
|
|
173
|
+
if (typeof value.pattern === "string") {
|
|
174
|
+
schema.pattern = value.pattern;
|
|
175
|
+
}
|
|
176
|
+
if (typeof value.format === "string") {
|
|
177
|
+
schema.format = value.format;
|
|
178
|
+
}
|
|
179
|
+
if (typeof value.minItems === "number") {
|
|
180
|
+
schema.minItems = value.minItems;
|
|
181
|
+
}
|
|
182
|
+
if (typeof value.maxItems === "number") {
|
|
183
|
+
schema.maxItems = value.maxItems;
|
|
184
|
+
}
|
|
185
|
+
if (typeof value.uniqueItems === "boolean") {
|
|
186
|
+
schema.uniqueItems = value.uniqueItems;
|
|
187
|
+
}
|
|
188
|
+
if (typeof value.minProperties === "number") {
|
|
189
|
+
schema.minProperties = value.minProperties;
|
|
190
|
+
}
|
|
191
|
+
if (typeof value.maxProperties === "number") {
|
|
192
|
+
schema.maxProperties = value.maxProperties;
|
|
193
|
+
}
|
|
194
|
+
if (typeof value.additionalProperties === "boolean") {
|
|
195
|
+
schema.additionalProperties = value.additionalProperties;
|
|
196
|
+
} else if (isRecord(value.additionalProperties)) {
|
|
197
|
+
schema.additionalProperties = toJsonSchema(value.additionalProperties);
|
|
198
|
+
}
|
|
199
|
+
if (Array.isArray(value.oneOf)) {
|
|
200
|
+
schema.oneOf = value.oneOf.map(toJsonSchema);
|
|
201
|
+
}
|
|
202
|
+
if (Array.isArray(value.anyOf)) {
|
|
203
|
+
schema.anyOf = value.anyOf.map(toJsonSchema);
|
|
204
|
+
}
|
|
205
|
+
if (Array.isArray(value.allOf)) {
|
|
206
|
+
schema.allOf = value.allOf.map(toJsonSchema);
|
|
207
|
+
}
|
|
208
|
+
return schema;
|
|
209
|
+
}
|
|
210
|
+
function convertEnum(schema) {
|
|
211
|
+
if (!schema.enum || schema.enum.length === 0) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
if (!schema.enum.every(isPrimitiveLiteral)) {
|
|
215
|
+
return import_typebox.Type.Unsafe(schema);
|
|
216
|
+
}
|
|
217
|
+
const baseOptions = pickOptions(schema, SCHEMA_OPTION_KEYS);
|
|
218
|
+
if (schema.enum.length === 1) {
|
|
219
|
+
return literalSchema(schema.enum[0], baseOptions);
|
|
220
|
+
}
|
|
221
|
+
return import_typebox.Type.Union(
|
|
222
|
+
schema.enum.map((value) => literalSchema(value)),
|
|
223
|
+
baseOptions
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
function convertObjectSchema(schema) {
|
|
227
|
+
const properties = schema.properties ?? {};
|
|
228
|
+
const required = new Set(schema.required ?? []);
|
|
229
|
+
const convertedProperties = Object.fromEntries(
|
|
230
|
+
Object.entries(properties).map(([key, value]) => {
|
|
231
|
+
const converted = convertSchemaNode(value);
|
|
232
|
+
return [key, required.has(key) ? converted : import_typebox.Type.Optional(converted)];
|
|
233
|
+
})
|
|
234
|
+
);
|
|
235
|
+
const options = pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minProperties", "maxProperties"]);
|
|
236
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
237
|
+
options.additionalProperties = schema.additionalProperties;
|
|
238
|
+
} else if (schema.additionalProperties) {
|
|
239
|
+
options.additionalProperties = convertSchemaNode(schema.additionalProperties);
|
|
240
|
+
}
|
|
241
|
+
return import_typebox.Type.Object(convertedProperties, options);
|
|
242
|
+
}
|
|
243
|
+
function convertArraySchema(schema) {
|
|
244
|
+
const itemSchema = schema.items ? convertSchemaNode(schema.items) : import_typebox.Type.Unknown();
|
|
245
|
+
return import_typebox.Type.Array(itemSchema, pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minItems", "maxItems", "uniqueItems"]));
|
|
246
|
+
}
|
|
247
|
+
function convertSingleTypeSchema(schema, type) {
|
|
248
|
+
switch (type) {
|
|
249
|
+
case "string":
|
|
250
|
+
return import_typebox.Type.String(pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minLength", "maxLength", "pattern", "format"]));
|
|
251
|
+
case "number":
|
|
252
|
+
return import_typebox.Type.Number(
|
|
253
|
+
pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"])
|
|
254
|
+
);
|
|
255
|
+
case "integer":
|
|
256
|
+
return import_typebox.Type.Integer(
|
|
257
|
+
pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"])
|
|
258
|
+
);
|
|
259
|
+
case "boolean":
|
|
260
|
+
return import_typebox.Type.Boolean(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
261
|
+
case "null":
|
|
262
|
+
return import_typebox.Type.Null(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
263
|
+
case "array":
|
|
264
|
+
return convertArraySchema(schema);
|
|
265
|
+
case "object":
|
|
266
|
+
return convertObjectSchema(schema);
|
|
267
|
+
default:
|
|
268
|
+
return import_typebox.Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function convertSchemaNode(schema) {
|
|
272
|
+
const enumSchema = convertEnum(schema);
|
|
273
|
+
if (enumSchema) {
|
|
274
|
+
return enumSchema;
|
|
275
|
+
}
|
|
276
|
+
if (schema.const !== void 0 && isPrimitiveLiteral(schema.const)) {
|
|
277
|
+
return literalSchema(schema.const, pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
278
|
+
}
|
|
279
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
280
|
+
return import_typebox.Type.Union(schema.oneOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
281
|
+
}
|
|
282
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
283
|
+
return import_typebox.Type.Union(schema.anyOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
284
|
+
}
|
|
285
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
286
|
+
return import_typebox.Type.Intersect(schema.allOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
287
|
+
}
|
|
288
|
+
if (schema.type === void 0) {
|
|
289
|
+
if (schema.properties) {
|
|
290
|
+
return convertObjectSchema({ ...schema, type: "object" });
|
|
291
|
+
}
|
|
292
|
+
if (schema.items) {
|
|
293
|
+
return convertArraySchema({ ...schema, type: "array" });
|
|
294
|
+
}
|
|
295
|
+
return import_typebox.Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
296
|
+
}
|
|
297
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
298
|
+
if (types.length === 1) {
|
|
299
|
+
return convertSingleTypeSchema(schema, types[0]);
|
|
300
|
+
}
|
|
301
|
+
return import_typebox.Type.Union(
|
|
302
|
+
types.map((type) => convertSingleTypeSchema(schema, type)),
|
|
303
|
+
pickOptions(schema, SCHEMA_OPTION_KEYS)
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
function convertSchema(mcpSchema) {
|
|
307
|
+
return convertSchemaNode(toJsonSchema(mcpSchema));
|
|
308
|
+
}
|
|
309
|
+
function registerPhantomTools(api, session) {
|
|
310
|
+
for (const mcpTool of import_mcp_server2.tools) {
|
|
311
|
+
api.registerTool({
|
|
312
|
+
name: mcpTool.name,
|
|
313
|
+
description: mcpTool.description,
|
|
314
|
+
parameters: convertSchema(mcpTool.inputSchema),
|
|
315
|
+
async execute(_id, params) {
|
|
316
|
+
const createLogger = (prefix) => ({
|
|
317
|
+
info: (msg) => console.info(`[${prefix}] ${msg}`),
|
|
318
|
+
// eslint-disable-line no-console
|
|
319
|
+
error: (msg) => console.error(`[${prefix}] ${msg}`),
|
|
320
|
+
// eslint-disable-line no-console
|
|
321
|
+
debug: (msg) => console.debug(`[${prefix}] ${msg}`),
|
|
322
|
+
// eslint-disable-line no-console
|
|
323
|
+
child: (name) => createLogger(`${prefix}:${name}`)
|
|
324
|
+
});
|
|
325
|
+
const context = {
|
|
326
|
+
client: session.getClient(),
|
|
327
|
+
session: session.getSession(),
|
|
328
|
+
logger: createLogger(mcpTool.name)
|
|
329
|
+
};
|
|
330
|
+
try {
|
|
331
|
+
const result = await mcpTool.handler(params, context);
|
|
332
|
+
const normalized = result ?? null;
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: typeof normalized === "string" ? normalized : JSON.stringify(normalized, null, 2)
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
};
|
|
341
|
+
} catch (error) {
|
|
342
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: JSON.stringify({ error: errorMessage }, null, 2)
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
isError: true
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/index.ts
|
|
359
|
+
var sessionInstance = null;
|
|
360
|
+
var PLUGIN_ID = "phantom-openclaw-plugin";
|
|
361
|
+
var STRING_CONFIG_KEYS = [
|
|
362
|
+
"PHANTOM_APP_ID",
|
|
363
|
+
"PHANTOM_CLIENT_ID",
|
|
364
|
+
"PHANTOM_CLIENT_SECRET",
|
|
365
|
+
"PHANTOM_AUTH_BASE_URL",
|
|
366
|
+
"PHANTOM_CONNECT_BASE_URL",
|
|
367
|
+
"PHANTOM_API_BASE_URL",
|
|
368
|
+
"PHANTOM_CALLBACK_PATH",
|
|
369
|
+
"PHANTOM_SSO_PROVIDER",
|
|
370
|
+
"PHANTOM_MCP_DEBUG"
|
|
371
|
+
];
|
|
372
|
+
function isRecord2(value) {
|
|
373
|
+
return typeof value === "object" && value !== null;
|
|
374
|
+
}
|
|
375
|
+
function getPluginConfig(fullConfig) {
|
|
376
|
+
if (!fullConfig) {
|
|
377
|
+
return void 0;
|
|
378
|
+
}
|
|
379
|
+
const plugins = fullConfig.plugins;
|
|
380
|
+
if (!isRecord2(plugins)) {
|
|
381
|
+
return fullConfig;
|
|
382
|
+
}
|
|
383
|
+
const entries = plugins.entries;
|
|
384
|
+
if (!isRecord2(entries)) {
|
|
385
|
+
return fullConfig;
|
|
386
|
+
}
|
|
387
|
+
const pluginEntry = entries[PLUGIN_ID];
|
|
388
|
+
if (!isRecord2(pluginEntry)) {
|
|
389
|
+
return fullConfig;
|
|
390
|
+
}
|
|
391
|
+
const pluginConfig = pluginEntry.config;
|
|
392
|
+
if (isRecord2(pluginConfig)) {
|
|
393
|
+
return pluginConfig;
|
|
394
|
+
}
|
|
395
|
+
return fullConfig;
|
|
396
|
+
}
|
|
397
|
+
function applyConfigToEnv(config) {
|
|
398
|
+
if (!config) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
for (const key of STRING_CONFIG_KEYS) {
|
|
402
|
+
const value = config[key];
|
|
403
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
404
|
+
process.env[key] = value.trim();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const rawPort = config.PHANTOM_CALLBACK_PORT;
|
|
408
|
+
let parsedPort = null;
|
|
409
|
+
if (typeof rawPort === "number") {
|
|
410
|
+
parsedPort = rawPort;
|
|
411
|
+
} else if (typeof rawPort === "string") {
|
|
412
|
+
const parsed = Number.parseInt(rawPort, 10);
|
|
413
|
+
parsedPort = Number.isNaN(parsed) ? null : parsed;
|
|
414
|
+
}
|
|
415
|
+
if (parsedPort !== null && Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {
|
|
416
|
+
process.env.PHANTOM_CALLBACK_PORT = String(parsedPort);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function getSession(config) {
|
|
420
|
+
if (!sessionInstance) {
|
|
421
|
+
const pluginConfig = getPluginConfig(config);
|
|
422
|
+
applyConfigToEnv(pluginConfig);
|
|
423
|
+
const appId = (process.env.PHANTOM_APP_ID ?? process.env.PHANTOM_CLIENT_ID)?.trim();
|
|
424
|
+
if (!appId) {
|
|
425
|
+
throw new Error(
|
|
426
|
+
'PHANTOM_APP_ID is required. Configure it in "~/.openclaw/openclaw.json" at plugins.entries["phantom-openclaw-plugin"].config.PHANTOM_APP_ID'
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const envPort = process.env.PHANTOM_CALLBACK_PORT?.trim();
|
|
430
|
+
const parsedPort = envPort ? Number.parseInt(envPort, 10) : NaN;
|
|
431
|
+
const callbackPort = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : void 0;
|
|
432
|
+
sessionInstance = new PluginSession({
|
|
433
|
+
appId,
|
|
434
|
+
callbackPort
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return sessionInstance;
|
|
438
|
+
}
|
|
439
|
+
function resetSession() {
|
|
440
|
+
sessionInstance = null;
|
|
441
|
+
}
|
|
442
|
+
async function register(api) {
|
|
443
|
+
try {
|
|
444
|
+
const session = getSession(api.config);
|
|
445
|
+
await session.initialize();
|
|
446
|
+
registerPhantomTools(api, session);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
console.error("Failed to initialize Phantom OpenClaw plugin:", error);
|
|
449
|
+
resetSession();
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/session.ts","../src/tools/register-tools.ts"],"sourcesContent":["/**\n * Phantom OpenClaw Plugin\n *\n * Integrates Phantom wallet operations directly with OpenClaw agents\n * by wrapping the Phantom MCP Server tools.\n */\n\nimport type { OpenClawApi } from \"./client/types.js\";\nimport { PluginSession } from \"./session.js\";\nimport { registerPhantomTools } from \"./tools/register-tools.js\";\n\n// Singleton session instance\nlet sessionInstance: PluginSession | null = null;\nconst PLUGIN_ID = \"phantom-openclaw-plugin\";\n\nconst STRING_CONFIG_KEYS = [\n \"PHANTOM_APP_ID\",\n \"PHANTOM_CLIENT_ID\",\n \"PHANTOM_CLIENT_SECRET\",\n \"PHANTOM_AUTH_BASE_URL\",\n \"PHANTOM_CONNECT_BASE_URL\",\n \"PHANTOM_API_BASE_URL\",\n \"PHANTOM_CALLBACK_PATH\",\n \"PHANTOM_SSO_PROVIDER\",\n \"PHANTOM_MCP_DEBUG\",\n] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * OpenClaw passes the full openclaw.json object as api.config.\n * Extract this plugin's scoped config when available.\n */\nfunction getPluginConfig(fullConfig?: Record<string, unknown>): Record<string, unknown> | undefined {\n if (!fullConfig) {\n return undefined;\n }\n\n const plugins = fullConfig.plugins;\n if (!isRecord(plugins)) {\n return fullConfig;\n }\n\n const entries = plugins.entries;\n if (!isRecord(entries)) {\n return fullConfig;\n }\n\n const pluginEntry = entries[PLUGIN_ID];\n if (!isRecord(pluginEntry)) {\n return fullConfig;\n }\n\n const pluginConfig = pluginEntry.config;\n if (isRecord(pluginConfig)) {\n return pluginConfig;\n }\n\n return fullConfig;\n}\n\nfunction applyConfigToEnv(config?: Record<string, unknown>): void {\n if (!config) {\n return;\n }\n\n for (const key of STRING_CONFIG_KEYS) {\n const value = config[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n process.env[key] = value.trim();\n }\n }\n\n const rawPort = config.PHANTOM_CALLBACK_PORT;\n let parsedPort: number | null = null;\n\n if (typeof rawPort === \"number\") {\n parsedPort = rawPort;\n } else if (typeof rawPort === \"string\") {\n const parsed = Number.parseInt(rawPort, 10);\n parsedPort = Number.isNaN(parsed) ? null : parsed;\n }\n\n if (parsedPort !== null && Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n process.env.PHANTOM_CALLBACK_PORT = String(parsedPort);\n }\n}\n\n/**\n * Get or create the plugin session with configuration\n */\nfunction getSession(config?: Record<string, unknown>): PluginSession {\n if (!sessionInstance) {\n const pluginConfig = getPluginConfig(config);\n applyConfigToEnv(pluginConfig);\n\n const appId = (process.env.PHANTOM_APP_ID ?? process.env.PHANTOM_CLIENT_ID)?.trim();\n if (!appId) {\n throw new Error(\n 'PHANTOM_APP_ID is required. Configure it in \"~/.openclaw/openclaw.json\" at plugins.entries[\"phantom-openclaw-plugin\"].config.PHANTOM_APP_ID',\n );\n }\n\n const envPort = process.env.PHANTOM_CALLBACK_PORT?.trim();\n const parsedPort = envPort ? Number.parseInt(envPort, 10) : NaN;\n const callbackPort = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : undefined;\n\n sessionInstance = new PluginSession({\n appId,\n callbackPort,\n });\n }\n return sessionInstance;\n}\n\n/**\n * Reset the session singleton (used for cleanup on initialization failure)\n */\nfunction resetSession(): void {\n sessionInstance = null;\n}\n\n/**\n * Plugin registration function\n */\nexport default async function register(api: OpenClawApi) {\n try {\n // Initialize session (authenticate if needed)\n const session = getSession(api.config);\n await session.initialize();\n\n // Register all Phantom MCP tools\n registerPhantomTools(api, session);\n } catch (error) {\n console.error(\"Failed to initialize Phantom OpenClaw plugin:\", error); // eslint-disable-line no-console\n // Reset singleton so next attempt gets a fresh instance\n resetSession();\n throw error;\n }\n}\n","/**\n * Session management for Phantom OpenClaw plugin\n * Wraps the SessionManager from @phantom/mcp-server\n */\n\nimport { SessionManager } from \"@phantom/mcp-server\";\nimport type { PhantomClient, SessionData } from \"@phantom/mcp-server\";\n\n/**\n * Configuration options for PluginSession\n */\nexport interface PluginSessionOptions {\n /** Application identifier from Phantom Portal */\n appId?: string;\n /** OAuth callback port (default: 8080) */\n callbackPort?: number;\n /** Directory to store session data (default: ~/.phantom-mcp) */\n sessionDir?: string;\n}\n\n/**\n * Plugin session manager\n * Handles authentication and provides access to PhantomClient\n */\nexport class PluginSession {\n private sessionManager: SessionManager;\n private initialized = false;\n private initializingPromise: Promise<void> | null = null;\n\n constructor(options: PluginSessionOptions = {}) {\n // Initialize SessionManager with configuration\n this.sessionManager = new SessionManager({\n appId: options.appId ?? \"phantom-openclaw\",\n callbackPort: options.callbackPort,\n sessionDir: options.sessionDir,\n });\n }\n\n /**\n * Initialize the session (authenticate if needed)\n * Thread-safe: concurrent calls will await the same initialization promise\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // If already initializing, return the existing promise\n if (this.initializingPromise) {\n return this.initializingPromise;\n }\n\n // Create and store the initialization promise\n this.initializingPromise = this.sessionManager\n .initialize()\n .then(() => {\n this.initialized = true;\n })\n .catch(error => {\n // Clear promise on error so subsequent calls can retry\n this.initializingPromise = null;\n throw error;\n });\n\n return this.initializingPromise;\n }\n\n /**\n * Get the authenticated PhantomClient\n */\n getClient(): PhantomClient {\n if (!this.initialized) {\n throw new Error(\"Session not initialized. Call initialize() first.\");\n }\n return this.sessionManager.getClient();\n }\n\n /**\n * Get the current session data\n */\n getSession(): SessionData {\n if (!this.initialized) {\n throw new Error(\"Session not initialized. Call initialize() first.\");\n }\n return this.sessionManager.getSession();\n }\n}\n","/**\n * Register Phantom MCP tools as OpenClaw tools\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport { tools } from \"@phantom/mcp-server\";\nimport type { OpenClawApi } from \"../client/types.js\";\nimport type { PluginSession } from \"../session.js\";\n\n/**\n * Convert MCP tool JSON schema to TypeBox schema\n */\ntype JsonSchemaType = \"string\" | \"number\" | \"integer\" | \"boolean\" | \"null\" | \"object\" | \"array\";\n\ntype JsonSchema = {\n type?: JsonSchemaType | JsonSchemaType[];\n description?: string;\n title?: string;\n default?: unknown;\n enum?: unknown[];\n const?: unknown;\n properties?: Record<string, JsonSchema>;\n required?: string[];\n items?: JsonSchema;\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n minItems?: number;\n maxItems?: number;\n uniqueItems?: boolean;\n minProperties?: number;\n maxProperties?: number;\n additionalProperties?: boolean | JsonSchema;\n oneOf?: JsonSchema[];\n anyOf?: JsonSchema[];\n allOf?: JsonSchema[];\n};\n\nconst SCHEMA_OPTION_KEYS = [\"description\", \"title\", \"default\"] as const;\nconst JSON_SCHEMA_TYPES: readonly JsonSchemaType[] = [\n \"string\",\n \"number\",\n \"integer\",\n \"boolean\",\n \"null\",\n \"object\",\n \"array\",\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isJsonSchemaType(value: unknown): value is JsonSchemaType {\n return typeof value === \"string\" && JSON_SCHEMA_TYPES.includes(value as JsonSchemaType);\n}\n\nfunction pickOptions(schema: JsonSchema, keys: readonly string[]): Record<string, unknown> {\n const options: Record<string, unknown> = {};\n for (const key of keys) {\n const value = schema[key as keyof JsonSchema];\n if (value !== undefined) {\n options[key] = value;\n }\n }\n return options;\n}\n\nfunction isPrimitiveLiteral(value: unknown): value is string | number | boolean | null {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\" || value === null;\n}\n\nfunction literalSchema(value: string | number | boolean | null, options: Record<string, unknown> = {}): TSchema {\n if (value === null) {\n return Type.Null(options);\n }\n return Type.Literal(value, options);\n}\n\nfunction toJsonSchema(value: unknown): JsonSchema {\n if (!isRecord(value)) {\n return {};\n }\n\n const schema: JsonSchema = {};\n\n if (isJsonSchemaType(value.type)) {\n schema.type = value.type;\n } else if (Array.isArray(value.type)) {\n const types = value.type.filter(isJsonSchemaType);\n if (types.length > 0) {\n schema.type = types;\n }\n }\n\n if (typeof value.description === \"string\") {\n schema.description = value.description;\n }\n if (typeof value.title === \"string\") {\n schema.title = value.title;\n }\n if (\"default\" in value) {\n schema.default = value.default;\n }\n if (Array.isArray(value.enum)) {\n schema.enum = value.enum;\n }\n if (\"const\" in value) {\n schema.const = value.const;\n }\n if (isRecord(value.properties)) {\n schema.properties = Object.fromEntries(\n Object.entries(value.properties).map(([key, prop]) => [key, toJsonSchema(prop)]),\n );\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter((item): item is string => typeof item === \"string\");\n }\n if (value.items !== undefined) {\n schema.items = toJsonSchema(value.items);\n }\n if (typeof value.minimum === \"number\") {\n schema.minimum = value.minimum;\n }\n if (typeof value.maximum === \"number\") {\n schema.maximum = value.maximum;\n }\n if (typeof value.exclusiveMinimum === \"number\") {\n schema.exclusiveMinimum = value.exclusiveMinimum;\n }\n if (typeof value.exclusiveMaximum === \"number\") {\n schema.exclusiveMaximum = value.exclusiveMaximum;\n }\n if (typeof value.minLength === \"number\") {\n schema.minLength = value.minLength;\n }\n if (typeof value.maxLength === \"number\") {\n schema.maxLength = value.maxLength;\n }\n if (typeof value.pattern === \"string\") {\n schema.pattern = value.pattern;\n }\n if (typeof value.format === \"string\") {\n schema.format = value.format;\n }\n if (typeof value.minItems === \"number\") {\n schema.minItems = value.minItems;\n }\n if (typeof value.maxItems === \"number\") {\n schema.maxItems = value.maxItems;\n }\n if (typeof value.uniqueItems === \"boolean\") {\n schema.uniqueItems = value.uniqueItems;\n }\n if (typeof value.minProperties === \"number\") {\n schema.minProperties = value.minProperties;\n }\n if (typeof value.maxProperties === \"number\") {\n schema.maxProperties = value.maxProperties;\n }\n if (typeof value.additionalProperties === \"boolean\") {\n schema.additionalProperties = value.additionalProperties;\n } else if (isRecord(value.additionalProperties)) {\n schema.additionalProperties = toJsonSchema(value.additionalProperties);\n }\n if (Array.isArray(value.oneOf)) {\n schema.oneOf = value.oneOf.map(toJsonSchema);\n }\n if (Array.isArray(value.anyOf)) {\n schema.anyOf = value.anyOf.map(toJsonSchema);\n }\n if (Array.isArray(value.allOf)) {\n schema.allOf = value.allOf.map(toJsonSchema);\n }\n\n return schema;\n}\n\nfunction convertEnum(schema: JsonSchema): TSchema | null {\n if (!schema.enum || schema.enum.length === 0) {\n return null;\n }\n\n if (!schema.enum.every(isPrimitiveLiteral)) {\n return Type.Unsafe(schema);\n }\n\n const baseOptions = pickOptions(schema, SCHEMA_OPTION_KEYS);\n if (schema.enum.length === 1) {\n return literalSchema(schema.enum[0], baseOptions);\n }\n\n return Type.Union(\n schema.enum.map(value => literalSchema(value)),\n baseOptions,\n );\n}\n\nfunction convertObjectSchema(schema: JsonSchema): TSchema {\n const properties = schema.properties ?? {};\n const required = new Set(schema.required ?? []);\n\n const convertedProperties = Object.fromEntries(\n Object.entries(properties).map(([key, value]) => {\n const converted = convertSchemaNode(value);\n return [key, required.has(key) ? converted : Type.Optional(converted)];\n }),\n );\n\n const options = pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minProperties\", \"maxProperties\"]);\n if (typeof schema.additionalProperties === \"boolean\") {\n options.additionalProperties = schema.additionalProperties;\n } else if (schema.additionalProperties) {\n options.additionalProperties = convertSchemaNode(schema.additionalProperties);\n }\n\n return Type.Object(convertedProperties, options);\n}\n\nfunction convertArraySchema(schema: JsonSchema): TSchema {\n const itemSchema = schema.items ? convertSchemaNode(schema.items) : Type.Unknown();\n return Type.Array(itemSchema, pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minItems\", \"maxItems\", \"uniqueItems\"]));\n}\n\nfunction convertSingleTypeSchema(schema: JsonSchema, type: JsonSchemaType): TSchema {\n switch (type) {\n case \"string\":\n return Type.String(pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minLength\", \"maxLength\", \"pattern\", \"format\"]));\n case \"number\":\n return Type.Number(\n pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minimum\", \"maximum\", \"exclusiveMinimum\", \"exclusiveMaximum\"]),\n );\n case \"integer\":\n return Type.Integer(\n pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minimum\", \"maximum\", \"exclusiveMinimum\", \"exclusiveMaximum\"]),\n );\n case \"boolean\":\n return Type.Boolean(pickOptions(schema, SCHEMA_OPTION_KEYS));\n case \"null\":\n return Type.Null(pickOptions(schema, SCHEMA_OPTION_KEYS));\n case \"array\":\n return convertArraySchema(schema);\n case \"object\":\n return convertObjectSchema(schema);\n default:\n return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n}\n\nfunction convertSchemaNode(schema: JsonSchema): TSchema {\n const enumSchema = convertEnum(schema);\n if (enumSchema) {\n return enumSchema;\n }\n\n if (schema.const !== undefined && isPrimitiveLiteral(schema.const)) {\n return literalSchema(schema.const, pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.oneOf && schema.oneOf.length > 0) {\n return Type.Union(schema.oneOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.anyOf && schema.anyOf.length > 0) {\n return Type.Union(schema.anyOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.allOf && schema.allOf.length > 0) {\n return Type.Intersect(schema.allOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.type === undefined) {\n if (schema.properties) {\n return convertObjectSchema({ ...schema, type: \"object\" });\n }\n if (schema.items) {\n return convertArraySchema({ ...schema, type: \"array\" });\n }\n return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n const types = Array.isArray(schema.type) ? schema.type : [schema.type];\n if (types.length === 1) {\n return convertSingleTypeSchema(schema, types[0]);\n }\n\n return Type.Union(\n types.map(type => convertSingleTypeSchema(schema, type)),\n pickOptions(schema, SCHEMA_OPTION_KEYS),\n );\n}\n\nfunction convertSchema(mcpSchema: unknown): TSchema {\n return convertSchemaNode(toJsonSchema(mcpSchema));\n}\n\n/**\n * Register all Phantom MCP tools with OpenClaw\n */\nexport function registerPhantomTools(api: OpenClawApi, session: PluginSession): void {\n for (const mcpTool of tools) {\n api.registerTool({\n name: mcpTool.name,\n description: mcpTool.description,\n parameters: convertSchema(mcpTool.inputSchema),\n async execute(_id: string, params: Record<string, unknown>) {\n // Create tool context for MCP tool with recursive logger\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const createLogger = (prefix: string): any => ({\n info: (msg: string) => console.info(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n error: (msg: string) => console.error(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n debug: (msg: string) => console.debug(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n child: (name: string) => createLogger(`${prefix}:${name}`),\n });\n\n const context = {\n client: session.getClient(),\n session: session.getSession(),\n logger: createLogger(mcpTool.name),\n };\n\n try {\n // Execute the MCP tool handler\n const result = await mcpTool.handler(params, context);\n\n // Return in OpenClaw format with defensive handling for undefined\n const normalized = result ?? null;\n return {\n content: [\n {\n type: \"text\" as const,\n text: typeof normalized === \"string\" ? normalized : JSON.stringify(normalized, null, 2),\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ error: errorMessage }, null, 2),\n },\n ],\n isError: true,\n };\n }\n },\n });\n }\n}\n"],"mappings":"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","names":["import_mcp_server","isRecord"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
// src/session.ts
|
|
2
|
+
import { SessionManager } from "@phantom/mcp-server";
|
|
3
|
+
var PluginSession = class {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.initialized = false;
|
|
6
|
+
this.initializingPromise = null;
|
|
7
|
+
this.sessionManager = new SessionManager({
|
|
8
|
+
appId: options.appId ?? "phantom-openclaw",
|
|
9
|
+
callbackPort: options.callbackPort,
|
|
10
|
+
sessionDir: options.sessionDir
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the session (authenticate if needed)
|
|
15
|
+
* Thread-safe: concurrent calls will await the same initialization promise
|
|
16
|
+
*/
|
|
17
|
+
async initialize() {
|
|
18
|
+
if (this.initialized) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (this.initializingPromise) {
|
|
22
|
+
return this.initializingPromise;
|
|
23
|
+
}
|
|
24
|
+
this.initializingPromise = this.sessionManager.initialize().then(() => {
|
|
25
|
+
this.initialized = true;
|
|
26
|
+
}).catch((error) => {
|
|
27
|
+
this.initializingPromise = null;
|
|
28
|
+
throw error;
|
|
29
|
+
});
|
|
30
|
+
return this.initializingPromise;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the authenticated PhantomClient
|
|
34
|
+
*/
|
|
35
|
+
getClient() {
|
|
36
|
+
if (!this.initialized) {
|
|
37
|
+
throw new Error("Session not initialized. Call initialize() first.");
|
|
38
|
+
}
|
|
39
|
+
return this.sessionManager.getClient();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the current session data
|
|
43
|
+
*/
|
|
44
|
+
getSession() {
|
|
45
|
+
if (!this.initialized) {
|
|
46
|
+
throw new Error("Session not initialized. Call initialize() first.");
|
|
47
|
+
}
|
|
48
|
+
return this.sessionManager.getSession();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/tools/register-tools.ts
|
|
53
|
+
import { Type } from "@sinclair/typebox";
|
|
54
|
+
import { tools } from "@phantom/mcp-server";
|
|
55
|
+
var SCHEMA_OPTION_KEYS = ["description", "title", "default"];
|
|
56
|
+
var JSON_SCHEMA_TYPES = [
|
|
57
|
+
"string",
|
|
58
|
+
"number",
|
|
59
|
+
"integer",
|
|
60
|
+
"boolean",
|
|
61
|
+
"null",
|
|
62
|
+
"object",
|
|
63
|
+
"array"
|
|
64
|
+
];
|
|
65
|
+
function isRecord(value) {
|
|
66
|
+
return typeof value === "object" && value !== null;
|
|
67
|
+
}
|
|
68
|
+
function isJsonSchemaType(value) {
|
|
69
|
+
return typeof value === "string" && JSON_SCHEMA_TYPES.includes(value);
|
|
70
|
+
}
|
|
71
|
+
function pickOptions(schema, keys) {
|
|
72
|
+
const options = {};
|
|
73
|
+
for (const key of keys) {
|
|
74
|
+
const value = schema[key];
|
|
75
|
+
if (value !== void 0) {
|
|
76
|
+
options[key] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return options;
|
|
80
|
+
}
|
|
81
|
+
function isPrimitiveLiteral(value) {
|
|
82
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
|
|
83
|
+
}
|
|
84
|
+
function literalSchema(value, options = {}) {
|
|
85
|
+
if (value === null) {
|
|
86
|
+
return Type.Null(options);
|
|
87
|
+
}
|
|
88
|
+
return Type.Literal(value, options);
|
|
89
|
+
}
|
|
90
|
+
function toJsonSchema(value) {
|
|
91
|
+
if (!isRecord(value)) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
const schema = {};
|
|
95
|
+
if (isJsonSchemaType(value.type)) {
|
|
96
|
+
schema.type = value.type;
|
|
97
|
+
} else if (Array.isArray(value.type)) {
|
|
98
|
+
const types = value.type.filter(isJsonSchemaType);
|
|
99
|
+
if (types.length > 0) {
|
|
100
|
+
schema.type = types;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (typeof value.description === "string") {
|
|
104
|
+
schema.description = value.description;
|
|
105
|
+
}
|
|
106
|
+
if (typeof value.title === "string") {
|
|
107
|
+
schema.title = value.title;
|
|
108
|
+
}
|
|
109
|
+
if ("default" in value) {
|
|
110
|
+
schema.default = value.default;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(value.enum)) {
|
|
113
|
+
schema.enum = value.enum;
|
|
114
|
+
}
|
|
115
|
+
if ("const" in value) {
|
|
116
|
+
schema.const = value.const;
|
|
117
|
+
}
|
|
118
|
+
if (isRecord(value.properties)) {
|
|
119
|
+
schema.properties = Object.fromEntries(
|
|
120
|
+
Object.entries(value.properties).map(([key, prop]) => [key, toJsonSchema(prop)])
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(value.required)) {
|
|
124
|
+
schema.required = value.required.filter((item) => typeof item === "string");
|
|
125
|
+
}
|
|
126
|
+
if (value.items !== void 0) {
|
|
127
|
+
schema.items = toJsonSchema(value.items);
|
|
128
|
+
}
|
|
129
|
+
if (typeof value.minimum === "number") {
|
|
130
|
+
schema.minimum = value.minimum;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value.maximum === "number") {
|
|
133
|
+
schema.maximum = value.maximum;
|
|
134
|
+
}
|
|
135
|
+
if (typeof value.exclusiveMinimum === "number") {
|
|
136
|
+
schema.exclusiveMinimum = value.exclusiveMinimum;
|
|
137
|
+
}
|
|
138
|
+
if (typeof value.exclusiveMaximum === "number") {
|
|
139
|
+
schema.exclusiveMaximum = value.exclusiveMaximum;
|
|
140
|
+
}
|
|
141
|
+
if (typeof value.minLength === "number") {
|
|
142
|
+
schema.minLength = value.minLength;
|
|
143
|
+
}
|
|
144
|
+
if (typeof value.maxLength === "number") {
|
|
145
|
+
schema.maxLength = value.maxLength;
|
|
146
|
+
}
|
|
147
|
+
if (typeof value.pattern === "string") {
|
|
148
|
+
schema.pattern = value.pattern;
|
|
149
|
+
}
|
|
150
|
+
if (typeof value.format === "string") {
|
|
151
|
+
schema.format = value.format;
|
|
152
|
+
}
|
|
153
|
+
if (typeof value.minItems === "number") {
|
|
154
|
+
schema.minItems = value.minItems;
|
|
155
|
+
}
|
|
156
|
+
if (typeof value.maxItems === "number") {
|
|
157
|
+
schema.maxItems = value.maxItems;
|
|
158
|
+
}
|
|
159
|
+
if (typeof value.uniqueItems === "boolean") {
|
|
160
|
+
schema.uniqueItems = value.uniqueItems;
|
|
161
|
+
}
|
|
162
|
+
if (typeof value.minProperties === "number") {
|
|
163
|
+
schema.minProperties = value.minProperties;
|
|
164
|
+
}
|
|
165
|
+
if (typeof value.maxProperties === "number") {
|
|
166
|
+
schema.maxProperties = value.maxProperties;
|
|
167
|
+
}
|
|
168
|
+
if (typeof value.additionalProperties === "boolean") {
|
|
169
|
+
schema.additionalProperties = value.additionalProperties;
|
|
170
|
+
} else if (isRecord(value.additionalProperties)) {
|
|
171
|
+
schema.additionalProperties = toJsonSchema(value.additionalProperties);
|
|
172
|
+
}
|
|
173
|
+
if (Array.isArray(value.oneOf)) {
|
|
174
|
+
schema.oneOf = value.oneOf.map(toJsonSchema);
|
|
175
|
+
}
|
|
176
|
+
if (Array.isArray(value.anyOf)) {
|
|
177
|
+
schema.anyOf = value.anyOf.map(toJsonSchema);
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(value.allOf)) {
|
|
180
|
+
schema.allOf = value.allOf.map(toJsonSchema);
|
|
181
|
+
}
|
|
182
|
+
return schema;
|
|
183
|
+
}
|
|
184
|
+
function convertEnum(schema) {
|
|
185
|
+
if (!schema.enum || schema.enum.length === 0) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
if (!schema.enum.every(isPrimitiveLiteral)) {
|
|
189
|
+
return Type.Unsafe(schema);
|
|
190
|
+
}
|
|
191
|
+
const baseOptions = pickOptions(schema, SCHEMA_OPTION_KEYS);
|
|
192
|
+
if (schema.enum.length === 1) {
|
|
193
|
+
return literalSchema(schema.enum[0], baseOptions);
|
|
194
|
+
}
|
|
195
|
+
return Type.Union(
|
|
196
|
+
schema.enum.map((value) => literalSchema(value)),
|
|
197
|
+
baseOptions
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
function convertObjectSchema(schema) {
|
|
201
|
+
const properties = schema.properties ?? {};
|
|
202
|
+
const required = new Set(schema.required ?? []);
|
|
203
|
+
const convertedProperties = Object.fromEntries(
|
|
204
|
+
Object.entries(properties).map(([key, value]) => {
|
|
205
|
+
const converted = convertSchemaNode(value);
|
|
206
|
+
return [key, required.has(key) ? converted : Type.Optional(converted)];
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
const options = pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minProperties", "maxProperties"]);
|
|
210
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
211
|
+
options.additionalProperties = schema.additionalProperties;
|
|
212
|
+
} else if (schema.additionalProperties) {
|
|
213
|
+
options.additionalProperties = convertSchemaNode(schema.additionalProperties);
|
|
214
|
+
}
|
|
215
|
+
return Type.Object(convertedProperties, options);
|
|
216
|
+
}
|
|
217
|
+
function convertArraySchema(schema) {
|
|
218
|
+
const itemSchema = schema.items ? convertSchemaNode(schema.items) : Type.Unknown();
|
|
219
|
+
return Type.Array(itemSchema, pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minItems", "maxItems", "uniqueItems"]));
|
|
220
|
+
}
|
|
221
|
+
function convertSingleTypeSchema(schema, type) {
|
|
222
|
+
switch (type) {
|
|
223
|
+
case "string":
|
|
224
|
+
return Type.String(pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minLength", "maxLength", "pattern", "format"]));
|
|
225
|
+
case "number":
|
|
226
|
+
return Type.Number(
|
|
227
|
+
pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"])
|
|
228
|
+
);
|
|
229
|
+
case "integer":
|
|
230
|
+
return Type.Integer(
|
|
231
|
+
pickOptions(schema, [...SCHEMA_OPTION_KEYS, "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"])
|
|
232
|
+
);
|
|
233
|
+
case "boolean":
|
|
234
|
+
return Type.Boolean(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
235
|
+
case "null":
|
|
236
|
+
return Type.Null(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
237
|
+
case "array":
|
|
238
|
+
return convertArraySchema(schema);
|
|
239
|
+
case "object":
|
|
240
|
+
return convertObjectSchema(schema);
|
|
241
|
+
default:
|
|
242
|
+
return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function convertSchemaNode(schema) {
|
|
246
|
+
const enumSchema = convertEnum(schema);
|
|
247
|
+
if (enumSchema) {
|
|
248
|
+
return enumSchema;
|
|
249
|
+
}
|
|
250
|
+
if (schema.const !== void 0 && isPrimitiveLiteral(schema.const)) {
|
|
251
|
+
return literalSchema(schema.const, pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
252
|
+
}
|
|
253
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
254
|
+
return Type.Union(schema.oneOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
255
|
+
}
|
|
256
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
257
|
+
return Type.Union(schema.anyOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
258
|
+
}
|
|
259
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
260
|
+
return Type.Intersect(schema.allOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
261
|
+
}
|
|
262
|
+
if (schema.type === void 0) {
|
|
263
|
+
if (schema.properties) {
|
|
264
|
+
return convertObjectSchema({ ...schema, type: "object" });
|
|
265
|
+
}
|
|
266
|
+
if (schema.items) {
|
|
267
|
+
return convertArraySchema({ ...schema, type: "array" });
|
|
268
|
+
}
|
|
269
|
+
return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));
|
|
270
|
+
}
|
|
271
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
272
|
+
if (types.length === 1) {
|
|
273
|
+
return convertSingleTypeSchema(schema, types[0]);
|
|
274
|
+
}
|
|
275
|
+
return Type.Union(
|
|
276
|
+
types.map((type) => convertSingleTypeSchema(schema, type)),
|
|
277
|
+
pickOptions(schema, SCHEMA_OPTION_KEYS)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
function convertSchema(mcpSchema) {
|
|
281
|
+
return convertSchemaNode(toJsonSchema(mcpSchema));
|
|
282
|
+
}
|
|
283
|
+
function registerPhantomTools(api, session) {
|
|
284
|
+
for (const mcpTool of tools) {
|
|
285
|
+
api.registerTool({
|
|
286
|
+
name: mcpTool.name,
|
|
287
|
+
description: mcpTool.description,
|
|
288
|
+
parameters: convertSchema(mcpTool.inputSchema),
|
|
289
|
+
async execute(_id, params) {
|
|
290
|
+
const createLogger = (prefix) => ({
|
|
291
|
+
info: (msg) => console.info(`[${prefix}] ${msg}`),
|
|
292
|
+
// eslint-disable-line no-console
|
|
293
|
+
error: (msg) => console.error(`[${prefix}] ${msg}`),
|
|
294
|
+
// eslint-disable-line no-console
|
|
295
|
+
debug: (msg) => console.debug(`[${prefix}] ${msg}`),
|
|
296
|
+
// eslint-disable-line no-console
|
|
297
|
+
child: (name) => createLogger(`${prefix}:${name}`)
|
|
298
|
+
});
|
|
299
|
+
const context = {
|
|
300
|
+
client: session.getClient(),
|
|
301
|
+
session: session.getSession(),
|
|
302
|
+
logger: createLogger(mcpTool.name)
|
|
303
|
+
};
|
|
304
|
+
try {
|
|
305
|
+
const result = await mcpTool.handler(params, context);
|
|
306
|
+
const normalized = result ?? null;
|
|
307
|
+
return {
|
|
308
|
+
content: [
|
|
309
|
+
{
|
|
310
|
+
type: "text",
|
|
311
|
+
text: typeof normalized === "string" ? normalized : JSON.stringify(normalized, null, 2)
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
};
|
|
315
|
+
} catch (error) {
|
|
316
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
317
|
+
return {
|
|
318
|
+
content: [
|
|
319
|
+
{
|
|
320
|
+
type: "text",
|
|
321
|
+
text: JSON.stringify({ error: errorMessage }, null, 2)
|
|
322
|
+
}
|
|
323
|
+
],
|
|
324
|
+
isError: true
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/index.ts
|
|
333
|
+
var sessionInstance = null;
|
|
334
|
+
var PLUGIN_ID = "phantom-openclaw-plugin";
|
|
335
|
+
var STRING_CONFIG_KEYS = [
|
|
336
|
+
"PHANTOM_APP_ID",
|
|
337
|
+
"PHANTOM_CLIENT_ID",
|
|
338
|
+
"PHANTOM_CLIENT_SECRET",
|
|
339
|
+
"PHANTOM_AUTH_BASE_URL",
|
|
340
|
+
"PHANTOM_CONNECT_BASE_URL",
|
|
341
|
+
"PHANTOM_API_BASE_URL",
|
|
342
|
+
"PHANTOM_CALLBACK_PATH",
|
|
343
|
+
"PHANTOM_SSO_PROVIDER",
|
|
344
|
+
"PHANTOM_MCP_DEBUG"
|
|
345
|
+
];
|
|
346
|
+
function isRecord2(value) {
|
|
347
|
+
return typeof value === "object" && value !== null;
|
|
348
|
+
}
|
|
349
|
+
function getPluginConfig(fullConfig) {
|
|
350
|
+
if (!fullConfig) {
|
|
351
|
+
return void 0;
|
|
352
|
+
}
|
|
353
|
+
const plugins = fullConfig.plugins;
|
|
354
|
+
if (!isRecord2(plugins)) {
|
|
355
|
+
return fullConfig;
|
|
356
|
+
}
|
|
357
|
+
const entries = plugins.entries;
|
|
358
|
+
if (!isRecord2(entries)) {
|
|
359
|
+
return fullConfig;
|
|
360
|
+
}
|
|
361
|
+
const pluginEntry = entries[PLUGIN_ID];
|
|
362
|
+
if (!isRecord2(pluginEntry)) {
|
|
363
|
+
return fullConfig;
|
|
364
|
+
}
|
|
365
|
+
const pluginConfig = pluginEntry.config;
|
|
366
|
+
if (isRecord2(pluginConfig)) {
|
|
367
|
+
return pluginConfig;
|
|
368
|
+
}
|
|
369
|
+
return fullConfig;
|
|
370
|
+
}
|
|
371
|
+
function applyConfigToEnv(config) {
|
|
372
|
+
if (!config) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
for (const key of STRING_CONFIG_KEYS) {
|
|
376
|
+
const value = config[key];
|
|
377
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
378
|
+
process.env[key] = value.trim();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const rawPort = config.PHANTOM_CALLBACK_PORT;
|
|
382
|
+
let parsedPort = null;
|
|
383
|
+
if (typeof rawPort === "number") {
|
|
384
|
+
parsedPort = rawPort;
|
|
385
|
+
} else if (typeof rawPort === "string") {
|
|
386
|
+
const parsed = Number.parseInt(rawPort, 10);
|
|
387
|
+
parsedPort = Number.isNaN(parsed) ? null : parsed;
|
|
388
|
+
}
|
|
389
|
+
if (parsedPort !== null && Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {
|
|
390
|
+
process.env.PHANTOM_CALLBACK_PORT = String(parsedPort);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function getSession(config) {
|
|
394
|
+
if (!sessionInstance) {
|
|
395
|
+
const pluginConfig = getPluginConfig(config);
|
|
396
|
+
applyConfigToEnv(pluginConfig);
|
|
397
|
+
const appId = (process.env.PHANTOM_APP_ID ?? process.env.PHANTOM_CLIENT_ID)?.trim();
|
|
398
|
+
if (!appId) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
'PHANTOM_APP_ID is required. Configure it in "~/.openclaw/openclaw.json" at plugins.entries["phantom-openclaw-plugin"].config.PHANTOM_APP_ID'
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
const envPort = process.env.PHANTOM_CALLBACK_PORT?.trim();
|
|
404
|
+
const parsedPort = envPort ? Number.parseInt(envPort, 10) : NaN;
|
|
405
|
+
const callbackPort = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : void 0;
|
|
406
|
+
sessionInstance = new PluginSession({
|
|
407
|
+
appId,
|
|
408
|
+
callbackPort
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return sessionInstance;
|
|
412
|
+
}
|
|
413
|
+
function resetSession() {
|
|
414
|
+
sessionInstance = null;
|
|
415
|
+
}
|
|
416
|
+
async function register(api) {
|
|
417
|
+
try {
|
|
418
|
+
const session = getSession(api.config);
|
|
419
|
+
await session.initialize();
|
|
420
|
+
registerPhantomTools(api, session);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error("Failed to initialize Phantom OpenClaw plugin:", error);
|
|
423
|
+
resetSession();
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
export {
|
|
428
|
+
register as default
|
|
429
|
+
};
|
|
430
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session.ts","../src/tools/register-tools.ts","../src/index.ts"],"sourcesContent":["/**\n * Session management for Phantom OpenClaw plugin\n * Wraps the SessionManager from @phantom/mcp-server\n */\n\nimport { SessionManager } from \"@phantom/mcp-server\";\nimport type { PhantomClient, SessionData } from \"@phantom/mcp-server\";\n\n/**\n * Configuration options for PluginSession\n */\nexport interface PluginSessionOptions {\n /** Application identifier from Phantom Portal */\n appId?: string;\n /** OAuth callback port (default: 8080) */\n callbackPort?: number;\n /** Directory to store session data (default: ~/.phantom-mcp) */\n sessionDir?: string;\n}\n\n/**\n * Plugin session manager\n * Handles authentication and provides access to PhantomClient\n */\nexport class PluginSession {\n private sessionManager: SessionManager;\n private initialized = false;\n private initializingPromise: Promise<void> | null = null;\n\n constructor(options: PluginSessionOptions = {}) {\n // Initialize SessionManager with configuration\n this.sessionManager = new SessionManager({\n appId: options.appId ?? \"phantom-openclaw\",\n callbackPort: options.callbackPort,\n sessionDir: options.sessionDir,\n });\n }\n\n /**\n * Initialize the session (authenticate if needed)\n * Thread-safe: concurrent calls will await the same initialization promise\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n // If already initializing, return the existing promise\n if (this.initializingPromise) {\n return this.initializingPromise;\n }\n\n // Create and store the initialization promise\n this.initializingPromise = this.sessionManager\n .initialize()\n .then(() => {\n this.initialized = true;\n })\n .catch(error => {\n // Clear promise on error so subsequent calls can retry\n this.initializingPromise = null;\n throw error;\n });\n\n return this.initializingPromise;\n }\n\n /**\n * Get the authenticated PhantomClient\n */\n getClient(): PhantomClient {\n if (!this.initialized) {\n throw new Error(\"Session not initialized. Call initialize() first.\");\n }\n return this.sessionManager.getClient();\n }\n\n /**\n * Get the current session data\n */\n getSession(): SessionData {\n if (!this.initialized) {\n throw new Error(\"Session not initialized. Call initialize() first.\");\n }\n return this.sessionManager.getSession();\n }\n}\n","/**\n * Register Phantom MCP tools as OpenClaw tools\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport { tools } from \"@phantom/mcp-server\";\nimport type { OpenClawApi } from \"../client/types.js\";\nimport type { PluginSession } from \"../session.js\";\n\n/**\n * Convert MCP tool JSON schema to TypeBox schema\n */\ntype JsonSchemaType = \"string\" | \"number\" | \"integer\" | \"boolean\" | \"null\" | \"object\" | \"array\";\n\ntype JsonSchema = {\n type?: JsonSchemaType | JsonSchemaType[];\n description?: string;\n title?: string;\n default?: unknown;\n enum?: unknown[];\n const?: unknown;\n properties?: Record<string, JsonSchema>;\n required?: string[];\n items?: JsonSchema;\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n minItems?: number;\n maxItems?: number;\n uniqueItems?: boolean;\n minProperties?: number;\n maxProperties?: number;\n additionalProperties?: boolean | JsonSchema;\n oneOf?: JsonSchema[];\n anyOf?: JsonSchema[];\n allOf?: JsonSchema[];\n};\n\nconst SCHEMA_OPTION_KEYS = [\"description\", \"title\", \"default\"] as const;\nconst JSON_SCHEMA_TYPES: readonly JsonSchemaType[] = [\n \"string\",\n \"number\",\n \"integer\",\n \"boolean\",\n \"null\",\n \"object\",\n \"array\",\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isJsonSchemaType(value: unknown): value is JsonSchemaType {\n return typeof value === \"string\" && JSON_SCHEMA_TYPES.includes(value as JsonSchemaType);\n}\n\nfunction pickOptions(schema: JsonSchema, keys: readonly string[]): Record<string, unknown> {\n const options: Record<string, unknown> = {};\n for (const key of keys) {\n const value = schema[key as keyof JsonSchema];\n if (value !== undefined) {\n options[key] = value;\n }\n }\n return options;\n}\n\nfunction isPrimitiveLiteral(value: unknown): value is string | number | boolean | null {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\" || value === null;\n}\n\nfunction literalSchema(value: string | number | boolean | null, options: Record<string, unknown> = {}): TSchema {\n if (value === null) {\n return Type.Null(options);\n }\n return Type.Literal(value, options);\n}\n\nfunction toJsonSchema(value: unknown): JsonSchema {\n if (!isRecord(value)) {\n return {};\n }\n\n const schema: JsonSchema = {};\n\n if (isJsonSchemaType(value.type)) {\n schema.type = value.type;\n } else if (Array.isArray(value.type)) {\n const types = value.type.filter(isJsonSchemaType);\n if (types.length > 0) {\n schema.type = types;\n }\n }\n\n if (typeof value.description === \"string\") {\n schema.description = value.description;\n }\n if (typeof value.title === \"string\") {\n schema.title = value.title;\n }\n if (\"default\" in value) {\n schema.default = value.default;\n }\n if (Array.isArray(value.enum)) {\n schema.enum = value.enum;\n }\n if (\"const\" in value) {\n schema.const = value.const;\n }\n if (isRecord(value.properties)) {\n schema.properties = Object.fromEntries(\n Object.entries(value.properties).map(([key, prop]) => [key, toJsonSchema(prop)]),\n );\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter((item): item is string => typeof item === \"string\");\n }\n if (value.items !== undefined) {\n schema.items = toJsonSchema(value.items);\n }\n if (typeof value.minimum === \"number\") {\n schema.minimum = value.minimum;\n }\n if (typeof value.maximum === \"number\") {\n schema.maximum = value.maximum;\n }\n if (typeof value.exclusiveMinimum === \"number\") {\n schema.exclusiveMinimum = value.exclusiveMinimum;\n }\n if (typeof value.exclusiveMaximum === \"number\") {\n schema.exclusiveMaximum = value.exclusiveMaximum;\n }\n if (typeof value.minLength === \"number\") {\n schema.minLength = value.minLength;\n }\n if (typeof value.maxLength === \"number\") {\n schema.maxLength = value.maxLength;\n }\n if (typeof value.pattern === \"string\") {\n schema.pattern = value.pattern;\n }\n if (typeof value.format === \"string\") {\n schema.format = value.format;\n }\n if (typeof value.minItems === \"number\") {\n schema.minItems = value.minItems;\n }\n if (typeof value.maxItems === \"number\") {\n schema.maxItems = value.maxItems;\n }\n if (typeof value.uniqueItems === \"boolean\") {\n schema.uniqueItems = value.uniqueItems;\n }\n if (typeof value.minProperties === \"number\") {\n schema.minProperties = value.minProperties;\n }\n if (typeof value.maxProperties === \"number\") {\n schema.maxProperties = value.maxProperties;\n }\n if (typeof value.additionalProperties === \"boolean\") {\n schema.additionalProperties = value.additionalProperties;\n } else if (isRecord(value.additionalProperties)) {\n schema.additionalProperties = toJsonSchema(value.additionalProperties);\n }\n if (Array.isArray(value.oneOf)) {\n schema.oneOf = value.oneOf.map(toJsonSchema);\n }\n if (Array.isArray(value.anyOf)) {\n schema.anyOf = value.anyOf.map(toJsonSchema);\n }\n if (Array.isArray(value.allOf)) {\n schema.allOf = value.allOf.map(toJsonSchema);\n }\n\n return schema;\n}\n\nfunction convertEnum(schema: JsonSchema): TSchema | null {\n if (!schema.enum || schema.enum.length === 0) {\n return null;\n }\n\n if (!schema.enum.every(isPrimitiveLiteral)) {\n return Type.Unsafe(schema);\n }\n\n const baseOptions = pickOptions(schema, SCHEMA_OPTION_KEYS);\n if (schema.enum.length === 1) {\n return literalSchema(schema.enum[0], baseOptions);\n }\n\n return Type.Union(\n schema.enum.map(value => literalSchema(value)),\n baseOptions,\n );\n}\n\nfunction convertObjectSchema(schema: JsonSchema): TSchema {\n const properties = schema.properties ?? {};\n const required = new Set(schema.required ?? []);\n\n const convertedProperties = Object.fromEntries(\n Object.entries(properties).map(([key, value]) => {\n const converted = convertSchemaNode(value);\n return [key, required.has(key) ? converted : Type.Optional(converted)];\n }),\n );\n\n const options = pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minProperties\", \"maxProperties\"]);\n if (typeof schema.additionalProperties === \"boolean\") {\n options.additionalProperties = schema.additionalProperties;\n } else if (schema.additionalProperties) {\n options.additionalProperties = convertSchemaNode(schema.additionalProperties);\n }\n\n return Type.Object(convertedProperties, options);\n}\n\nfunction convertArraySchema(schema: JsonSchema): TSchema {\n const itemSchema = schema.items ? convertSchemaNode(schema.items) : Type.Unknown();\n return Type.Array(itemSchema, pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minItems\", \"maxItems\", \"uniqueItems\"]));\n}\n\nfunction convertSingleTypeSchema(schema: JsonSchema, type: JsonSchemaType): TSchema {\n switch (type) {\n case \"string\":\n return Type.String(pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minLength\", \"maxLength\", \"pattern\", \"format\"]));\n case \"number\":\n return Type.Number(\n pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minimum\", \"maximum\", \"exclusiveMinimum\", \"exclusiveMaximum\"]),\n );\n case \"integer\":\n return Type.Integer(\n pickOptions(schema, [...SCHEMA_OPTION_KEYS, \"minimum\", \"maximum\", \"exclusiveMinimum\", \"exclusiveMaximum\"]),\n );\n case \"boolean\":\n return Type.Boolean(pickOptions(schema, SCHEMA_OPTION_KEYS));\n case \"null\":\n return Type.Null(pickOptions(schema, SCHEMA_OPTION_KEYS));\n case \"array\":\n return convertArraySchema(schema);\n case \"object\":\n return convertObjectSchema(schema);\n default:\n return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n}\n\nfunction convertSchemaNode(schema: JsonSchema): TSchema {\n const enumSchema = convertEnum(schema);\n if (enumSchema) {\n return enumSchema;\n }\n\n if (schema.const !== undefined && isPrimitiveLiteral(schema.const)) {\n return literalSchema(schema.const, pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.oneOf && schema.oneOf.length > 0) {\n return Type.Union(schema.oneOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.anyOf && schema.anyOf.length > 0) {\n return Type.Union(schema.anyOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.allOf && schema.allOf.length > 0) {\n return Type.Intersect(schema.allOf.map(convertSchemaNode), pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n if (schema.type === undefined) {\n if (schema.properties) {\n return convertObjectSchema({ ...schema, type: \"object\" });\n }\n if (schema.items) {\n return convertArraySchema({ ...schema, type: \"array\" });\n }\n return Type.Unknown(pickOptions(schema, SCHEMA_OPTION_KEYS));\n }\n\n const types = Array.isArray(schema.type) ? schema.type : [schema.type];\n if (types.length === 1) {\n return convertSingleTypeSchema(schema, types[0]);\n }\n\n return Type.Union(\n types.map(type => convertSingleTypeSchema(schema, type)),\n pickOptions(schema, SCHEMA_OPTION_KEYS),\n );\n}\n\nfunction convertSchema(mcpSchema: unknown): TSchema {\n return convertSchemaNode(toJsonSchema(mcpSchema));\n}\n\n/**\n * Register all Phantom MCP tools with OpenClaw\n */\nexport function registerPhantomTools(api: OpenClawApi, session: PluginSession): void {\n for (const mcpTool of tools) {\n api.registerTool({\n name: mcpTool.name,\n description: mcpTool.description,\n parameters: convertSchema(mcpTool.inputSchema),\n async execute(_id: string, params: Record<string, unknown>) {\n // Create tool context for MCP tool with recursive logger\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const createLogger = (prefix: string): any => ({\n info: (msg: string) => console.info(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n error: (msg: string) => console.error(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n debug: (msg: string) => console.debug(`[${prefix}] ${msg}`), // eslint-disable-line no-console\n child: (name: string) => createLogger(`${prefix}:${name}`),\n });\n\n const context = {\n client: session.getClient(),\n session: session.getSession(),\n logger: createLogger(mcpTool.name),\n };\n\n try {\n // Execute the MCP tool handler\n const result = await mcpTool.handler(params, context);\n\n // Return in OpenClaw format with defensive handling for undefined\n const normalized = result ?? null;\n return {\n content: [\n {\n type: \"text\" as const,\n text: typeof normalized === \"string\" ? normalized : JSON.stringify(normalized, null, 2),\n },\n ],\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ error: errorMessage }, null, 2),\n },\n ],\n isError: true,\n };\n }\n },\n });\n }\n}\n","/**\n * Phantom OpenClaw Plugin\n *\n * Integrates Phantom wallet operations directly with OpenClaw agents\n * by wrapping the Phantom MCP Server tools.\n */\n\nimport type { OpenClawApi } from \"./client/types.js\";\nimport { PluginSession } from \"./session.js\";\nimport { registerPhantomTools } from \"./tools/register-tools.js\";\n\n// Singleton session instance\nlet sessionInstance: PluginSession | null = null;\nconst PLUGIN_ID = \"phantom-openclaw-plugin\";\n\nconst STRING_CONFIG_KEYS = [\n \"PHANTOM_APP_ID\",\n \"PHANTOM_CLIENT_ID\",\n \"PHANTOM_CLIENT_SECRET\",\n \"PHANTOM_AUTH_BASE_URL\",\n \"PHANTOM_CONNECT_BASE_URL\",\n \"PHANTOM_API_BASE_URL\",\n \"PHANTOM_CALLBACK_PATH\",\n \"PHANTOM_SSO_PROVIDER\",\n \"PHANTOM_MCP_DEBUG\",\n] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * OpenClaw passes the full openclaw.json object as api.config.\n * Extract this plugin's scoped config when available.\n */\nfunction getPluginConfig(fullConfig?: Record<string, unknown>): Record<string, unknown> | undefined {\n if (!fullConfig) {\n return undefined;\n }\n\n const plugins = fullConfig.plugins;\n if (!isRecord(plugins)) {\n return fullConfig;\n }\n\n const entries = plugins.entries;\n if (!isRecord(entries)) {\n return fullConfig;\n }\n\n const pluginEntry = entries[PLUGIN_ID];\n if (!isRecord(pluginEntry)) {\n return fullConfig;\n }\n\n const pluginConfig = pluginEntry.config;\n if (isRecord(pluginConfig)) {\n return pluginConfig;\n }\n\n return fullConfig;\n}\n\nfunction applyConfigToEnv(config?: Record<string, unknown>): void {\n if (!config) {\n return;\n }\n\n for (const key of STRING_CONFIG_KEYS) {\n const value = config[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n process.env[key] = value.trim();\n }\n }\n\n const rawPort = config.PHANTOM_CALLBACK_PORT;\n let parsedPort: number | null = null;\n\n if (typeof rawPort === \"number\") {\n parsedPort = rawPort;\n } else if (typeof rawPort === \"string\") {\n const parsed = Number.parseInt(rawPort, 10);\n parsedPort = Number.isNaN(parsed) ? null : parsed;\n }\n\n if (parsedPort !== null && Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n process.env.PHANTOM_CALLBACK_PORT = String(parsedPort);\n }\n}\n\n/**\n * Get or create the plugin session with configuration\n */\nfunction getSession(config?: Record<string, unknown>): PluginSession {\n if (!sessionInstance) {\n const pluginConfig = getPluginConfig(config);\n applyConfigToEnv(pluginConfig);\n\n const appId = (process.env.PHANTOM_APP_ID ?? process.env.PHANTOM_CLIENT_ID)?.trim();\n if (!appId) {\n throw new Error(\n 'PHANTOM_APP_ID is required. Configure it in \"~/.openclaw/openclaw.json\" at plugins.entries[\"phantom-openclaw-plugin\"].config.PHANTOM_APP_ID',\n );\n }\n\n const envPort = process.env.PHANTOM_CALLBACK_PORT?.trim();\n const parsedPort = envPort ? Number.parseInt(envPort, 10) : NaN;\n const callbackPort = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : undefined;\n\n sessionInstance = new PluginSession({\n appId,\n callbackPort,\n });\n }\n return sessionInstance;\n}\n\n/**\n * Reset the session singleton (used for cleanup on initialization failure)\n */\nfunction resetSession(): void {\n sessionInstance = null;\n}\n\n/**\n * Plugin registration function\n */\nexport default async function register(api: OpenClawApi) {\n try {\n // Initialize session (authenticate if needed)\n const session = getSession(api.config);\n await session.initialize();\n\n // Register all Phantom MCP tools\n registerPhantomTools(api, session);\n } catch (error) {\n console.error(\"Failed to initialize Phantom OpenClaw plugin:\", error); // eslint-disable-line no-console\n // Reset singleton so next attempt gets a fresh instance\n resetSession();\n throw error;\n }\n}\n"],"mappings":"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","names":["isRecord"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/phantom-openclaw-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "OpenClaw plugin that bridges tool calls to Phantom's MCP server for wallet operations.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"typescript": "^5.0.4"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@phantom/mcp-server": "
|
|
46
|
+
"@phantom/mcp-server": "^0.2.2",
|
|
47
47
|
"@sinclair/typebox": "^0.32.0"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
@@ -55,4 +55,4 @@
|
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"directory": "_release/package"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|