@finchagentic/mcp 4.7.2 → 4.7.4
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 +74 -39
- package/dist/_zod-helpers.js +10 -0
- package/dist/cli-doctor.js +300 -0
- package/dist/cli-env.js +91 -0
- package/dist/cli-install.js +236 -0
- package/dist/cli-login.js +101 -0
- package/dist/cli-orders.js +175 -0
- package/dist/cli-setup.js +270 -0
- package/dist/cli-ui.js +168 -0
- package/dist/cli-vault.js +75 -0
- package/dist/cli.js +61 -1152
- package/dist/convex.js +16 -5
- package/dist/enrichment-router.js +5 -18
- package/dist/finch-output.js +30 -29
- package/dist/finch-status.js +126 -44
- package/dist/local-vault.js +5 -12
- package/dist/output-schemas.js +3 -12
- package/dist/project.js +4 -13
- package/dist/server.js +13 -35
- package/dist/tool-filter.js +4 -13
- package/dist/tools/_solidity-scan.js +5 -14
- package/dist/tools/agents.js +14 -26
- package/dist/tools/deep-research-constants.js +33 -0
- package/dist/tools/deep-research-firecrawl.js +88 -0
- package/dist/tools/deep-research-planning.js +249 -0
- package/dist/tools/deep-research-synthesis.js +343 -0
- package/dist/tools/deep-research-text.js +158 -0
- package/dist/tools/deep-research-tools.js +90 -0
- package/dist/tools/deep-research.js +47 -938
- package/dist/tools/defi.js +4 -3
- package/dist/tools/insider.js +4 -14
- package/dist/tools/insight.js +7 -6
- package/dist/tools/market.js +16 -15
- package/dist/tools/memory.js +43 -61
- package/dist/tools/monitor.js +7 -6
- package/dist/tools/os.js +7 -85
- package/dist/tools/research.js +7 -6
- package/dist/tools/rh-mcp-constants.js +65 -0
- package/dist/tools/rh-mcp-dex.js +107 -0
- package/dist/tools/rh-mcp-provider.js +127 -0
- package/dist/tools/rh-mcp-resolve.js +137 -0
- package/dist/tools/rh-mcp-risk.js +230 -0
- package/dist/tools/rh-mcp-safety.js +234 -0
- package/dist/tools/rh-mcp-swap.js +181 -0
- package/dist/tools/rh-mcp-tools.js +137 -0
- package/dist/tools/rh-mcp.js +75 -1191
- package/dist/tools/scanner.js +10 -9
- package/dist/tools/stake.js +7 -20
- package/dist/tools/vault.js +52 -59
- package/package.json +13 -10
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `finch install` - auto-configure detected MCP clients (Claude Desktop,
|
|
3
|
+
// Cursor, Windsurf, VS Code, Zed).
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.installFlow = installFlow;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const config_js_1 = require("./config.js");
|
|
43
|
+
const cli_ui_js_1 = require("./cli-ui.js");
|
|
44
|
+
const cli_env_js_1 = require("./cli-env.js");
|
|
45
|
+
function resolveClients() {
|
|
46
|
+
const home = os.homedir();
|
|
47
|
+
const plat = os.platform();
|
|
48
|
+
const appdata = process.env.APPDATA ?? path.join(home, "AppData", "Roaming");
|
|
49
|
+
const defs = [
|
|
50
|
+
{
|
|
51
|
+
name: "Claude Desktop", serversKey: "mcpServers", entryStyle: "standard",
|
|
52
|
+
paths: {
|
|
53
|
+
darwin: path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
54
|
+
win32: path.join(appdata, "Claude", "claude_desktop_config.json"),
|
|
55
|
+
linux: path.join(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Cursor", serversKey: "mcpServers", entryStyle: "standard",
|
|
60
|
+
paths: {
|
|
61
|
+
darwin: path.join(home, ".cursor", "mcp.json"),
|
|
62
|
+
win32: path.join(home, ".cursor", "mcp.json"),
|
|
63
|
+
linux: path.join(home, ".cursor", "mcp.json"),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "Windsurf", serversKey: "mcpServers", entryStyle: "standard",
|
|
68
|
+
paths: {
|
|
69
|
+
darwin: path.join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
70
|
+
win32: path.join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
71
|
+
linux: path.join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
// VS Code: user-level mcp.json uses the top-level `servers` key (NOT
|
|
75
|
+
// mcpServers) and each entry needs `"type": "stdio"`.
|
|
76
|
+
{
|
|
77
|
+
name: "VS Code", serversKey: "servers", entryStyle: "vscode",
|
|
78
|
+
paths: {
|
|
79
|
+
darwin: path.join(home, "Library", "Application Support", "Code", "User", "mcp.json"),
|
|
80
|
+
win32: path.join(appdata, "Code", "User", "mcp.json"),
|
|
81
|
+
linux: path.join(home, ".config", "Code", "User", "mcp.json"),
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "VS Code Insiders", serversKey: "servers", entryStyle: "vscode",
|
|
86
|
+
paths: {
|
|
87
|
+
darwin: path.join(home, "Library", "Application Support", "Code - Insiders", "User", "mcp.json"),
|
|
88
|
+
win32: path.join(appdata, "Code - Insiders", "User", "mcp.json"),
|
|
89
|
+
linux: path.join(home, ".config", "Code - Insiders", "User", "mcp.json"),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
// Zed: MCP servers live in settings.json under `context_servers`, and each
|
|
93
|
+
// entry needs `"source": "custom"`. (Not a separate mcp.json file.)
|
|
94
|
+
{
|
|
95
|
+
name: "Zed", serversKey: "context_servers", entryStyle: "zed",
|
|
96
|
+
paths: {
|
|
97
|
+
darwin: path.join(home, ".config", "zed", "settings.json"),
|
|
98
|
+
win32: path.join(appdata, "Zed", "settings.json"),
|
|
99
|
+
linux: path.join(home, ".config", "zed", "settings.json"),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
return defs
|
|
104
|
+
.map((d) => {
|
|
105
|
+
const configPath = d.paths[plat] ?? d.paths.linux;
|
|
106
|
+
return { name: d.name, configPath, serversKey: d.serversKey, entryStyle: d.entryStyle };
|
|
107
|
+
})
|
|
108
|
+
.filter((c) => {
|
|
109
|
+
// Include if the config file exists OR the parent directory exists (app installed but not yet configured)
|
|
110
|
+
return fs.existsSync(c.configPath) || fs.existsSync(path.dirname(c.configPath));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// `npx -y @finchagentic/mcp@version` alone fails with "could not determine
|
|
114
|
+
// executable to run" - the package ships two bins (finch, finch-mcp)
|
|
115
|
+
// and neither matches the unscoped package name npx resolves by default.
|
|
116
|
+
// -p ... finch-mcp names the actual MCP-protocol server explicitly
|
|
117
|
+
// (not `finch`, which is the human-interactive REPL and would break the
|
|
118
|
+
// JSON-RPC handshake if a host spawned it instead). Pinned to the currently
|
|
119
|
+
// installed version, never @latest - matches the security boundary
|
|
120
|
+
// documented in the README (wallet/credential access needs a stable,
|
|
121
|
+
// reviewable version, not a moving target).
|
|
122
|
+
function buildEntry(style) {
|
|
123
|
+
const base = {
|
|
124
|
+
command: "npx",
|
|
125
|
+
args: ["-y", "-p", `@finchagentic/mcp@${cli_env_js_1.PKG_VERSION}`, "finch-mcp"],
|
|
126
|
+
env: {},
|
|
127
|
+
};
|
|
128
|
+
if (style === "vscode")
|
|
129
|
+
return { type: "stdio", ...base };
|
|
130
|
+
if (style === "zed")
|
|
131
|
+
return { source: "custom", ...base };
|
|
132
|
+
return base;
|
|
133
|
+
}
|
|
134
|
+
// Returns "skipped" (distinct from "error") when an existing config file can't
|
|
135
|
+
// be parsed - overwriting it would destroy the user's real config (VS Code
|
|
136
|
+
// mcp.json and Zed settings.json are JSONC and routinely contain comments,
|
|
137
|
+
// which JSON.parse rejects). In that case we leave the file untouched and the
|
|
138
|
+
// caller prints a manual-add snippet instead.
|
|
139
|
+
function installIntoConfig(client) {
|
|
140
|
+
try {
|
|
141
|
+
let json = {};
|
|
142
|
+
if (fs.existsSync(client.configPath)) {
|
|
143
|
+
const raw = fs.readFileSync(client.configPath, "utf8");
|
|
144
|
+
try {
|
|
145
|
+
json = JSON.parse(raw);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return "skipped"; // don't clobber an unparseable (likely JSONC) file
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Ensure parent dir exists
|
|
153
|
+
fs.mkdirSync(path.dirname(client.configPath), { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
const key = client.serversKey;
|
|
156
|
+
if (!json[key] || typeof json[key] !== "object")
|
|
157
|
+
json[key] = {};
|
|
158
|
+
const existed = !!json[key].finch;
|
|
159
|
+
json[key].finch = buildEntry(client.entryStyle);
|
|
160
|
+
fs.writeFileSync(client.configPath, JSON.stringify(json, null, 2), "utf8");
|
|
161
|
+
return existed ? "updated" : "added";
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return "error";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function installFlow() {
|
|
168
|
+
console.log(`\n ${cli_ui_js_1.C.cyan}${cli_ui_js_1.C.bold}finch install${cli_ui_js_1.C.reset}\n`);
|
|
169
|
+
console.log(` ${cli_ui_js_1.C.dim}Scanning for MCP-compatible apps...${cli_ui_js_1.C.reset}\n`);
|
|
170
|
+
const clients = resolveClients();
|
|
171
|
+
if (clients.length === 0) {
|
|
172
|
+
console.log(` ${cli_ui_js_1.C.yellow}No MCP-compatible apps found.${cli_ui_js_1.C.reset}`);
|
|
173
|
+
console.log(` ${cli_ui_js_1.C.dim}Install Claude Desktop, Cursor, or Windsurf, then run this again.${cli_ui_js_1.C.reset}\n`);
|
|
174
|
+
console.log(` ${cli_ui_js_1.C.dim}Or add manually to your app's MCP config:${cli_ui_js_1.C.reset}`);
|
|
175
|
+
console.log(` ${cli_ui_js_1.C.dim} "finch": { "command": "npx", "args": ["-y", "-p", "@finchagentic/mcp@${cli_env_js_1.PKG_VERSION}", "finch-mcp"] }${cli_ui_js_1.C.reset}\n`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
let installed = 0;
|
|
179
|
+
const toRestart = [];
|
|
180
|
+
const manual = [];
|
|
181
|
+
for (const client of clients) {
|
|
182
|
+
const result = installIntoConfig(client);
|
|
183
|
+
const short = client.configPath.replace(os.homedir(), "~");
|
|
184
|
+
if (result === "added") {
|
|
185
|
+
console.log(` ${cli_ui_js_1.C.green}✓${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.bold}${client.name}${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}→ added${cli_ui_js_1.C.reset}`);
|
|
186
|
+
console.log(` ${cli_ui_js_1.C.dim}${short}${cli_ui_js_1.C.reset}`);
|
|
187
|
+
installed++;
|
|
188
|
+
toRestart.push(client.name);
|
|
189
|
+
}
|
|
190
|
+
else if (result === "updated") {
|
|
191
|
+
console.log(` ${cli_ui_js_1.C.green}↑${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.bold}${client.name}${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}→ updated (re-pinned to v${cli_env_js_1.PKG_VERSION})${cli_ui_js_1.C.reset}`);
|
|
192
|
+
console.log(` ${cli_ui_js_1.C.dim}${short}${cli_ui_js_1.C.reset}`);
|
|
193
|
+
installed++;
|
|
194
|
+
toRestart.push(client.name);
|
|
195
|
+
}
|
|
196
|
+
else if (result === "skipped") {
|
|
197
|
+
// Existing config couldn't be parsed (likely has comments) - never
|
|
198
|
+
// overwrite it. Show the user exactly what to paste under which key.
|
|
199
|
+
console.log(` ${cli_ui_js_1.C.yellow}⚠${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.bold}${client.name}${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}→ has an existing config we won't overwrite${cli_ui_js_1.C.reset}`);
|
|
200
|
+
console.log(` ${cli_ui_js_1.C.dim}${short}${cli_ui_js_1.C.reset}`);
|
|
201
|
+
manual.push(` ${cli_ui_js_1.C.dim}${client.name}: add under ${cli_ui_js_1.C.reset}${cli_ui_js_1.C.cyan}"${client.serversKey}"${cli_ui_js_1.C.reset}${cli_ui_js_1.C.dim}:${cli_ui_js_1.C.reset}\n ${cli_ui_js_1.C.dim}${JSON.stringify({ finch: buildEntry(client.entryStyle) })}${cli_ui_js_1.C.reset}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.log(` ${cli_ui_js_1.C.yellow}✗${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.bold}${client.name}${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}→ write failed (check permissions)${cli_ui_js_1.C.reset}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (manual.length) {
|
|
208
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Add these by hand (we didn't touch your existing config):${cli_ui_js_1.C.reset}`);
|
|
209
|
+
for (const m of manual)
|
|
210
|
+
console.log(m);
|
|
211
|
+
}
|
|
212
|
+
console.log(`\n ${cli_ui_js_1.C.dim}${"─".repeat(52)}${cli_ui_js_1.C.reset}\n`);
|
|
213
|
+
if (installed === 0) {
|
|
214
|
+
console.log(` ${cli_ui_js_1.C.yellow}Nothing was installed. Check file permissions.${cli_ui_js_1.C.reset}\n`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log(` ${cli_ui_js_1.C.green}${cli_ui_js_1.C.bold}✓ Finch installed in ${installed} app${installed === 1 ? "" : "s"}.${cli_ui_js_1.C.reset}\n`);
|
|
218
|
+
if (toRestart.length > 0) {
|
|
219
|
+
console.log(` ${cli_ui_js_1.C.dim}Restart to activate: ${toRestart.join(" · ")}${cli_ui_js_1.C.reset}`);
|
|
220
|
+
}
|
|
221
|
+
// Tool palette: the server exposes the CORE subset by default to keep the
|
|
222
|
+
// client's context lean. Everything else stays callable by name, and power
|
|
223
|
+
// users can flip on the full surface. Surfaced here so the count a user sees
|
|
224
|
+
// in their client (CORE) doesn't silently contradict the "all tools" framing.
|
|
225
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Your client will see the ${cli_env_js_1.CORE_TOOL_COUNT} core tools by default. ` +
|
|
226
|
+
`Add ${cli_ui_js_1.C.reset}${cli_ui_js_1.C.cyan}"env": { "FINCH_TOOLS": "all" }${cli_ui_js_1.C.reset}${cli_ui_js_1.C.dim} to the finch entry for all ${cli_env_js_1.TOTAL_TOOL_COUNT}.${cli_ui_js_1.C.reset}`);
|
|
227
|
+
const cfg = (0, config_js_1.readConfig)();
|
|
228
|
+
if (!cfg.sessionToken) {
|
|
229
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Next step - sign in to unlock all tools:${cli_ui_js_1.C.reset}`);
|
|
230
|
+
console.log(` ${cli_ui_js_1.C.cyan} finch login${cli_ui_js_1.C.reset}\n`);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Already signed in as ${cfg.email ?? "user"}.${cli_ui_js_1.C.reset}`);
|
|
234
|
+
console.log(` ${cli_ui_js_1.C.dim}Open your MCP client and start using Finch.${cli_ui_js_1.C.reset}\n`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `finch login` - API-key sign-in, both the interactive prompt and the
|
|
3
|
+
// FINCH_API_KEY env-var fast path.
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.loginWithApiKey = loginWithApiKey;
|
|
39
|
+
exports.loginFlow = loginFlow;
|
|
40
|
+
const readline = __importStar(require("readline"));
|
|
41
|
+
const config_js_1 = require("./config.js");
|
|
42
|
+
const clink_input_js_1 = require("./clink-input.js");
|
|
43
|
+
const cli_ui_js_1 = require("./cli-ui.js");
|
|
44
|
+
const cli_env_js_1 = require("./cli-env.js");
|
|
45
|
+
async function loginWithApiKey(rl) {
|
|
46
|
+
const ask = (q) => new Promise(resolve => rl.question(q, resolve));
|
|
47
|
+
console.log(` ${cli_ui_js_1.C.dim}Generate an API key at app.finchagentic.com → Settings → API Keys${cli_ui_js_1.C.reset}`);
|
|
48
|
+
console.log(` ${cli_ui_js_1.C.dim}Or set env var: set FINCH_API_KEY=finch_sk_... (or noel_sk_...)${cli_ui_js_1.C.reset}`);
|
|
49
|
+
let apiKey = (await ask(` API key (finch_sk_... / noel_sk_...): `)).trim();
|
|
50
|
+
if (!apiKey)
|
|
51
|
+
return;
|
|
52
|
+
// Deduplicates doubled input from a known Clink v1.7.6 terminal bug (e.g.
|
|
53
|
+
// "finch_sk_xxfinch_sk_xx" → "finch_sk_xx"); also strips non-ASCII Clink can inject.
|
|
54
|
+
apiKey = (0, clink_input_js_1.dedupClinkInput)(apiKey).replace(/[^\x20-\x7E]/g, "").trim();
|
|
55
|
+
if (!(0, config_js_1.isApiKey)(apiKey)) {
|
|
56
|
+
console.log(`\n ${cli_ui_js_1.C.red}✗${cli_ui_js_1.C.reset} API key must start with "finch_sk_" or "noel_sk_". Got: "${apiKey.slice(0, 20)}..."\n`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
process.stdout.write(` Authenticating...`);
|
|
60
|
+
const res = await fetch(`${cli_env_js_1.CONVEX_SITE}/auth/apikey/login`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/json" },
|
|
63
|
+
body: JSON.stringify({ apiKey }),
|
|
64
|
+
});
|
|
65
|
+
const data = await res.json();
|
|
66
|
+
if (!res.ok || !data.token) {
|
|
67
|
+
console.log(`\n ${cli_ui_js_1.C.red}✗${cli_ui_js_1.C.reset} ${data.error ?? "Invalid API key"}\n`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const email = data.email ?? "api-key-user";
|
|
71
|
+
const name = data.displayName ?? undefined;
|
|
72
|
+
(0, config_js_1.writeConfig)({ sessionToken: data.token, email, name, walletAddress: data.walletAddress });
|
|
73
|
+
(0, cli_ui_js_1.printLoginSuccess)({ email, name, walletAddress: data.walletAddress });
|
|
74
|
+
}
|
|
75
|
+
async function loginFlow(loginRl) {
|
|
76
|
+
const rl = loginRl ?? readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
77
|
+
// Check env var first — skip prompt entirely
|
|
78
|
+
const envKey = process.env.FINCH_API_KEY;
|
|
79
|
+
if (envKey && (0, config_js_1.isApiKey)(envKey)) {
|
|
80
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Found FINCH_API_KEY in environment — authenticating...${cli_ui_js_1.C.reset}`);
|
|
81
|
+
process.stdout.write(` Authenticating...`);
|
|
82
|
+
const res = await fetch(`${cli_env_js_1.CONVEX_SITE}/auth/apikey/login`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
body: JSON.stringify({ apiKey: envKey }),
|
|
86
|
+
});
|
|
87
|
+
const data = await res.json();
|
|
88
|
+
if (res.ok && data.token) {
|
|
89
|
+
const email = data.email ?? "api-key-user";
|
|
90
|
+
const name = data.displayName ?? undefined;
|
|
91
|
+
(0, config_js_1.writeConfig)({ sessionToken: data.token, email, name, walletAddress: data.walletAddress });
|
|
92
|
+
(0, cli_ui_js_1.printLoginSuccess)({ email, name, walletAddress: data.walletAddress });
|
|
93
|
+
rl.close();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`\n ${cli_ui_js_1.C.red}✗${cli_ui_js_1.C.reset} Env var FINCH_API_KEY is invalid. Falling back to manual login.\n`);
|
|
97
|
+
}
|
|
98
|
+
console.log(`\n ${cli_ui_js_1.C.cyan}${cli_ui_js_1.C.bold}Sign in to Finch${cli_ui_js_1.C.reset}\n`);
|
|
99
|
+
await loginWithApiKey(rl);
|
|
100
|
+
rl.close();
|
|
101
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `finch orders` - Robinhood Chain order scheduler.
|
|
3
|
+
//
|
|
4
|
+
// rh_dca_create/rh_bracket_create (src/tools/rh-orders.ts) only ever write a
|
|
5
|
+
// LOCAL pending order - nothing executes it. Only rh_orders_tick does, and
|
|
6
|
+
// per that file's own header comment, "an always-on scheduler must call
|
|
7
|
+
// rh_orders_tick {execute:true} periodically" - which this package never
|
|
8
|
+
// actually set up. A user who creates a daily DCA and closes their laptop
|
|
9
|
+
// got nothing, silently: no error, no warning, the order just sat there.
|
|
10
|
+
// These three subcommands are that missing scheduler, reusing the existing
|
|
11
|
+
// tick/lock/kill-switch logic as-is - "wire up the missing piece", not
|
|
12
|
+
// "build order execution from scratch".
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.runOrdersTick = runOrdersTick;
|
|
48
|
+
exports.installOrdersScheduler = installOrdersScheduler;
|
|
49
|
+
exports.ordersFlow = ordersFlow;
|
|
50
|
+
const os = __importStar(require("os"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const child_process = __importStar(require("child_process"));
|
|
53
|
+
const cli_ui_js_1 = require("./cli-ui.js");
|
|
54
|
+
const cli_env_js_1 = require("./cli-env.js");
|
|
55
|
+
async function runOrdersTick(execute) {
|
|
56
|
+
const { handleRhOrderTool } = await Promise.resolve().then(() => __importStar(require("./tools/rh-orders.js")));
|
|
57
|
+
const result = await handleRhOrderTool("rh_orders_tick", { execute });
|
|
58
|
+
const text = result?.content?.[0]?.type === "text" ? result.content[0].text : "(no output)";
|
|
59
|
+
return { text, isError: !!result?.isError };
|
|
60
|
+
}
|
|
61
|
+
function installOrdersScheduler() {
|
|
62
|
+
// Version-pinned npx invocation, not a hardcoded path to this process's
|
|
63
|
+
// own binary - matches this package's own "always pin the version, never
|
|
64
|
+
// @latest" security guidance (README), and survives regardless of whether
|
|
65
|
+
// the user got here via a global install or a one-off `npx -p`, whose
|
|
66
|
+
// resolved path may not exist by the time the scheduler fires later.
|
|
67
|
+
const invocation = `npx -y -p @finchagentic/mcp@${cli_env_js_1.PKG_VERSION} finch orders tick --execute`;
|
|
68
|
+
if (process.platform === "win32") {
|
|
69
|
+
const taskName = "FinchOrdersTick";
|
|
70
|
+
try {
|
|
71
|
+
child_process.execSync(`schtasks /Create /TN "${taskName}" /TR "${invocation}" /SC MINUTE /MO 15 /F`, { stdio: "pipe" });
|
|
72
|
+
console.log(`\n ${cli_ui_js_1.C.green}✓${cli_ui_js_1.C.reset} Registered Windows Task Scheduler task "${taskName}" - ticks every 15 min, survives reboots.`);
|
|
73
|
+
console.log(` ${cli_ui_js_1.C.dim}Remove it:${cli_ui_js_1.C.reset} schtasks /Delete /TN "${taskName}" /F`);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.error(` ${cli_ui_js_1.C.red}✗ schtasks failed: ${err.message}${cli_ui_js_1.C.reset}`);
|
|
77
|
+
console.error(` ${cli_ui_js_1.C.dim}Try running as Administrator, or use \`finch orders daemon\` instead.${cli_ui_js_1.C.reset}\n`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const logFile = path.join(os.homedir(), ".finch", "orders-tick.log");
|
|
83
|
+
const line = `*/15 * * * * ${invocation} >> ${logFile} 2>&1`;
|
|
84
|
+
try {
|
|
85
|
+
const existing = (() => {
|
|
86
|
+
try {
|
|
87
|
+
return child_process.execSync("crontab -l", { stdio: ["pipe", "pipe", "ignore"] }).toString();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return "";
|
|
91
|
+
} // no crontab yet for this user - not an error
|
|
92
|
+
})();
|
|
93
|
+
if (existing.includes("finch orders tick")) {
|
|
94
|
+
console.log(`\n ${cli_ui_js_1.C.yellow}○${cli_ui_js_1.C.reset} A finch orders tick entry already exists in crontab - not adding a duplicate.\n`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const next = existing.trim().length > 0 ? `${existing.trim()}\n${line}\n` : `${line}\n`;
|
|
98
|
+
child_process.execSync("crontab -", { input: next });
|
|
99
|
+
console.log(`\n ${cli_ui_js_1.C.green}✓${cli_ui_js_1.C.reset} Added a crontab entry - ticks every 15 min, survives reboots.`);
|
|
100
|
+
console.log(` ${cli_ui_js_1.C.dim}Remove it:${cli_ui_js_1.C.reset} crontab -e ${cli_ui_js_1.C.dim}(delete the finch orders line)${cli_ui_js_1.C.reset}`);
|
|
101
|
+
console.log(` ${cli_ui_js_1.C.dim}Logs:${cli_ui_js_1.C.reset} ${logFile}`);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.error(` ${cli_ui_js_1.C.red}✗ crontab failed: ${err.message}${cli_ui_js_1.C.reset}`);
|
|
105
|
+
console.error(` ${cli_ui_js_1.C.dim}Try \`finch orders daemon\` instead.${cli_ui_js_1.C.reset}\n`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
console.log(` ${cli_ui_js_1.C.dim}Only the local wallet's own default key is needed for signing - set FINCH_WALLET_PASSPHRASE as a persistent system env var (not just your shell rc file) if you use one, since scheduled tasks don't inherit an interactive shell's environment.${cli_ui_js_1.C.reset}\n`);
|
|
110
|
+
}
|
|
111
|
+
async function ordersFlow() {
|
|
112
|
+
const sub = process.argv[3];
|
|
113
|
+
if (sub === "tick") {
|
|
114
|
+
const execute = process.argv.includes("--execute");
|
|
115
|
+
const { text, isError } = await runOrdersTick(execute);
|
|
116
|
+
console.log(`\n${text}\n`);
|
|
117
|
+
if (isError)
|
|
118
|
+
process.exit(1);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (sub === "daemon") {
|
|
122
|
+
const intervalFlag = process.argv.indexOf("--interval");
|
|
123
|
+
const intervalMinutes = intervalFlag !== -1 ? Number(process.argv[intervalFlag + 1]) : 15;
|
|
124
|
+
if (!(intervalMinutes > 0)) {
|
|
125
|
+
console.error(` ${cli_ui_js_1.C.red}✗ --interval must be a positive number of minutes${cli_ui_js_1.C.reset}\n`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
console.log(`\n ${cli_ui_js_1.C.cyan}${cli_ui_js_1.C.bold}finch orders daemon${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}- ticking every ${intervalMinutes}m, Ctrl+C to stop${cli_ui_js_1.C.reset}`);
|
|
129
|
+
console.log(` ${cli_ui_js_1.C.dim}Kill-switch: RH_ORDERS_DISABLED=1 or create ~/.finch/rh-orders.OFF${cli_ui_js_1.C.reset}`);
|
|
130
|
+
console.log(` ${cli_ui_js_1.C.dim}Note: this only runs while this process stays alive - for something that${cli_ui_js_1.C.reset}`);
|
|
131
|
+
console.log(` ${cli_ui_js_1.C.dim}survives reboots/terminal closure, use \`finch orders install-scheduler\` instead.${cli_ui_js_1.C.reset}\n`);
|
|
132
|
+
let running = false; // skip a tick if the previous one is still in flight (slow RPC), don't queue up
|
|
133
|
+
const tick = async () => {
|
|
134
|
+
if (running)
|
|
135
|
+
return;
|
|
136
|
+
running = true;
|
|
137
|
+
const ts = new Date().toISOString();
|
|
138
|
+
try {
|
|
139
|
+
const { text } = await runOrdersTick(true);
|
|
140
|
+
console.log(` ${cli_ui_js_1.C.dim}[${ts}]${cli_ui_js_1.C.reset}`);
|
|
141
|
+
console.log(text.split("\n").map((l) => ` ${l}`).join("\n") + "\n");
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
console.error(` ${cli_ui_js_1.C.red}[${ts}] tick error: ${err.message ?? err}${cli_ui_js_1.C.reset}\n`);
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
running = false;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
await tick();
|
|
151
|
+
const timer = setInterval(tick, intervalMinutes * 60000);
|
|
152
|
+
const shutdown = () => {
|
|
153
|
+
clearInterval(timer);
|
|
154
|
+
console.log(`\n ${cli_ui_js_1.C.dim}Stopped.${cli_ui_js_1.C.reset}\n`);
|
|
155
|
+
process.exit(0);
|
|
156
|
+
};
|
|
157
|
+
process.on("SIGINT", shutdown);
|
|
158
|
+
process.on("SIGTERM", shutdown);
|
|
159
|
+
return; // process stays alive via the interval timer
|
|
160
|
+
}
|
|
161
|
+
if (sub === "install-scheduler") {
|
|
162
|
+
installOrdersScheduler();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
console.log(`
|
|
166
|
+
${cli_ui_js_1.C.cyan}${cli_ui_js_1.C.bold}finch orders${cli_ui_js_1.C.reset} ${cli_ui_js_1.C.dim}- Robinhood Chain DCA/TP-SL orders (rh_dca_create/rh_bracket_create)${cli_ui_js_1.C.reset}
|
|
167
|
+
${cli_ui_js_1.C.dim}do nothing on their own until one of these is running - creating an order${cli_ui_js_1.C.reset}
|
|
168
|
+
${cli_ui_js_1.C.dim}only ever writes it locally, nothing executes it automatically by itself.${cli_ui_js_1.C.reset}
|
|
169
|
+
|
|
170
|
+
${cli_ui_js_1.C.cyan}Commands:${cli_ui_js_1.C.reset}
|
|
171
|
+
finch orders tick [--execute] Run one tick now and exit (preview by default)
|
|
172
|
+
finch orders daemon [--interval N] Run in the foreground, ticking every N min (default 15)
|
|
173
|
+
finch orders install-scheduler Register an OS-level recurring task (Task Scheduler / cron)
|
|
174
|
+
`);
|
|
175
|
+
}
|