@exagent/agent 0.3.2 → 0.3.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/dist/{chunk-VDK4XPAC.js → chunk-WTECTX2Z.js} +31 -4
- package/dist/cli.js +212 -77
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +122 -3
- package/src/config.ts +19 -2
- package/src/runtime.ts +18 -2
- package/src/setup.ts +118 -67
- package/dist/chunk-25J5ZDKX.js +0 -5622
- package/dist/chunk-2ML4XS5X.js +0 -5626
- package/dist/chunk-2OKYNZ3J.js +0 -5622
- package/dist/chunk-2PAASZJN.js +0 -5132
- package/dist/chunk-2WAYVOLU.js +0 -5624
- package/dist/chunk-2XBVVY3I.js +0 -5621
- package/dist/chunk-37HCPVBZ.js +0 -2941
- package/dist/chunk-3DZAZBLF.js +0 -5652
- package/dist/chunk-3IXCKNSV.js +0 -5008
- package/dist/chunk-3MXFTRXB.js +0 -3326
- package/dist/chunk-4MURFLNJ.js +0 -5136
- package/dist/chunk-4UVMO6ZM.js +0 -6318
- package/dist/chunk-56YQROFU.js +0 -5639
- package/dist/chunk-5B3ZEGMD.js +0 -4330
- package/dist/chunk-5NU6FDDE.js +0 -6020
- package/dist/chunk-5WADKPJU.js +0 -1552
- package/dist/chunk-6LOIFEEV.js +0 -4895
- package/dist/chunk-6YIXPNL4.js +0 -5626
- package/dist/chunk-72HK2V74.js +0 -5224
- package/dist/chunk-7Q72QQIV.js +0 -4697
- package/dist/chunk-7V3XTBIF.js +0 -4896
- package/dist/chunk-A4CM4F7Q.js +0 -5633
- package/dist/chunk-ADXXR2MA.js +0 -4816
- package/dist/chunk-AG6CJPIC.js +0 -4895
- package/dist/chunk-AJPXHBTF.js +0 -4459
- package/dist/chunk-AQ6R37XV.js +0 -4809
- package/dist/chunk-AS4UEZMU.js +0 -5001
- package/dist/chunk-ASYMD22Y.js +0 -5624
- package/dist/chunk-AU4MCQCE.js +0 -5624
- package/dist/chunk-B4VHIITU.js +0 -5748
- package/dist/chunk-B6GVDNKQ.js +0 -5624
- package/dist/chunk-BCIAW6ZL.js +0 -5132
- package/dist/chunk-BJZ5PCG3.js +0 -5358
- package/dist/chunk-BS4J5QSM.js +0 -5622
- package/dist/chunk-BV2AUUX6.js +0 -2940
- package/dist/chunk-BWNSH2LK.js +0 -1574
- package/dist/chunk-C3GMBW3R.js +0 -5640
- package/dist/chunk-C6CVQGJ4.js +0 -5624
- package/dist/chunk-CGXKXNUJ.js +0 -5626
- package/dist/chunk-CIEZAYOU.js +0 -4701
- package/dist/chunk-CORZCEAQ.js +0 -5621
- package/dist/chunk-CS5LGZWP.js +0 -5357
- package/dist/chunk-CVT3KC24.js +0 -5624
- package/dist/chunk-D5MJ45R7.js +0 -3258
- package/dist/chunk-DSBRZ5DZ.js +0 -5624
- package/dist/chunk-E2X7JARQ.js +0 -4437
- package/dist/chunk-E3GY36ZP.js +0 -3258
- package/dist/chunk-E6NCIFKB.js +0 -4733
- package/dist/chunk-EOXLKW4D.js +0 -4895
- package/dist/chunk-FCI7LX4Q.js +0 -5624
- package/dist/chunk-FFJSKTOL.js +0 -4539
- package/dist/chunk-FOQYP3IB.js +0 -2950
- package/dist/chunk-GNEYTZDH.js +0 -4686
- package/dist/chunk-GPMXUMYH.js +0 -5991
- package/dist/chunk-GZWPAQPU.js +0 -4593
- package/dist/chunk-H5DXDKMX.js +0 -5619
- package/dist/chunk-HFQRTMS6.js +0 -3377
- package/dist/chunk-HQKRHX6Y.js +0 -5626
- package/dist/chunk-HTF3TNBY.js +0 -4834
- package/dist/chunk-IADSQBBY.js +0 -5523
- package/dist/chunk-IE2SXMZK.js +0 -4890
- package/dist/chunk-IGUQVJCB.js +0 -5622
- package/dist/chunk-IIREL7SL.js +0 -5615
- package/dist/chunk-IJK4EFTJ.js +0 -6043
- package/dist/chunk-J2MQ3Y5O.js +0 -5223
- package/dist/chunk-J3NG7AGT.js +0 -6047
- package/dist/chunk-JIBBZ3NV.js +0 -5132
- package/dist/chunk-JIPSBE6S.js +0 -5622
- package/dist/chunk-JPG755XK.js +0 -4589
- package/dist/chunk-JQBNL5GX.js +0 -5230
- package/dist/chunk-KS3F5WSX.js +0 -4831
- package/dist/chunk-KUYTQ4FR.js +0 -4808
- package/dist/chunk-KVP4CMJ5.js +0 -4711
- package/dist/chunk-LAR2I44B.js +0 -5626
- package/dist/chunk-LBTHSED2.js +0 -1531
- package/dist/chunk-M6OAMYVM.js +0 -5621
- package/dist/chunk-MFN5WWOY.js +0 -5132
- package/dist/chunk-MMTSKXLK.js +0 -5624
- package/dist/chunk-MPUSQLTH.js +0 -5626
- package/dist/chunk-MREXDTWL.js +0 -1555
- package/dist/chunk-MUEDKRFC.js +0 -5624
- package/dist/chunk-NOVPL2JH.js +0 -3327
- package/dist/chunk-NQIP4MHV.js +0 -4334
- package/dist/chunk-NXXKMYLS.js +0 -5624
- package/dist/chunk-OBYNZXNM.js +0 -4756
- package/dist/chunk-OFY4HBOJ.js +0 -5624
- package/dist/chunk-OJNUEZEK.js +0 -5602
- package/dist/chunk-OQCJOMUQ.js +0 -5624
- package/dist/chunk-OZH75GY6.js +0 -5132
- package/dist/chunk-P3IJVDMZ.js +0 -4700
- package/dist/chunk-PMYMYMBH.js +0 -4877
- package/dist/chunk-PRELNRVN.js +0 -5623
- package/dist/chunk-PSQUSNSI.js +0 -4703
- package/dist/chunk-QAIQ5IB6.js +0 -5624
- package/dist/chunk-QG22GADV.js +0 -6316
- package/dist/chunk-QNE2KGGK.js +0 -3315
- package/dist/chunk-RH7ZBSG4.js +0 -5132
- package/dist/chunk-RLD5MUCR.js +0 -5626
- package/dist/chunk-S42VEBNR.js +0 -3268
- package/dist/chunk-SEM6UXU4.js +0 -3324
- package/dist/chunk-SI5WP77M.js +0 -4430
- package/dist/chunk-SID4SQSY.js +0 -4837
- package/dist/chunk-SIELPKWF.js +0 -1558
- package/dist/chunk-SVBLY6QT.js +0 -5742
- package/dist/chunk-SVFTC5V2.js +0 -6021
- package/dist/chunk-SXHTX62B.js +0 -4823
- package/dist/chunk-T2YCEA5U.js +0 -4730
- package/dist/chunk-TARCHIOU.js +0 -4718
- package/dist/chunk-TDACLKD7.js +0 -5867
- package/dist/chunk-TGCBM3NP.js +0 -4890
- package/dist/chunk-TIWG6KAK.js +0 -4769
- package/dist/chunk-TKLKATVM.js +0 -1534
- package/dist/chunk-TSLZ4A5P.js +0 -5222
- package/dist/chunk-TWSDKORW.js +0 -4698
- package/dist/chunk-U5QHYVMJ.js +0 -3341
- package/dist/chunk-UAP5CTHB.js +0 -5985
- package/dist/chunk-UK6SEUWU.js +0 -3210
- package/dist/chunk-UKU5YO65.js +0 -5132
- package/dist/chunk-UOZQXP4Q.js +0 -5144
- package/dist/chunk-UPTN2TSS.js +0 -4727
- package/dist/chunk-UQT2APOE.js +0 -2944
- package/dist/chunk-V32QDZKW.js +0 -5132
- package/dist/chunk-VKY2CDCD.js +0 -5622
- package/dist/chunk-VUCSYMCY.js +0 -3323
- package/dist/chunk-VVLNBD5Y.js +0 -5132
- package/dist/chunk-W3TQ22O6.js +0 -4459
- package/dist/chunk-WA4DSGOM.js +0 -3355
- package/dist/chunk-WI6MIICK.js +0 -4687
- package/dist/chunk-XRHJLL74.js +0 -4893
- package/dist/chunk-XXWXEBJQ.js +0 -4885
- package/dist/chunk-YC6TH2H3.js +0 -5624
- package/dist/chunk-YDH6HCUJ.js +0 -5624
- package/dist/chunk-YJD35VKQ.js +0 -4890
- package/dist/chunk-ZBIQJBY7.js +0 -5620
- package/dist/chunk-ZKTSA2AE.js +0 -5629
- package/dist/chunk-ZKZZL3PE.js +0 -3379
- package/dist/chunk-ZM5KCPRK.js +0 -4541
- package/dist/chunk-ZTYPDSE3.js +0 -3258
package/src/runtime.ts
CHANGED
|
@@ -58,6 +58,21 @@ try { SDK_VERSION = _require('../package.json').version; } catch {}
|
|
|
58
58
|
/** Number of consecutive cycle failures before switching to idle */
|
|
59
59
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
60
60
|
|
|
61
|
+
function getCycleErrorHint(category: string, msg: string): string | null {
|
|
62
|
+
const lower = msg.toLowerCase();
|
|
63
|
+
if (category === 'auth' || lower.includes('401') || lower.includes('unauthorized') || lower.includes('invalid api key') || lower.includes('authentication'))
|
|
64
|
+
return 'Check your LLM API key — update via npx exagent setup';
|
|
65
|
+
if (category === 'network' || lower.includes('econnrefused') || lower.includes('enotfound') || lower.includes('fetch failed') || lower.includes('timeout'))
|
|
66
|
+
return 'Network connectivity issue — check your internet connection';
|
|
67
|
+
if (category === 'venue' || lower.includes('hyperliquid') || lower.includes('polymarket') || lower.includes('venue'))
|
|
68
|
+
return 'Venue API error — the venue may be experiencing issues';
|
|
69
|
+
if (category === 'strategy' || lower.includes('invalid') && lower.includes('signal') || lower.includes('strategy'))
|
|
70
|
+
return 'Strategy returned invalid output — check your strategy file';
|
|
71
|
+
if (lower.includes('rate limit') || lower.includes('429'))
|
|
72
|
+
return 'Rate limited — consider increasing tradingIntervalMs';
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
export class AgentRuntime {
|
|
62
77
|
private config: RuntimeConfig;
|
|
63
78
|
private relay: RelayClient;
|
|
@@ -809,12 +824,13 @@ export class AgentRuntime {
|
|
|
809
824
|
timings.totalMs = Date.now() - cycleStart;
|
|
810
825
|
|
|
811
826
|
const categorized = this.diagnostics.recordError(err as Error);
|
|
812
|
-
|
|
827
|
+
const hint = getCycleErrorHint(categorized.category, errMsg);
|
|
828
|
+
log.error('cycle', `Cycle error: ${errMsg}${hint ? ` — ${hint}` : ''}`, {
|
|
813
829
|
cycle: this.cycleCount,
|
|
814
830
|
totalMs: timings.totalMs,
|
|
815
831
|
category: categorized.category,
|
|
816
832
|
});
|
|
817
|
-
this.signal.reportError('Cycle Error', errMsg);
|
|
833
|
+
this.signal.reportError('Cycle Error', `${errMsg}${hint ? ` — ${hint}` : ''}`);
|
|
818
834
|
|
|
819
835
|
this.diagnostics.recordCycle({
|
|
820
836
|
cycleNumber: this.cycleCount,
|
package/src/setup.ts
CHANGED
|
@@ -33,6 +33,24 @@ function cancelled(): never {
|
|
|
33
33
|
process.exit(0);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function isNonInteractive(): boolean {
|
|
37
|
+
return process.env.EXAGENT_NONINTERACTIVE === '1' || process.env.EXAGENT_NONINTERACTIVE === 'true';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const LLM_KEY_PREFIXES: Record<string, string> = {
|
|
41
|
+
openai: 'sk-',
|
|
42
|
+
anthropic: 'sk-ant-',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function validateLlmKeyFormat(provider: string, key: string): string | undefined {
|
|
46
|
+
if (!key.trim()) return 'API key is required.';
|
|
47
|
+
const expectedPrefix = LLM_KEY_PREFIXES[provider];
|
|
48
|
+
if (expectedPrefix && !key.startsWith(expectedPrefix)) {
|
|
49
|
+
return `${provider} API keys typically start with "${expectedPrefix}". Double-check your key.`;
|
|
50
|
+
}
|
|
51
|
+
if (key.length < 10) return 'API key seems too short.';
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
// ---------------------------------------------------------------------------
|
|
37
55
|
// Step 1: Bootstrap
|
|
38
56
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +92,24 @@ async function setupWallet(config: RuntimeConfigFile): Promise<string> {
|
|
|
74
92
|
return config.wallet.privateKey;
|
|
75
93
|
}
|
|
76
94
|
|
|
95
|
+
// Non-interactive: env var wallet
|
|
96
|
+
if (isNonInteractive()) {
|
|
97
|
+
const mode = process.env.EXAGENT_WALLET_MODE || 'generate';
|
|
98
|
+
if (mode === 'import') {
|
|
99
|
+
const key = process.env.EXAGENT_WALLET_KEY;
|
|
100
|
+
if (!key || !/^0x[a-fA-F0-9]{64}$/.test(key)) {
|
|
101
|
+
throw new Error('EXAGENT_WALLET_KEY must be a valid 0x-prefixed 64-char hex private key in non-interactive mode');
|
|
102
|
+
}
|
|
103
|
+
const address = privateKeyToAccount(key as `0x${string}`).address;
|
|
104
|
+
printDone(`Wallet imported: ${pc.dim(address)}`);
|
|
105
|
+
return key;
|
|
106
|
+
}
|
|
107
|
+
const privateKey = generatePrivateKey();
|
|
108
|
+
const address = privateKeyToAccount(privateKey).address;
|
|
109
|
+
printDone(`Wallet created: ${pc.dim(address)}`);
|
|
110
|
+
return privateKey;
|
|
111
|
+
}
|
|
112
|
+
|
|
77
113
|
const method = await clack.select({
|
|
78
114
|
message: 'How would you like to set up your wallet?',
|
|
79
115
|
options: [
|
|
@@ -114,62 +150,52 @@ const LLM_PROVIDERS = ['openai', 'anthropic', 'google', 'deepseek', 'mistral', '
|
|
|
114
150
|
|
|
115
151
|
async function setupLlm(
|
|
116
152
|
config: RuntimeConfigFile,
|
|
117
|
-
bootstrapPayload: BootstrapPayload,
|
|
118
153
|
): Promise<{ provider: string; model: string; apiKey: string }> {
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
154
|
+
// LLM config is always entered locally — never pulled from bootstrap.
|
|
155
|
+
// Config file may have provider/model as defaults from the deploy wizard.
|
|
156
|
+
|
|
157
|
+
// Non-interactive mode
|
|
158
|
+
if (isNonInteractive()) {
|
|
159
|
+
const provider = process.env.EXAGENT_LLM_PROVIDER || config.llm?.provider;
|
|
160
|
+
const model = process.env.EXAGENT_LLM_MODEL || config.llm?.model;
|
|
161
|
+
const apiKey = process.env.EXAGENT_LLM_KEY;
|
|
162
|
+
if (!provider) throw new Error('EXAGENT_LLM_PROVIDER required in non-interactive mode');
|
|
163
|
+
if (!model) throw new Error('EXAGENT_LLM_MODEL required in non-interactive mode');
|
|
164
|
+
if (!apiKey) throw new Error('EXAGENT_LLM_KEY required in non-interactive mode');
|
|
165
|
+
printDone('LLM configured');
|
|
166
|
+
return { provider, model, apiKey };
|
|
130
167
|
}
|
|
131
168
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
// Provider — use config as default selection if available
|
|
170
|
+
const defaultProvider = config.llm?.provider;
|
|
171
|
+
const providerOptions = LLM_PROVIDERS.map(p => ({ value: p, label: p }));
|
|
172
|
+
const selected = await clack.select({
|
|
173
|
+
message: 'LLM provider:',
|
|
174
|
+
options: providerOptions,
|
|
175
|
+
initialValue: defaultProvider || undefined,
|
|
176
|
+
});
|
|
177
|
+
if (clack.isCancel(selected)) cancelled();
|
|
178
|
+
const provider = selected;
|
|
179
|
+
|
|
180
|
+
// Model — use config as default/placeholder if available
|
|
181
|
+
const defaultModel = config.llm?.model;
|
|
182
|
+
const entered = await clack.text({
|
|
183
|
+
message: 'LLM model:',
|
|
184
|
+
placeholder: defaultModel || 'gpt-4o',
|
|
185
|
+
defaultValue: defaultModel || undefined,
|
|
186
|
+
validate: (val) => {
|
|
187
|
+
if (!val.trim()) return 'Model name is required.';
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (clack.isCancel(entered)) cancelled();
|
|
191
|
+
const model = entered;
|
|
147
192
|
|
|
148
|
-
// API Key
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
if (clack.isCancel(useBootstrap)) cancelled();
|
|
156
|
-
if (useBootstrap) {
|
|
157
|
-
apiKey = bootstrapPayload.llm.apiKey;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (!apiKey) {
|
|
161
|
-
apiKey = config.llm?.apiKey;
|
|
162
|
-
}
|
|
163
|
-
if (!apiKey) {
|
|
164
|
-
const entered = await clack.password({
|
|
165
|
-
message: 'LLM API key:',
|
|
166
|
-
validate: (val) => {
|
|
167
|
-
if (!val.trim()) return 'API key is required.';
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
if (clack.isCancel(entered)) cancelled();
|
|
171
|
-
apiKey = entered;
|
|
172
|
-
}
|
|
193
|
+
// API Key — always prompt, never from bootstrap
|
|
194
|
+
const apiKey = await clack.password({
|
|
195
|
+
message: 'LLM API key:',
|
|
196
|
+
validate: (val) => validateLlmKeyFormat(provider, val),
|
|
197
|
+
});
|
|
198
|
+
if (clack.isCancel(apiKey)) cancelled();
|
|
173
199
|
|
|
174
200
|
printDone('LLM configured');
|
|
175
201
|
return { provider, model, apiKey };
|
|
@@ -180,6 +206,15 @@ async function setupLlm(
|
|
|
180
206
|
// ---------------------------------------------------------------------------
|
|
181
207
|
|
|
182
208
|
async function setupEncryption(): Promise<string> {
|
|
209
|
+
// Non-interactive mode
|
|
210
|
+
if (isNonInteractive()) {
|
|
211
|
+
const password = process.env.EXAGENT_PASSWORD;
|
|
212
|
+
if (!password || password.length < 12) {
|
|
213
|
+
throw new Error('EXAGENT_PASSWORD must be at least 12 characters in non-interactive mode');
|
|
214
|
+
}
|
|
215
|
+
return password;
|
|
216
|
+
}
|
|
217
|
+
|
|
183
218
|
printInfo(`Secrets encrypted with ${pc.cyan('AES-256-GCM')} (${pc.cyan('scrypt')} KDF)`);
|
|
184
219
|
printInfo('The password never leaves this machine.');
|
|
185
220
|
console.log();
|
|
@@ -228,6 +263,11 @@ function writeSecureStore(path: string, secrets: LocalSecretPayload, password: s
|
|
|
228
263
|
// ---------------------------------------------------------------------------
|
|
229
264
|
|
|
230
265
|
export async function promptSecretPassword(question: string = 'Device password:'): Promise<string> {
|
|
266
|
+
if (isNonInteractive()) {
|
|
267
|
+
const password = process.env.EXAGENT_PASSWORD || process.env.EXAGENT_SECRET_PASSWORD;
|
|
268
|
+
if (!password) throw new Error('EXAGENT_PASSWORD required in non-interactive mode');
|
|
269
|
+
return password;
|
|
270
|
+
}
|
|
231
271
|
const password = await clack.password({ message: question });
|
|
232
272
|
if (clack.isCancel(password)) cancelled();
|
|
233
273
|
return password;
|
|
@@ -248,6 +288,14 @@ export async function ensureLocalSetup(configPath: string): Promise<void> {
|
|
|
248
288
|
!config.wallet?.privateKey &&
|
|
249
289
|
!config.llm.apiKey
|
|
250
290
|
) {
|
|
291
|
+
printBanner();
|
|
292
|
+
printSuccess('Already set up', [
|
|
293
|
+
`${pc.cyan('npx exagent run')} Start the agent`,
|
|
294
|
+
`${pc.cyan('npx exagent config')} Change LLM API key or model`,
|
|
295
|
+
`${pc.cyan('npx exagent status')} Check agent connection`,
|
|
296
|
+
'',
|
|
297
|
+
`${pc.dim('Dashboard:')} ${pc.cyan('https://exagent.io')}`,
|
|
298
|
+
]);
|
|
251
299
|
return;
|
|
252
300
|
}
|
|
253
301
|
|
|
@@ -260,9 +308,6 @@ export async function ensureLocalSetup(configPath: string): Promise<void> {
|
|
|
260
308
|
const bootstrapPayload = await consumeBootstrapPackage(config);
|
|
261
309
|
if (config.secrets?.bootstrapToken) {
|
|
262
310
|
printDone('Bootstrap package consumed');
|
|
263
|
-
if (bootstrapPayload.llm?.provider) {
|
|
264
|
-
printInfo(`LLM config received: ${pc.cyan(bootstrapPayload.llm.provider)}${bootstrapPayload.llm.model ? ` / ${pc.cyan(bootstrapPayload.llm.model)}` : ''}`);
|
|
265
|
-
}
|
|
266
311
|
} else {
|
|
267
312
|
printInfo('No bootstrap token — manual configuration');
|
|
268
313
|
}
|
|
@@ -273,7 +318,7 @@ export async function ensureLocalSetup(configPath: string): Promise<void> {
|
|
|
273
318
|
|
|
274
319
|
// Step 3: LLM
|
|
275
320
|
printStep(3, 4, 'LLM configuration');
|
|
276
|
-
const llm = await setupLlm(config
|
|
321
|
+
const llm = await setupLlm(config);
|
|
277
322
|
|
|
278
323
|
// Step 4: Encryption
|
|
279
324
|
printStep(4, 4, 'Device encryption');
|
|
@@ -287,15 +332,20 @@ export async function ensureLocalSetup(configPath: string): Promise<void> {
|
|
|
287
332
|
};
|
|
288
333
|
|
|
289
334
|
if (!secrets.apiToken) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
335
|
+
if (isNonInteractive()) {
|
|
336
|
+
const token = process.env.EXAGENT_API_TOKEN;
|
|
337
|
+
if (!token) throw new Error('EXAGENT_API_TOKEN required in non-interactive mode (no relay token from bootstrap)');
|
|
338
|
+
secrets.apiToken = token;
|
|
339
|
+
} else {
|
|
340
|
+
const token = await clack.password({
|
|
341
|
+
message: 'Agent relay token:',
|
|
342
|
+
validate: (val) => {
|
|
343
|
+
if (!val.trim()) return 'Relay token is required.';
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
if (clack.isCancel(token)) cancelled();
|
|
347
|
+
secrets.apiToken = token;
|
|
348
|
+
}
|
|
299
349
|
}
|
|
300
350
|
|
|
301
351
|
const nextConfig = structuredClone(config);
|
|
@@ -324,8 +374,9 @@ export async function ensureLocalSetup(configPath: string): Promise<void> {
|
|
|
324
374
|
clack.outro(pc.green('Setup complete'));
|
|
325
375
|
|
|
326
376
|
printSuccess('Ready', [
|
|
327
|
-
`${pc.cyan('npx exagent run')}
|
|
328
|
-
`${pc.cyan('npx exagent
|
|
377
|
+
`${pc.cyan('npx exagent run')} Start the agent`,
|
|
378
|
+
`${pc.cyan('npx exagent config')} Change LLM API key or model`,
|
|
379
|
+
`${pc.cyan('npx exagent status')} Check agent connection`,
|
|
329
380
|
'',
|
|
330
381
|
`${pc.dim('Dashboard:')} ${pc.cyan('https://exagent.io')}`,
|
|
331
382
|
]);
|