@spfunctions/cli 1.7.19 → 1.7.22
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/dist/101.index.js +1 -0
- package/dist/12.index.js +1 -0
- package/dist/160.index.js +1 -0
- package/dist/174.index.js +1 -0
- package/dist/278.index.js +6 -0
- package/dist/582.index.js +1 -0
- package/dist/641.index.js +324 -0
- package/dist/669.index.js +1 -0
- package/dist/722.index.js +1 -0
- package/dist/788.index.js +1 -0
- package/dist/816.index.js +12 -0
- package/dist/830.index.js +1 -0
- package/dist/921.index.js +1 -0
- package/dist/index.js +1 -833
- package/package.json +5 -2
- package/dist/cache.d.ts +0 -6
- package/dist/cache.js +0 -31
- package/dist/cache.test.d.ts +0 -1
- package/dist/cache.test.js +0 -73
- package/dist/client.d.ts +0 -56
- package/dist/client.js +0 -205
- package/dist/client.test.d.ts +0 -1
- package/dist/client.test.js +0 -89
- package/dist/commands/agent.d.ts +0 -20
- package/dist/commands/agent.js +0 -4119
- package/dist/commands/announcements.d.ts +0 -3
- package/dist/commands/announcements.js +0 -28
- package/dist/commands/augment.d.ts +0 -12
- package/dist/commands/augment.js +0 -56
- package/dist/commands/balance.d.ts +0 -3
- package/dist/commands/balance.js +0 -17
- package/dist/commands/book.d.ts +0 -17
- package/dist/commands/book.js +0 -220
- package/dist/commands/cancel.d.ts +0 -5
- package/dist/commands/cancel.js +0 -41
- package/dist/commands/context.d.ts +0 -6
- package/dist/commands/context.js +0 -208
- package/dist/commands/create.d.ts +0 -7
- package/dist/commands/create.js +0 -42
- package/dist/commands/dashboard.d.ts +0 -14
- package/dist/commands/dashboard.js +0 -215
- package/dist/commands/delta.d.ts +0 -16
- package/dist/commands/delta.js +0 -115
- package/dist/commands/edges.d.ts +0 -26
- package/dist/commands/edges.js +0 -246
- package/dist/commands/evaluate.d.ts +0 -4
- package/dist/commands/evaluate.js +0 -30
- package/dist/commands/explore.d.ts +0 -14
- package/dist/commands/explore.js +0 -116
- package/dist/commands/feed.d.ts +0 -13
- package/dist/commands/feed.js +0 -73
- package/dist/commands/fills.d.ts +0 -4
- package/dist/commands/fills.js +0 -29
- package/dist/commands/forecast.d.ts +0 -4
- package/dist/commands/forecast.js +0 -53
- package/dist/commands/get.d.ts +0 -5
- package/dist/commands/get.js +0 -98
- package/dist/commands/heartbeat.d.ts +0 -20
- package/dist/commands/heartbeat.js +0 -73
- package/dist/commands/history.d.ts +0 -3
- package/dist/commands/history.js +0 -38
- package/dist/commands/liquidity.d.ts +0 -14
- package/dist/commands/liquidity.js +0 -378
- package/dist/commands/list.d.ts +0 -5
- package/dist/commands/list.js +0 -38
- package/dist/commands/login.d.ts +0 -10
- package/dist/commands/login.js +0 -98
- package/dist/commands/markets.d.ts +0 -10
- package/dist/commands/markets.js +0 -39
- package/dist/commands/milestones.d.ts +0 -8
- package/dist/commands/milestones.js +0 -56
- package/dist/commands/orders.d.ts +0 -4
- package/dist/commands/orders.js +0 -28
- package/dist/commands/performance.d.ts +0 -11
- package/dist/commands/performance.js +0 -250
- package/dist/commands/positions.d.ts +0 -19
- package/dist/commands/positions.js +0 -294
- package/dist/commands/prompt.d.ts +0 -13
- package/dist/commands/prompt.js +0 -35
- package/dist/commands/publish.d.ts +0 -15
- package/dist/commands/publish.js +0 -39
- package/dist/commands/query.d.ts +0 -15
- package/dist/commands/query.js +0 -132
- package/dist/commands/rfq.d.ts +0 -5
- package/dist/commands/rfq.js +0 -35
- package/dist/commands/scan.d.ts +0 -11
- package/dist/commands/scan.js +0 -230
- package/dist/commands/schedule.d.ts +0 -3
- package/dist/commands/schedule.js +0 -38
- package/dist/commands/settlements.d.ts +0 -6
- package/dist/commands/settlements.js +0 -50
- package/dist/commands/setup.d.ts +0 -24
- package/dist/commands/setup.js +0 -700
- package/dist/commands/signal.d.ts +0 -6
- package/dist/commands/signal.js +0 -32
- package/dist/commands/strategies.d.ts +0 -11
- package/dist/commands/strategies.js +0 -130
- package/dist/commands/telegram.d.ts +0 -15
- package/dist/commands/telegram.js +0 -125
- package/dist/commands/trade.d.ts +0 -12
- package/dist/commands/trade.js +0 -112
- package/dist/commands/watch.d.ts +0 -19
- package/dist/commands/watch.js +0 -157
- package/dist/commands/whatif.d.ts +0 -17
- package/dist/commands/whatif.js +0 -209
- package/dist/commands/x.d.ts +0 -28
- package/dist/commands/x.js +0 -167
- package/dist/config.d.ts +0 -55
- package/dist/config.js +0 -139
- package/dist/config.test.d.ts +0 -1
- package/dist/config.test.js +0 -138
- package/dist/index.d.ts +0 -20
- package/dist/kalshi.d.ts +0 -144
- package/dist/kalshi.js +0 -498
- package/dist/polymarket.d.ts +0 -237
- package/dist/polymarket.js +0 -353
- package/dist/polymarket.test.d.ts +0 -1
- package/dist/polymarket.test.js +0 -424
- package/dist/share.d.ts +0 -4
- package/dist/share.js +0 -27
- package/dist/skills/loader.d.ts +0 -19
- package/dist/skills/loader.js +0 -86
- package/dist/telegram/agent-bridge.d.ts +0 -15
- package/dist/telegram/agent-bridge.js +0 -573
- package/dist/telegram/bot.d.ts +0 -10
- package/dist/telegram/bot.js +0 -297
- package/dist/telegram/commands.d.ts +0 -11
- package/dist/telegram/commands.js +0 -120
- package/dist/telegram/format.d.ts +0 -11
- package/dist/telegram/format.js +0 -51
- package/dist/telegram/format.test.d.ts +0 -1
- package/dist/telegram/format.test.js +0 -73
- package/dist/telegram/poller.d.ts +0 -6
- package/dist/telegram/poller.js +0 -32
- package/dist/topics.d.ts +0 -17
- package/dist/topics.js +0 -102
- package/dist/topics.test.d.ts +0 -1
- package/dist/topics.test.js +0 -131
- package/dist/tui/border.d.ts +0 -33
- package/dist/tui/border.js +0 -87
- package/dist/tui/chart.d.ts +0 -19
- package/dist/tui/chart.js +0 -117
- package/dist/tui/dashboard.d.ts +0 -9
- package/dist/tui/dashboard.js +0 -814
- package/dist/tui/layout.d.ts +0 -16
- package/dist/tui/layout.js +0 -41
- package/dist/tui/screen.d.ts +0 -33
- package/dist/tui/screen.js +0 -102
- package/dist/tui/state.d.ts +0 -40
- package/dist/tui/state.js +0 -36
- package/dist/tui/widgets/commandbar.d.ts +0 -8
- package/dist/tui/widgets/commandbar.js +0 -82
- package/dist/tui/widgets/detail.d.ts +0 -9
- package/dist/tui/widgets/detail.js +0 -151
- package/dist/tui/widgets/edges.d.ts +0 -4
- package/dist/tui/widgets/edges.js +0 -34
- package/dist/tui/widgets/liquidity.d.ts +0 -9
- package/dist/tui/widgets/liquidity.js +0 -142
- package/dist/tui/widgets/orders.d.ts +0 -4
- package/dist/tui/widgets/orders.js +0 -37
- package/dist/tui/widgets/portfolio.d.ts +0 -4
- package/dist/tui/widgets/portfolio.js +0 -59
- package/dist/tui/widgets/signals.d.ts +0 -4
- package/dist/tui/widgets/signals.js +0 -31
- package/dist/tui/widgets/statusbar.d.ts +0 -8
- package/dist/tui/widgets/statusbar.js +0 -72
- package/dist/tui/widgets/thesis.d.ts +0 -4
- package/dist/tui/widgets/thesis.js +0 -66
- package/dist/tui/widgets/trade.d.ts +0 -9
- package/dist/tui/widgets/trade.js +0 -117
- package/dist/tui/widgets/upcoming.d.ts +0 -4
- package/dist/tui/widgets/upcoming.js +0 -41
- package/dist/tui/widgets/whatif.d.ts +0 -7
- package/dist/tui/widgets/whatif.js +0 -113
- package/dist/types/output.d.ts +0 -412
- package/dist/types/output.js +0 -9
- package/dist/utils.d.ts +0 -52
- package/dist/utils.js +0 -146
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -111
package/dist/commands/setup.js
DELETED
|
@@ -1,700 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* sf setup — Interactive configuration wizard
|
|
4
|
-
*
|
|
5
|
-
* Walks user through:
|
|
6
|
-
* 1. SF API key (required)
|
|
7
|
-
* 2. OpenRouter API key (optional, for agent)
|
|
8
|
-
* 3. Kalshi exchange credentials (optional, for positions)
|
|
9
|
-
* 4. Tavily API key (optional, for web search)
|
|
10
|
-
* 5. First thesis creation (if none exist)
|
|
11
|
-
*
|
|
12
|
-
* Each key is validated in real-time.
|
|
13
|
-
* Config is saved to ~/.sf/config.json.
|
|
14
|
-
*/
|
|
15
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
-
};
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.setupCommand = setupCommand;
|
|
20
|
-
const readline_1 = __importDefault(require("readline"));
|
|
21
|
-
const child_process_1 = require("child_process");
|
|
22
|
-
const config_js_1 = require("../config.js");
|
|
23
|
-
const client_js_1 = require("../client.js");
|
|
24
|
-
const kalshi_js_1 = require("../kalshi.js");
|
|
25
|
-
const agent_js_1 = require("./agent.js");
|
|
26
|
-
// ─── ANSI helpers ────────────────────────────────────────────────────────────
|
|
27
|
-
const green = (s) => `\x1b[32m${s}\x1b[39m`;
|
|
28
|
-
const red = (s) => `\x1b[31m${s}\x1b[39m`;
|
|
29
|
-
const dim = (s) => `\x1b[2m${s}\x1b[22m`;
|
|
30
|
-
const bold = (s) => `\x1b[1m${s}\x1b[22m`;
|
|
31
|
-
const cyan = (s) => `\x1b[36m${s}\x1b[39m`;
|
|
32
|
-
function ok(msg) { console.log(` ${green('✓')} ${msg}`); }
|
|
33
|
-
function fail(msg) { console.log(` ${red('✗')} ${msg}`); }
|
|
34
|
-
function info(msg) { console.log(` ${msg}`); }
|
|
35
|
-
function blank() { console.log(); }
|
|
36
|
-
// ─── Prompt helper ───────────────────────────────────────────────────────────
|
|
37
|
-
function prompt(question) {
|
|
38
|
-
const rl = readline_1.default.createInterface({
|
|
39
|
-
input: process.stdin,
|
|
40
|
-
output: process.stdout,
|
|
41
|
-
terminal: true,
|
|
42
|
-
});
|
|
43
|
-
return new Promise(resolve => {
|
|
44
|
-
rl.question(question, answer => {
|
|
45
|
-
rl.close();
|
|
46
|
-
resolve(answer.trim());
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
function promptYN(question, defaultYes = true) {
|
|
51
|
-
return prompt(question).then(ans => {
|
|
52
|
-
if (!ans)
|
|
53
|
-
return defaultYes;
|
|
54
|
-
return ans.toLowerCase().startsWith('y');
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
function openBrowser(url) {
|
|
58
|
-
const cmd = process.platform === 'darwin' ? 'open' :
|
|
59
|
-
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
60
|
-
(0, child_process_1.exec)(`${cmd} ${url}`);
|
|
61
|
-
}
|
|
62
|
-
function mask(s) {
|
|
63
|
-
if (!s || s.length <= 12)
|
|
64
|
-
return s;
|
|
65
|
-
return s.slice(0, 8) + '...' + s.slice(-4);
|
|
66
|
-
}
|
|
67
|
-
// ─── Validators ──────────────────────────────────────────────────────────────
|
|
68
|
-
async function validateSFKey(key, apiUrl) {
|
|
69
|
-
try {
|
|
70
|
-
const res = await fetch(`${apiUrl}/api/thesis`, {
|
|
71
|
-
headers: { 'Authorization': `Bearer ${key}` },
|
|
72
|
-
});
|
|
73
|
-
if (res.ok)
|
|
74
|
-
return { valid: true, msg: `API key valid — connected to ${apiUrl.replace('https://', '')}` };
|
|
75
|
-
if (res.status === 401)
|
|
76
|
-
return { valid: false, msg: 'Invalid key, please try again' };
|
|
77
|
-
return { valid: false, msg: `Server returned ${res.status}` };
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
return { valid: false, msg: `Connection failed: ${err.message}` };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async function validateOpenRouterKey(key) {
|
|
84
|
-
try {
|
|
85
|
-
const res = await fetch('https://openrouter.ai/api/v1/models', {
|
|
86
|
-
headers: { 'Authorization': `Bearer ${key}` },
|
|
87
|
-
});
|
|
88
|
-
if (res.ok)
|
|
89
|
-
return { valid: true, msg: 'OpenRouter connected — available model: claude-sonnet-4.6' };
|
|
90
|
-
return { valid: false, msg: `OpenRouter returned ${res.status}` };
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
return { valid: false, msg: `Connection failed: ${err.message}` };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async function validateKalshi() {
|
|
97
|
-
try {
|
|
98
|
-
const positions = await (0, kalshi_js_1.getPositions)();
|
|
99
|
-
if (positions === null)
|
|
100
|
-
return { valid: false, msg: 'Kalshi authentication failed', posCount: 0 };
|
|
101
|
-
return { valid: true, msg: `Kalshi authenticated — found ${positions.length} position(s)`, posCount: positions.length };
|
|
102
|
-
}
|
|
103
|
-
catch (err) {
|
|
104
|
-
return { valid: false, msg: `Kalshi connection failed: ${err.message}`, posCount: 0 };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async function validateTavily(key) {
|
|
108
|
-
try {
|
|
109
|
-
const res = await fetch('https://api.tavily.com/search', {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: { 'Content-Type': 'application/json' },
|
|
112
|
-
body: JSON.stringify({ api_key: key, query: 'test', max_results: 1 }),
|
|
113
|
-
});
|
|
114
|
-
if (res.ok)
|
|
115
|
-
return { valid: true, msg: 'Tavily connected' };
|
|
116
|
-
return { valid: false, msg: `Tavily returned ${res.status}` };
|
|
117
|
-
}
|
|
118
|
-
catch (err) {
|
|
119
|
-
return { valid: false, msg: `Connection failed: ${err.message}` };
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
async function setupCommand(opts) {
|
|
123
|
-
// ── sf setup --check ──────────────────────────────────────────────────────
|
|
124
|
-
if (opts.check) {
|
|
125
|
-
return showCheck();
|
|
126
|
-
}
|
|
127
|
-
// ── sf setup --reset ──────────────────────────────────────────────────────
|
|
128
|
-
if (opts.reset) {
|
|
129
|
-
(0, config_js_1.resetConfig)();
|
|
130
|
-
ok('Config reset');
|
|
131
|
-
blank();
|
|
132
|
-
info('Run sf setup to reconfigure');
|
|
133
|
-
blank();
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
// ── sf setup --key <key> (non-interactive) ────────────────────────────────
|
|
137
|
-
if (opts.key) {
|
|
138
|
-
const apiUrl = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
139
|
-
const result = await validateSFKey(opts.key, apiUrl);
|
|
140
|
-
if (!result.valid) {
|
|
141
|
-
fail(result.msg);
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
const existing = (0, config_js_1.loadFileConfig)();
|
|
145
|
-
(0, config_js_1.saveConfig)({ ...existing, apiKey: opts.key, apiUrl });
|
|
146
|
-
ok(result.msg);
|
|
147
|
-
ok(`Saved to ${(0, config_js_1.getConfigPath)()}`);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
// ── sf setup --kalshi (reconfigure Kalshi credentials) ──────────────────
|
|
151
|
-
if (opts.kalshi) {
|
|
152
|
-
const existing = (0, config_js_1.loadFileConfig)();
|
|
153
|
-
blank();
|
|
154
|
-
console.log(` ${bold('Reconfigure Kalshi Credentials')}`);
|
|
155
|
-
blank();
|
|
156
|
-
info('Go to https://kalshi.com/account/api-keys to generate a new API key.');
|
|
157
|
-
info('If you need trading, make sure to enable read+write permissions.');
|
|
158
|
-
blank();
|
|
159
|
-
await promptForKalshi(existing);
|
|
160
|
-
(0, config_js_1.saveConfig)(existing);
|
|
161
|
-
if (existing.kalshiKeyId) {
|
|
162
|
-
process.env.KALSHI_API_KEY_ID = existing.kalshiKeyId;
|
|
163
|
-
process.env.KALSHI_PRIVATE_KEY_PATH = existing.kalshiPrivateKeyPath;
|
|
164
|
-
}
|
|
165
|
-
blank();
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
// ── sf setup --polymarket (reconfigure Polymarket credentials) ──────────
|
|
169
|
-
if (opts.polymarket) {
|
|
170
|
-
const existing = (0, config_js_1.loadFileConfig)();
|
|
171
|
-
blank();
|
|
172
|
-
console.log(` ${bold('Reconfigure Polymarket Credentials')}`);
|
|
173
|
-
blank();
|
|
174
|
-
await promptForPolymarket(existing);
|
|
175
|
-
(0, config_js_1.saveConfig)(existing);
|
|
176
|
-
blank();
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
// ── sf setup --enable-trading / --disable-trading ────────────────────────
|
|
180
|
-
if (opts.enableTrading) {
|
|
181
|
-
const existing = (0, config_js_1.loadFileConfig)();
|
|
182
|
-
(0, config_js_1.saveConfig)({ ...existing, tradingEnabled: true });
|
|
183
|
-
ok('Trading enabled. sf buy / sf sell / sf cancel now available.');
|
|
184
|
-
blank();
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
if (opts.disableTrading) {
|
|
188
|
-
const existing = (0, config_js_1.loadFileConfig)();
|
|
189
|
-
(0, config_js_1.saveConfig)({ ...existing, tradingEnabled: false });
|
|
190
|
-
ok('Trading disabled.');
|
|
191
|
-
blank();
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
// ── Full interactive wizard ───────────────────────────────────────────────
|
|
195
|
-
return runWizard();
|
|
196
|
-
}
|
|
197
|
-
// ─── Check command ───────────────────────────────────────────────────────────
|
|
198
|
-
async function showCheck() {
|
|
199
|
-
const config = (0, config_js_1.loadConfig)();
|
|
200
|
-
blank();
|
|
201
|
-
console.log(` ${bold('SimpleFunctions Config Status')}`);
|
|
202
|
-
console.log(` ${dim('─'.repeat(35))}`);
|
|
203
|
-
blank();
|
|
204
|
-
// SF API Key
|
|
205
|
-
if (config.apiKey) {
|
|
206
|
-
ok(`SF_API_KEY ${dim(mask(config.apiKey))}`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
fail('SF_API_KEY not configured (required)');
|
|
210
|
-
}
|
|
211
|
-
// OpenRouter
|
|
212
|
-
if (config.openrouterKey) {
|
|
213
|
-
ok(`OPENROUTER ${dim(mask(config.openrouterKey))} (direct)`);
|
|
214
|
-
}
|
|
215
|
-
else if (config.apiKey) {
|
|
216
|
-
ok(`OPENROUTER ${dim('proxied via SimpleFunctions')}`);
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
fail(`OPENROUTER not configured (run sf login or set key)`);
|
|
220
|
-
}
|
|
221
|
-
// Kalshi
|
|
222
|
-
if (config.kalshiKeyId && config.kalshiPrivateKeyPath) {
|
|
223
|
-
ok(`KALSHI ${dim(mask(config.kalshiKeyId))}`);
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
info(`${dim('○')} KALSHI ${dim('skipped')}`);
|
|
227
|
-
}
|
|
228
|
-
// Polymarket
|
|
229
|
-
if (config.polymarketWalletAddress) {
|
|
230
|
-
ok(`POLYMARKET ${dim(mask(config.polymarketWalletAddress))}`);
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
info(`${dim('○')} POLYMARKET ${dim('skipped')}`);
|
|
234
|
-
}
|
|
235
|
-
// Tavily
|
|
236
|
-
if (config.tavilyKey) {
|
|
237
|
-
ok(`TAVILY ${dim(mask(config.tavilyKey))} (direct)`);
|
|
238
|
-
}
|
|
239
|
-
else if (config.apiKey) {
|
|
240
|
-
ok(`TAVILY ${dim('proxied via SimpleFunctions')}`);
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
info(`${dim('○')} TAVILY ${dim('skipped')}`);
|
|
244
|
-
}
|
|
245
|
-
// Trading
|
|
246
|
-
if (config.tradingEnabled) {
|
|
247
|
-
ok('TRADING enabled');
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
info(`${dim('○')} TRADING ${dim('disabled — sf setup --enable-trading')}`);
|
|
251
|
-
}
|
|
252
|
-
blank();
|
|
253
|
-
console.log(` ${dim('Config file: ' + (0, config_js_1.getConfigPath)())}`);
|
|
254
|
-
blank();
|
|
255
|
-
}
|
|
256
|
-
// ─── Interactive Wizard ──────────────────────────────────────────────────────
|
|
257
|
-
async function runWizard() {
|
|
258
|
-
blank();
|
|
259
|
-
console.log(` ${bold('SimpleFunctions Setup')}`);
|
|
260
|
-
console.log(` ${dim('─'.repeat(25))}`);
|
|
261
|
-
blank();
|
|
262
|
-
const config = (0, config_js_1.loadFileConfig)();
|
|
263
|
-
const apiUrl = config.apiUrl || 'https://simplefunctions.dev';
|
|
264
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
265
|
-
// Step 1: SF API Key
|
|
266
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
267
|
-
console.log(` ${bold('Step 1: API Key')}`);
|
|
268
|
-
blank();
|
|
269
|
-
const existingSfKey = process.env.SF_API_KEY || config.apiKey;
|
|
270
|
-
if (existingSfKey) {
|
|
271
|
-
const result = await validateSFKey(existingSfKey, apiUrl);
|
|
272
|
-
if (result.valid) {
|
|
273
|
-
ok(`Detected SF_API_KEY — ${dim(mask(existingSfKey))}`);
|
|
274
|
-
info(dim('Skipping.'));
|
|
275
|
-
config.apiKey = existingSfKey;
|
|
276
|
-
blank();
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
fail(`Existing key invalid: ${result.msg}`);
|
|
280
|
-
config.apiKey = await promptForSFKey(apiUrl);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
config.apiKey = await promptForSFKey(apiUrl);
|
|
285
|
-
}
|
|
286
|
-
// Save after each step (so partial progress is preserved)
|
|
287
|
-
config.apiUrl = apiUrl;
|
|
288
|
-
(0, config_js_1.saveConfig)(config);
|
|
289
|
-
// Also apply so subsequent validation calls can use it
|
|
290
|
-
process.env.SF_API_KEY = config.apiKey;
|
|
291
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
292
|
-
// Step 2: OpenRouter API Key
|
|
293
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
294
|
-
console.log(` ${bold('Step 2: AI Model (for sf agent)')}`);
|
|
295
|
-
blank();
|
|
296
|
-
const existingOrKey = process.env.OPENROUTER_API_KEY || config.openrouterKey;
|
|
297
|
-
if (existingOrKey) {
|
|
298
|
-
const result = await validateOpenRouterKey(existingOrKey);
|
|
299
|
-
if (result.valid) {
|
|
300
|
-
ok(`Detected OPENROUTER_API_KEY — ${dim(mask(existingOrKey))}`);
|
|
301
|
-
info(dim('Skipping.'));
|
|
302
|
-
config.openrouterKey = existingOrKey;
|
|
303
|
-
blank();
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
fail(`Existing key invalid: ${result.msg}`);
|
|
307
|
-
config.openrouterKey = await promptForOpenRouterKey();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
config.openrouterKey = await promptForOpenRouterKey();
|
|
312
|
-
}
|
|
313
|
-
(0, config_js_1.saveConfig)(config);
|
|
314
|
-
if (config.openrouterKey)
|
|
315
|
-
process.env.OPENROUTER_API_KEY = config.openrouterKey;
|
|
316
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
317
|
-
// Step 3: Kalshi Exchange
|
|
318
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
319
|
-
console.log(` ${bold('Step 3: Kalshi Exchange (optional)')}`);
|
|
320
|
-
blank();
|
|
321
|
-
const existingKalshiId = process.env.KALSHI_API_KEY_ID || config.kalshiKeyId;
|
|
322
|
-
const existingKalshiPath = process.env.KALSHI_PRIVATE_KEY_PATH || config.kalshiPrivateKeyPath;
|
|
323
|
-
if (existingKalshiId && existingKalshiPath) {
|
|
324
|
-
// Temporarily apply for validation
|
|
325
|
-
process.env.KALSHI_API_KEY_ID = existingKalshiId;
|
|
326
|
-
process.env.KALSHI_PRIVATE_KEY_PATH = existingKalshiPath;
|
|
327
|
-
const result = await validateKalshi();
|
|
328
|
-
if (result.valid) {
|
|
329
|
-
ok(`Detected Kalshi — ${dim(mask(existingKalshiId))} (${result.posCount} position(s))`);
|
|
330
|
-
info(dim('Skipping.'));
|
|
331
|
-
config.kalshiKeyId = existingKalshiId;
|
|
332
|
-
config.kalshiPrivateKeyPath = existingKalshiPath;
|
|
333
|
-
blank();
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
fail(`Existing credentials invalid: ${result.msg}`);
|
|
337
|
-
await promptForKalshi(config);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
else {
|
|
341
|
-
await promptForKalshi(config);
|
|
342
|
-
}
|
|
343
|
-
(0, config_js_1.saveConfig)(config);
|
|
344
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
345
|
-
// Step 4: Polymarket
|
|
346
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
347
|
-
console.log(` ${bold('Step 4: Polymarket (optional)')}`);
|
|
348
|
-
blank();
|
|
349
|
-
const existingPolyWallet = process.env.POLYMARKET_WALLET_ADDRESS || config.polymarketWalletAddress;
|
|
350
|
-
if (existingPolyWallet) {
|
|
351
|
-
ok(`Detected wallet — ${dim(mask(existingPolyWallet))}`);
|
|
352
|
-
info(dim('Skipping.'));
|
|
353
|
-
config.polymarketWalletAddress = existingPolyWallet;
|
|
354
|
-
blank();
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
await promptForPolymarket(config);
|
|
358
|
-
}
|
|
359
|
-
(0, config_js_1.saveConfig)(config);
|
|
360
|
-
if (config.polymarketWalletAddress)
|
|
361
|
-
process.env.POLYMARKET_WALLET_ADDRESS = config.polymarketWalletAddress;
|
|
362
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
363
|
-
// Step 5: Tavily
|
|
364
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
365
|
-
console.log(` ${bold('Step 5: News Search (optional)')}`);
|
|
366
|
-
blank();
|
|
367
|
-
const existingTavily = process.env.TAVILY_API_KEY || config.tavilyKey;
|
|
368
|
-
if (existingTavily) {
|
|
369
|
-
const result = await validateTavily(existingTavily);
|
|
370
|
-
if (result.valid) {
|
|
371
|
-
ok(`Detected TAVILY_API_KEY — ${dim(mask(existingTavily))}`);
|
|
372
|
-
info(dim('Skipping.'));
|
|
373
|
-
config.tavilyKey = existingTavily;
|
|
374
|
-
blank();
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
fail(`Existing key invalid: ${result.msg}`);
|
|
378
|
-
config.tavilyKey = await promptForTavily();
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
config.tavilyKey = await promptForTavily();
|
|
383
|
-
}
|
|
384
|
-
(0, config_js_1.saveConfig)(config);
|
|
385
|
-
if (config.tavilyKey)
|
|
386
|
-
process.env.TAVILY_API_KEY = config.tavilyKey;
|
|
387
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
388
|
-
// Step 5: Trading
|
|
389
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
390
|
-
if (config.kalshiKeyId) {
|
|
391
|
-
console.log(` ${bold('Step 6: Trading (optional)')}`);
|
|
392
|
-
blank();
|
|
393
|
-
info('Warning: enabling this unlocks sf buy / sf sell / sf cancel.');
|
|
394
|
-
info('Your Kalshi API key must have read+write permissions.');
|
|
395
|
-
blank();
|
|
396
|
-
const enableTrading = await promptYN(' Enable trading? (y/N) ', false);
|
|
397
|
-
config.tradingEnabled = enableTrading;
|
|
398
|
-
if (enableTrading) {
|
|
399
|
-
ok('Trading enabled');
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
info(dim('Skipped. You can enable later with sf setup --enable-trading.'));
|
|
403
|
-
}
|
|
404
|
-
blank();
|
|
405
|
-
(0, config_js_1.saveConfig)(config);
|
|
406
|
-
}
|
|
407
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
408
|
-
// Summary
|
|
409
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
410
|
-
console.log(` ${dim('─'.repeat(25))}`);
|
|
411
|
-
info(`Config saved to ${dim((0, config_js_1.getConfigPath)())}`);
|
|
412
|
-
blank();
|
|
413
|
-
if (config.apiKey)
|
|
414
|
-
ok('SF_API_KEY configured');
|
|
415
|
-
else
|
|
416
|
-
fail('SF_API_KEY not configured');
|
|
417
|
-
if (config.openrouterKey)
|
|
418
|
-
ok('OPENROUTER_KEY configured');
|
|
419
|
-
else
|
|
420
|
-
fail('OPENROUTER_KEY skipped');
|
|
421
|
-
if (config.kalshiKeyId)
|
|
422
|
-
ok('KALSHI configured');
|
|
423
|
-
else
|
|
424
|
-
info(`${dim('○')} KALSHI skipped`);
|
|
425
|
-
if (config.tavilyKey)
|
|
426
|
-
ok('TAVILY configured');
|
|
427
|
-
else
|
|
428
|
-
info(`${dim('○')} TAVILY skipped`);
|
|
429
|
-
blank();
|
|
430
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
431
|
-
// Step 5: Thesis creation
|
|
432
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
433
|
-
if (config.apiKey) {
|
|
434
|
-
await handleThesisStep(config);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
// ─── Step prompt helpers ─────────────────────────────────────────────────────
|
|
438
|
-
async function promptForSFKey(apiUrl) {
|
|
439
|
-
info(`Don't have a key? Sign up at ${cyan('https://simplefunctions.dev/dashboard')}.`);
|
|
440
|
-
info('Press Enter to open browser, or paste your key:');
|
|
441
|
-
blank();
|
|
442
|
-
while (true) {
|
|
443
|
-
const answer = await prompt(' > ');
|
|
444
|
-
if (!answer) {
|
|
445
|
-
// Open browser
|
|
446
|
-
openBrowser('https://simplefunctions.dev/dashboard');
|
|
447
|
-
info(dim('Browser opened. Paste your key here once you have it:'));
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
info(dim('Validating...'));
|
|
451
|
-
const result = await validateSFKey(answer, apiUrl);
|
|
452
|
-
if (result.valid) {
|
|
453
|
-
ok(result.msg);
|
|
454
|
-
blank();
|
|
455
|
-
return answer;
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
fail(result.msg);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
async function promptForOpenRouterKey() {
|
|
463
|
-
info(`Requires an OpenRouter API key. Get one at ${cyan('https://openrouter.ai/settings/keys')}.`);
|
|
464
|
-
info('Press Enter to skip (agent unavailable), or paste key:');
|
|
465
|
-
blank();
|
|
466
|
-
const answer = await prompt(' > ');
|
|
467
|
-
if (!answer) {
|
|
468
|
-
info(dim('Skipped.'));
|
|
469
|
-
blank();
|
|
470
|
-
return undefined;
|
|
471
|
-
}
|
|
472
|
-
info(dim('Validating...'));
|
|
473
|
-
const result = await validateOpenRouterKey(answer);
|
|
474
|
-
if (result.valid) {
|
|
475
|
-
ok(result.msg);
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
fail(result.msg);
|
|
479
|
-
info(dim('Saved. You can re-run sf setup later to fix this.'));
|
|
480
|
-
}
|
|
481
|
-
blank();
|
|
482
|
-
return answer;
|
|
483
|
-
}
|
|
484
|
-
async function promptForKalshi(config) {
|
|
485
|
-
info(`Connect Kalshi to view your positions and P&L.`);
|
|
486
|
-
info(`Requires an API Key ID and private key file.`);
|
|
487
|
-
info(`Get them at ${cyan('https://kalshi.com/account/api-keys')}.`);
|
|
488
|
-
info('Press Enter to skip, or paste Key ID:');
|
|
489
|
-
blank();
|
|
490
|
-
const keyId = await prompt(' > ');
|
|
491
|
-
if (!keyId) {
|
|
492
|
-
info(dim('Skipped.'));
|
|
493
|
-
blank();
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
info('Private key file path (default ~/.kalshi/private.pem):');
|
|
497
|
-
const keyPathInput = await prompt(' > ');
|
|
498
|
-
const keyPath = keyPathInput || '~/.kalshi/private.pem';
|
|
499
|
-
config.kalshiKeyId = keyId;
|
|
500
|
-
config.kalshiPrivateKeyPath = keyPath;
|
|
501
|
-
// Temporarily set for validation
|
|
502
|
-
process.env.KALSHI_API_KEY_ID = keyId;
|
|
503
|
-
process.env.KALSHI_PRIVATE_KEY_PATH = keyPath;
|
|
504
|
-
info(dim('Validating...'));
|
|
505
|
-
const result = await validateKalshi();
|
|
506
|
-
if (result.valid) {
|
|
507
|
-
ok(result.msg);
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
fail(result.msg);
|
|
511
|
-
info(dim('Saved. You can re-run sf setup later to fix this.'));
|
|
512
|
-
}
|
|
513
|
-
blank();
|
|
514
|
-
}
|
|
515
|
-
async function promptForTavily() {
|
|
516
|
-
info(`Tavily API powers the agent's web_search tool.`);
|
|
517
|
-
info(`Get a free key at ${cyan('https://tavily.com')}.`);
|
|
518
|
-
info('Press Enter to skip:');
|
|
519
|
-
blank();
|
|
520
|
-
const answer = await prompt(' > ');
|
|
521
|
-
if (!answer) {
|
|
522
|
-
info(dim('Skipped.'));
|
|
523
|
-
blank();
|
|
524
|
-
return undefined;
|
|
525
|
-
}
|
|
526
|
-
info(dim('Validating...'));
|
|
527
|
-
const result = await validateTavily(answer);
|
|
528
|
-
if (result.valid) {
|
|
529
|
-
ok(result.msg);
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
fail(result.msg);
|
|
533
|
-
info(dim('Saved. You can re-run sf setup later to fix this.'));
|
|
534
|
-
}
|
|
535
|
-
blank();
|
|
536
|
-
return answer;
|
|
537
|
-
}
|
|
538
|
-
async function promptForPolymarket(config) {
|
|
539
|
-
info('Connect Polymarket to view positions and scan orderbooks.');
|
|
540
|
-
info('Your Polygon wallet address is needed (starts with 0x...).');
|
|
541
|
-
info(`Find it at ${cyan('https://polymarket.com')} → Settings → Profile.`);
|
|
542
|
-
info('Press Enter to skip:');
|
|
543
|
-
blank();
|
|
544
|
-
const walletAddress = await prompt(' Wallet address > ');
|
|
545
|
-
if (!walletAddress) {
|
|
546
|
-
info(dim('Skipped.'));
|
|
547
|
-
blank();
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
if (!walletAddress.startsWith('0x') || walletAddress.length < 40) {
|
|
551
|
-
fail('Invalid wallet address (must start with 0x and be 42 characters)');
|
|
552
|
-
info(dim('Saved anyway. You can fix it later with sf setup.'));
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
ok(`Wallet: ${mask(walletAddress)}`);
|
|
556
|
-
}
|
|
557
|
-
config.polymarketWalletAddress = walletAddress;
|
|
558
|
-
// Optionally configure private key for future trading
|
|
559
|
-
info(dim('Private key (for future trading) — press Enter to skip:'));
|
|
560
|
-
const keyPath = await prompt(' Key path > ');
|
|
561
|
-
if (keyPath) {
|
|
562
|
-
config.polymarketPrivateKeyPath = keyPath;
|
|
563
|
-
ok(`Private key path: ${dim(keyPath)}`);
|
|
564
|
-
}
|
|
565
|
-
blank();
|
|
566
|
-
}
|
|
567
|
-
// ─── Step 7: Thesis ──────────────────────────────────────────────────────────
|
|
568
|
-
async function handleThesisStep(config) {
|
|
569
|
-
try {
|
|
570
|
-
const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
|
|
571
|
-
const data = await client.listTheses();
|
|
572
|
-
const theses = data.theses || [];
|
|
573
|
-
const activeTheses = theses.filter((t) => t.status === 'active');
|
|
574
|
-
if (activeTheses.length > 0) {
|
|
575
|
-
console.log(` ${bold('Step 7: Theses')}`);
|
|
576
|
-
blank();
|
|
577
|
-
ok(`Found ${activeTheses.length} active thesis(es):`);
|
|
578
|
-
for (const t of activeTheses.slice(0, 5)) {
|
|
579
|
-
const conf = typeof t.confidence === 'number' ? Math.round(t.confidence * 100) : 0;
|
|
580
|
-
const thesis = (t.rawThesis || t.thesis || t.title || '').slice(0, 60);
|
|
581
|
-
info(` ${dim(t.id.slice(0, 8))} — ${thesis} — ${conf}%`);
|
|
582
|
-
}
|
|
583
|
-
info(dim('Skipping creation.'));
|
|
584
|
-
blank();
|
|
585
|
-
// Offer to launch agent
|
|
586
|
-
if (config.openrouterKey) {
|
|
587
|
-
console.log(` ${dim('─'.repeat(25))}`);
|
|
588
|
-
console.log(` ${bold('All set!')}`);
|
|
589
|
-
blank();
|
|
590
|
-
info(` ${cyan('sf agent')} Chat with your thesis`);
|
|
591
|
-
info(` ${cyan('sf context <id>')} View thesis snapshot`);
|
|
592
|
-
info(` ${cyan('sf positions')} View positions`);
|
|
593
|
-
info(` ${cyan('sf setup --check')} Check config`);
|
|
594
|
-
blank();
|
|
595
|
-
const shouldLaunch = await promptYN(` Launch agent now? (Y/n) `);
|
|
596
|
-
if (shouldLaunch) {
|
|
597
|
-
blank();
|
|
598
|
-
info('Launching...');
|
|
599
|
-
blank();
|
|
600
|
-
await (0, agent_js_1.agentCommand)(activeTheses[0].id, { model: config.model });
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
else {
|
|
604
|
-
blank();
|
|
605
|
-
console.log(` ${bold('All set!')}`);
|
|
606
|
-
blank();
|
|
607
|
-
info(` ${cyan('sf list')} List all theses`);
|
|
608
|
-
info(` ${cyan('sf context <id>')} View thesis snapshot`);
|
|
609
|
-
info(` ${cyan('sf positions')} View positions`);
|
|
610
|
-
info(` ${cyan('sf setup --check')} Check config`);
|
|
611
|
-
blank();
|
|
612
|
-
}
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
// No theses — offer to create one
|
|
616
|
-
console.log(` ${bold('Step 7: Create Your First Thesis')}`);
|
|
617
|
-
blank();
|
|
618
|
-
info('A thesis is your core market conviction. The system builds a causal model');
|
|
619
|
-
info('from it, then continuously scans prediction markets for mispriced contracts.');
|
|
620
|
-
blank();
|
|
621
|
-
info('Examples:');
|
|
622
|
-
info(` ${dim('"The Fed won\'t cut rates in 2026 — inflation stays elevated due to oil prices"')}`);
|
|
623
|
-
info(` ${dim('"AI-driven layoffs cause consumer spending to contract, S&P drops 20% by year-end"')}`);
|
|
624
|
-
info(` ${dim('"Trump can\'t exit the Iran conflict — oil stays above $100 for six months"')}`);
|
|
625
|
-
blank();
|
|
626
|
-
const thesis = await prompt(' Enter your thesis (press Enter to skip, use sf create later):\n > ');
|
|
627
|
-
if (!thesis) {
|
|
628
|
-
blank();
|
|
629
|
-
info(dim('Skipped. Use sf create "your thesis" to create one later.'));
|
|
630
|
-
blank();
|
|
631
|
-
showFinalHints(config);
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
blank();
|
|
635
|
-
info('Building causal model... (~30s)');
|
|
636
|
-
blank();
|
|
637
|
-
try {
|
|
638
|
-
const result = await client.createThesis(thesis, true);
|
|
639
|
-
if (result.id) {
|
|
640
|
-
const nodeCount = result.causalTree?.nodes?.length || 0;
|
|
641
|
-
const edgeCount = result.edgeAnalysis?.edges?.length || 0;
|
|
642
|
-
const totalMarkets = result.edgeAnalysis?.totalMarketsAnalyzed || 0;
|
|
643
|
-
const confidence = Math.round((parseFloat(result.confidence) || 0.5) * 100);
|
|
644
|
-
ok(`Causal tree: ${nodeCount} node(s)`);
|
|
645
|
-
ok(`Scanned ${totalMarkets} markets, found ${edgeCount} contract(s) with edge`);
|
|
646
|
-
ok(`Confidence: ${confidence}%`);
|
|
647
|
-
ok(`Thesis ID: ${result.id.slice(0, 8)}`);
|
|
648
|
-
blank();
|
|
649
|
-
// Offer to launch agent
|
|
650
|
-
if (config.openrouterKey) {
|
|
651
|
-
console.log(` ${dim('─'.repeat(25))}`);
|
|
652
|
-
console.log(` ${bold('All set!')}`);
|
|
653
|
-
blank();
|
|
654
|
-
const shouldLaunch = await promptYN(` Launch agent now? (Y/n) `);
|
|
655
|
-
if (shouldLaunch) {
|
|
656
|
-
blank();
|
|
657
|
-
info('Launching...');
|
|
658
|
-
blank();
|
|
659
|
-
await (0, agent_js_1.agentCommand)(result.id, { model: config.model });
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
blank();
|
|
663
|
-
showFinalHints(config);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
showFinalHints(config);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
fail(`Creation failed: ${result.error || 'unknown error'}`);
|
|
672
|
-
info(dim('You can retry later with sf create "your thesis"'));
|
|
673
|
-
blank();
|
|
674
|
-
showFinalHints(config);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
catch (err) {
|
|
678
|
-
fail(`Creation failed: ${err.message}`);
|
|
679
|
-
info(dim('You can retry later with sf create "your thesis"'));
|
|
680
|
-
blank();
|
|
681
|
-
showFinalHints(config);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
catch {
|
|
685
|
-
// Can't connect to API, skip thesis step
|
|
686
|
-
blank();
|
|
687
|
-
showFinalHints(config);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
function showFinalHints(config) {
|
|
691
|
-
console.log(` ${dim('─'.repeat(25))}`);
|
|
692
|
-
console.log(` ${bold('All set!')}`);
|
|
693
|
-
blank();
|
|
694
|
-
info(` ${cyan('sf agent')} Chat with your thesis`);
|
|
695
|
-
info(` ${cyan('sf list')} List all theses`);
|
|
696
|
-
info(` ${cyan('sf context <id>')} View thesis snapshot`);
|
|
697
|
-
info(` ${cyan('sf positions')} View positions`);
|
|
698
|
-
info(` ${cyan('sf setup --check')} Check config`);
|
|
699
|
-
blank();
|
|
700
|
-
}
|