moon-iq 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 +41 -0
- package/dist/index.js +461 -31
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/src/auth.ts +0 -332
- package/src/client.ts +0 -68
- package/src/generated/client.ts +0 -556
- package/src/index.ts +0 -307
- package/tsconfig.json +0 -10
- package/tsup.config.ts +0 -10
package/src/index.ts
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command, Option } from "commander";
|
|
3
|
-
import {
|
|
4
|
-
clearCredentials,
|
|
5
|
-
getConfig,
|
|
6
|
-
getConfigOrDefault,
|
|
7
|
-
getCredentials,
|
|
8
|
-
getValidToken,
|
|
9
|
-
login,
|
|
10
|
-
refreshCredentials,
|
|
11
|
-
} from "./auth";
|
|
12
|
-
import { callToolWithAuth, TOOL_NAMES } from "./client";
|
|
13
|
-
import {
|
|
14
|
-
TOOL_DEFINITIONS,
|
|
15
|
-
type ToolDefinition,
|
|
16
|
-
type ToolOption,
|
|
17
|
-
} from "./generated/client";
|
|
18
|
-
|
|
19
|
-
const DEFAULT_BASE_URL = "https://moon-iq.com";
|
|
20
|
-
|
|
21
|
-
const program = new Command();
|
|
22
|
-
|
|
23
|
-
program.name("mooniq").description("Moon IQ CLI").version("0.2.0");
|
|
24
|
-
|
|
25
|
-
program
|
|
26
|
-
.command("login")
|
|
27
|
-
.description("Log in to Moon IQ via OAuth")
|
|
28
|
-
.option(
|
|
29
|
-
"-b, --base-url <url>",
|
|
30
|
-
"Base URL (default: https://moon-iq.com or from ~/.config/moon-iq/config.json)",
|
|
31
|
-
)
|
|
32
|
-
.action(async (options) => {
|
|
33
|
-
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
34
|
-
const config = getConfigOrDefault(baseUrl);
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
await login({ ...config, baseUrl });
|
|
38
|
-
console.log("Logged in successfully.");
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error("Login failed:", (error as Error).message);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
program
|
|
46
|
-
.command("logout")
|
|
47
|
-
.description("Log out and clear stored credentials")
|
|
48
|
-
.action(() => {
|
|
49
|
-
clearCredentials();
|
|
50
|
-
console.log("Logged out.");
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
program
|
|
54
|
-
.command("whoami")
|
|
55
|
-
.description("Show current user info")
|
|
56
|
-
.action(async () => {
|
|
57
|
-
let token = await getValidToken();
|
|
58
|
-
if (!token) {
|
|
59
|
-
console.error("Not logged in. Run `mooniq login` first.");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const creds = getCredentials();
|
|
64
|
-
const config = getConfig();
|
|
65
|
-
const baseUrl =
|
|
66
|
-
config?.baseUrl ?? creds?.baseUrl ?? DEFAULT_BASE_URL;
|
|
67
|
-
|
|
68
|
-
let res = await fetch(`${baseUrl}/api/oauth/userinfo`, {
|
|
69
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (res.status === 401 && creds?.refreshToken && config && config.baseUrl === creds.baseUrl) {
|
|
73
|
-
try {
|
|
74
|
-
const newCreds = await refreshCredentials(creds, config);
|
|
75
|
-
res = await fetch(`${baseUrl}/api/oauth/userinfo`, {
|
|
76
|
-
headers: { Authorization: `Bearer ${newCreds.accessToken}` },
|
|
77
|
-
});
|
|
78
|
-
} catch {
|
|
79
|
-
// Refresh failed, fall through
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!res.ok) {
|
|
84
|
-
console.error("Failed to fetch user info:", res.status);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const userinfo = await res.json();
|
|
89
|
-
console.log(JSON.stringify(userinfo, null, 2));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
program
|
|
93
|
-
.command("run <tool_name>")
|
|
94
|
-
.description("Run a tool by name. Use --input for JSON params.")
|
|
95
|
-
.option(
|
|
96
|
-
"-i, --input <json>",
|
|
97
|
-
"Input parameters as JSON (omit for empty params)",
|
|
98
|
-
)
|
|
99
|
-
.option(
|
|
100
|
-
"-b, --base-url <url>",
|
|
101
|
-
"Base URL (default: from credentials or https://moon-iq.com)",
|
|
102
|
-
)
|
|
103
|
-
.addHelpText(
|
|
104
|
-
"after",
|
|
105
|
-
`
|
|
106
|
-
Examples:
|
|
107
|
-
mooniq run user_retrieve # no params
|
|
108
|
-
mooniq run token_search --input '{"query":"SOL","chain":"solana"}' # with params
|
|
109
|
-
`,
|
|
110
|
-
)
|
|
111
|
-
.action(async (toolName: string, options) => {
|
|
112
|
-
const toolNames = TOOL_NAMES as readonly string[];
|
|
113
|
-
if (!toolNames.includes(toolName)) {
|
|
114
|
-
console.error(`Unknown tool: ${toolName}`);
|
|
115
|
-
console.error(`Available tools: ${toolNames.slice(0, 10).join(", ")}...`);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let params: Record<string, unknown> = {};
|
|
120
|
-
if (options.input) {
|
|
121
|
-
try {
|
|
122
|
-
params = JSON.parse(options.input);
|
|
123
|
-
} catch {
|
|
124
|
-
console.error("Invalid JSON in --input");
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const creds = getCredentials();
|
|
130
|
-
const config = getConfig();
|
|
131
|
-
const baseUrl =
|
|
132
|
-
options.baseUrl ?? config?.baseUrl ?? creds?.baseUrl ?? DEFAULT_BASE_URL;
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
const result = await callToolWithAuth(baseUrl, toolName, params);
|
|
136
|
-
console.log(JSON.stringify(result, null, 2));
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error("Tool call failed:", (error as Error).message);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
program
|
|
144
|
-
.command("tools")
|
|
145
|
-
.description("List available tools")
|
|
146
|
-
.action(() => {
|
|
147
|
-
TOOL_NAMES.forEach((name) => console.log(name));
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Build hierarchical subcommands from tool definitions.
|
|
152
|
-
* e.g., token_balance_list -> mooniq token balance list --wallet <wallet> --chain <chain>
|
|
153
|
-
*/
|
|
154
|
-
function buildToolCommands(program: Command): void {
|
|
155
|
-
// Build a tree structure from tool names
|
|
156
|
-
type CommandNode = {
|
|
157
|
-
children: Map<string, CommandNode>;
|
|
158
|
-
tool?: ToolDefinition;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const root: CommandNode = { children: new Map() };
|
|
162
|
-
|
|
163
|
-
// Build the tree
|
|
164
|
-
for (const tool of TOOL_DEFINITIONS) {
|
|
165
|
-
const parts = tool.name.split("_");
|
|
166
|
-
let current = root;
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < parts.length; i++) {
|
|
169
|
-
const part = parts[i];
|
|
170
|
-
if (!current.children.has(part)) {
|
|
171
|
-
current.children.set(part, { children: new Map() });
|
|
172
|
-
}
|
|
173
|
-
current = current.children.get(part)!;
|
|
174
|
-
|
|
175
|
-
// If this is the last part, attach the tool
|
|
176
|
-
if (i === parts.length - 1) {
|
|
177
|
-
current.tool = tool;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Recursively create Commander commands
|
|
183
|
-
function createCommands(node: CommandNode, parent: Command, path: string[]) {
|
|
184
|
-
for (const [name, child] of node.children) {
|
|
185
|
-
const currentPath = [...path, name];
|
|
186
|
-
|
|
187
|
-
if (child.tool) {
|
|
188
|
-
// This is a leaf node with a tool
|
|
189
|
-
const cmd = parent
|
|
190
|
-
.command(name)
|
|
191
|
-
.description(child.tool.description || `Run ${child.tool.name}`);
|
|
192
|
-
|
|
193
|
-
// Add options from the tool schema
|
|
194
|
-
for (const opt of child.tool.options) {
|
|
195
|
-
const optionFlag = createOptionFlag(opt);
|
|
196
|
-
const option = new Option(optionFlag, opt.description);
|
|
197
|
-
|
|
198
|
-
if (opt.choices) {
|
|
199
|
-
option.choices(opt.choices);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (opt.required) {
|
|
203
|
-
cmd.addOption(option);
|
|
204
|
-
} else {
|
|
205
|
-
cmd.addOption(option);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Add base-url option
|
|
210
|
-
cmd.option(
|
|
211
|
-
"-b, --base-url <url>",
|
|
212
|
-
"Base URL (default: from credentials or https://moon-iq.com)",
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// Add action handler
|
|
216
|
-
const toolName = child.tool.name;
|
|
217
|
-
cmd.action(async (options) => {
|
|
218
|
-
const params = buildParams(child.tool!.options, options);
|
|
219
|
-
|
|
220
|
-
const creds = getCredentials();
|
|
221
|
-
const config = getConfig();
|
|
222
|
-
const baseUrl =
|
|
223
|
-
options.baseUrl ??
|
|
224
|
-
config?.baseUrl ??
|
|
225
|
-
creds?.baseUrl ??
|
|
226
|
-
DEFAULT_BASE_URL;
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
const result = await callToolWithAuth(baseUrl, toolName, params);
|
|
230
|
-
console.log(JSON.stringify(result, null, 2));
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.error("Tool call failed:", (error as Error).message);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// If there are more children, recurse
|
|
238
|
-
if (child.children.size > 0) {
|
|
239
|
-
createCommands(child, cmd, currentPath);
|
|
240
|
-
}
|
|
241
|
-
} else if (child.children.size > 0) {
|
|
242
|
-
// This is an intermediate node
|
|
243
|
-
const cmd = parent
|
|
244
|
-
.command(name)
|
|
245
|
-
.description(`${name} commands`);
|
|
246
|
-
|
|
247
|
-
createCommands(child, cmd, currentPath);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
createCommands(root, program, []);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Create an option flag string for Commander
|
|
257
|
-
*/
|
|
258
|
-
function createOptionFlag(opt: ToolOption): string {
|
|
259
|
-
const kebabName = opt.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
260
|
-
|
|
261
|
-
if (opt.type === "boolean") {
|
|
262
|
-
return `--${kebabName}`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const valuePlaceholder = `<${opt.name}>`;
|
|
266
|
-
return `--${kebabName} ${valuePlaceholder}`;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Build params object from options
|
|
271
|
-
*/
|
|
272
|
-
function buildParams(
|
|
273
|
-
toolOptions: ToolOption[],
|
|
274
|
-
cmdOptions: Record<string, unknown>,
|
|
275
|
-
): Record<string, unknown> {
|
|
276
|
-
const params: Record<string, unknown> = {};
|
|
277
|
-
|
|
278
|
-
for (const opt of toolOptions) {
|
|
279
|
-
// Convert kebab-case back to camelCase for lookup
|
|
280
|
-
const kebabName = opt.name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
281
|
-
const camelName = kebabName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
282
|
-
|
|
283
|
-
// Commander converts --some-option to someOption
|
|
284
|
-
const value = cmdOptions[camelName] ?? cmdOptions[opt.name];
|
|
285
|
-
|
|
286
|
-
if (value !== undefined) {
|
|
287
|
-
// Convert types as needed
|
|
288
|
-
if (opt.type === "number" && typeof value === "string") {
|
|
289
|
-
params[opt.name] = parseFloat(value);
|
|
290
|
-
} else if (opt.type === "boolean") {
|
|
291
|
-
params[opt.name] = value === true || value === "true";
|
|
292
|
-
} else {
|
|
293
|
-
params[opt.name] = value;
|
|
294
|
-
}
|
|
295
|
-
} else if (!opt.required) {
|
|
296
|
-
// Set null for optional params that weren't provided
|
|
297
|
-
params[opt.name] = null;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return params;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Build all tool commands
|
|
305
|
-
buildToolCommands(program);
|
|
306
|
-
|
|
307
|
-
program.parse();
|
package/tsconfig.json
DELETED