agentxl 1.1.3 → 1.1.5
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 +17 -37
- package/bin/agentxl.js +460 -521
- package/dist/agent/models.d.ts +8 -3
- package/dist/agent/models.d.ts.map +1 -1
- package/dist/agent/models.js +33 -21
- package/dist/agent/models.js.map +1 -1
- package/dist/agent/session.d.ts +19 -2
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +37 -6
- package/dist/agent/session.js.map +1 -1
- package/dist/server/excel-bridge.d.ts.map +1 -1
- package/dist/server/excel-bridge.js +6 -1
- package/dist/server/excel-bridge.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/agent.d.ts.map +1 -1
- package/dist/server/routes/agent.js +88 -1
- package/dist/server/routes/agent.js.map +1 -1
- package/dist/server/routes/auth.d.ts +12 -0
- package/dist/server/routes/auth.d.ts.map +1 -0
- package/dist/server/routes/auth.js +50 -0
- package/dist/server/routes/auth.js.map +1 -0
- package/package.json +5 -5
- package/taskpane/dist/assets/index-BMcoQ8v_.js +229 -0
- package/taskpane/dist/assets/index-DY4oaVhg.css +1 -0
- package/taskpane/dist/index.html +2 -2
- package/taskpane/dist/assets/index-BnD8psE_.js +0 -224
- package/taskpane/dist/assets/index-BuAcDfRq.css +0 -1
package/bin/agentxl.js
CHANGED
|
@@ -1,521 +1,460 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFileSync, existsSync } from "fs";
|
|
4
|
-
import { resolve, join, dirname } from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import { homedir } from "os";
|
|
7
|
-
import { createInterface } from "readline";
|
|
8
|
-
import { config as loadDotenv } from "dotenv";
|
|
9
|
-
|
|
10
|
-
// Load .env from project root (before any other imports)
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = dirname(__filename);
|
|
13
|
-
const projectRoot = resolve(__dirname, "..");
|
|
14
|
-
loadDotenv({ path: join(projectRoot, ".env") });
|
|
15
|
-
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Package info
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
const pkgPath = join(__dirname, "..", "package.json");
|
|
21
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
22
|
-
const VERSION = pkg.version;
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Arg parsing
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
const args = process.argv.slice(2);
|
|
29
|
-
const command = args[0];
|
|
30
|
-
|
|
31
|
-
function getFlag(name) {
|
|
32
|
-
const eqIdx = args.findIndex((a) => a.startsWith(`--${name}=`));
|
|
33
|
-
if (eqIdx !== -1) return args[eqIdx].split("=")[1];
|
|
34
|
-
const spaceIdx = args.indexOf(`--${name}`);
|
|
35
|
-
if (spaceIdx !== -1 && args[spaceIdx + 1]) return args[spaceIdx + 1];
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const hasFlag = (name) => args.includes(`--${name}`);
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Terminal I/O helpers
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
function prompt(question) {
|
|
46
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
47
|
-
return new Promise((resolve) => {
|
|
48
|
-
rl.question(question, (answer) => {
|
|
49
|
-
rl.close();
|
|
50
|
-
resolve(answer.trim());
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function promptSecret(question) {
|
|
56
|
-
return new Promise((resolve) => {
|
|
57
|
-
process.stdout.write(question);
|
|
58
|
-
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
59
|
-
if (process.stdin.isTTY) process.stdin.setRawMode?.(false);
|
|
60
|
-
rl.on("line", (line) => {
|
|
61
|
-
rl.close();
|
|
62
|
-
process.stdout.write("\n");
|
|
63
|
-
resolve(line.trim());
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function openUrl(url) {
|
|
69
|
-
const { exec } = await import("child_process");
|
|
70
|
-
const cmd =
|
|
71
|
-
process.platform === "win32"
|
|
72
|
-
? `start "" "${url}"`
|
|
73
|
-
: process.platform === "darwin"
|
|
74
|
-
? `open "${url}"`
|
|
75
|
-
: `xdg-open "${url}"`;
|
|
76
|
-
exec(cmd);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ---------------------------------------------------------------------------
|
|
80
|
-
// Step-by-step progress output
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
|
|
83
|
-
/** Print a step status: ✅ done, ⏳ in progress, ❌ failed */
|
|
84
|
-
function step(icon, text) {
|
|
85
|
-
console.log(` ${icon} ${text}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
// Auth flow using Pi SDK
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
|
-
|
|
92
|
-
async function checkAuth() {
|
|
93
|
-
const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
|
|
94
|
-
|
|
95
|
-
const piAuthPath = join(homedir(), ".pi", "agent", "auth.json");
|
|
96
|
-
const agentxlAuthPath = join(homedir(), ".agentxl", "auth.json");
|
|
97
|
-
const authPath = existsSync(piAuthPath) ? piAuthPath : agentxlAuthPath;
|
|
98
|
-
const authStorage = AuthStorage.create(authPath);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
`);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
console.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
`);
|
|
462
|
-
|
|
463
|
-
// Optionally open Excel
|
|
464
|
-
if (hasFlag("open")) {
|
|
465
|
-
try {
|
|
466
|
-
const devSettings = await import("office-addin-dev-settings");
|
|
467
|
-
const manifestLib = await import("office-addin-manifest");
|
|
468
|
-
step("⏳", "Opening Excel with AgentXL...");
|
|
469
|
-
await devSettings.sideloadAddIn(manifestPath, manifestLib.OfficeApp.Excel, false, devSettings.AppType.Desktop);
|
|
470
|
-
} catch (err) {
|
|
471
|
-
step("⚠️", `Could not open Excel: ${err.message}`);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
async function login() {
|
|
477
|
-
console.log("");
|
|
478
|
-
const authed = await runAuthFlow();
|
|
479
|
-
if (authed) {
|
|
480
|
-
console.log(" Run 'agentxl start' to launch the server.\n");
|
|
481
|
-
}
|
|
482
|
-
process.exit(authed ? 0 : 1);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// ---------------------------------------------------------------------------
|
|
486
|
-
// Main
|
|
487
|
-
// ---------------------------------------------------------------------------
|
|
488
|
-
|
|
489
|
-
if (hasFlag("version") || command === "--version") {
|
|
490
|
-
console.log(VERSION);
|
|
491
|
-
process.exit(0);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (hasFlag("help") || command === "--help" || command === "help") {
|
|
495
|
-
printHelp();
|
|
496
|
-
process.exit(0);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (command === "start") {
|
|
500
|
-
start().catch((err) => {
|
|
501
|
-
console.error(`\n ❌ ${err.message || err}\n`);
|
|
502
|
-
process.exit(1);
|
|
503
|
-
});
|
|
504
|
-
} else if (command === "install") {
|
|
505
|
-
install().catch((err) => {
|
|
506
|
-
console.error(`\n ❌ ${err.message || err}\n`);
|
|
507
|
-
process.exit(1);
|
|
508
|
-
});
|
|
509
|
-
} else if (command === "login") {
|
|
510
|
-
login().catch((err) => {
|
|
511
|
-
console.error(`\n ❌ ${err.message || err}\n`);
|
|
512
|
-
process.exit(1);
|
|
513
|
-
});
|
|
514
|
-
} else if (!command) {
|
|
515
|
-
printHelp();
|
|
516
|
-
process.exit(0);
|
|
517
|
-
} else {
|
|
518
|
-
console.error(`Unknown command: ${command}`);
|
|
519
|
-
console.error(`Run 'agentxl --help' for usage.`);
|
|
520
|
-
process.exit(1);
|
|
521
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, existsSync } from "fs";
|
|
4
|
+
import { resolve, join, dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { createInterface } from "readline";
|
|
8
|
+
import { config as loadDotenv } from "dotenv";
|
|
9
|
+
|
|
10
|
+
// Load .env from project root (before any other imports)
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const projectRoot = resolve(__dirname, "..");
|
|
14
|
+
loadDotenv({ path: join(projectRoot, ".env") });
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Package info
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
const pkgPath = join(__dirname, "..", "package.json");
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
22
|
+
const VERSION = pkg.version;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Arg parsing
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
const args = process.argv.slice(2);
|
|
29
|
+
const command = args[0];
|
|
30
|
+
|
|
31
|
+
function getFlag(name) {
|
|
32
|
+
const eqIdx = args.findIndex((a) => a.startsWith(`--${name}=`));
|
|
33
|
+
if (eqIdx !== -1) return args[eqIdx].split("=")[1];
|
|
34
|
+
const spaceIdx = args.indexOf(`--${name}`);
|
|
35
|
+
if (spaceIdx !== -1 && args[spaceIdx + 1]) return args[spaceIdx + 1];
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const hasFlag = (name) => args.includes(`--${name}`);
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Terminal I/O helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
function prompt(question) {
|
|
46
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
rl.question(question, (answer) => {
|
|
49
|
+
rl.close();
|
|
50
|
+
resolve(answer.trim());
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function promptSecret(question) {
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
process.stdout.write(question);
|
|
58
|
+
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
59
|
+
if (process.stdin.isTTY) process.stdin.setRawMode?.(false);
|
|
60
|
+
rl.on("line", (line) => {
|
|
61
|
+
rl.close();
|
|
62
|
+
process.stdout.write("\n");
|
|
63
|
+
resolve(line.trim());
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function openUrl(url) {
|
|
69
|
+
const { exec } = await import("child_process");
|
|
70
|
+
const cmd =
|
|
71
|
+
process.platform === "win32"
|
|
72
|
+
? `start "" "${url}"`
|
|
73
|
+
: process.platform === "darwin"
|
|
74
|
+
? `open "${url}"`
|
|
75
|
+
: `xdg-open "${url}"`;
|
|
76
|
+
exec(cmd);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Step-by-step progress output
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
/** Print a step status: ✅ done, ⏳ in progress, ❌ failed */
|
|
84
|
+
function step(icon, text) {
|
|
85
|
+
console.log(` ${icon} ${text}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Auth flow using Pi SDK
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
async function checkAuth() {
|
|
93
|
+
const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
|
|
94
|
+
|
|
95
|
+
const piAuthPath = join(homedir(), ".pi", "agent", "auth.json");
|
|
96
|
+
const agentxlAuthPath = join(homedir(), ".agentxl", "auth.json");
|
|
97
|
+
const authPath = existsSync(piAuthPath) ? piAuthPath : agentxlAuthPath;
|
|
98
|
+
const authStorage = AuthStorage.create(authPath);
|
|
99
|
+
|
|
100
|
+
if (authStorage.list().length > 0) return true;
|
|
101
|
+
|
|
102
|
+
// Also accept env vars — Pi SDK's hasAuth() falls back to them at runtime.
|
|
103
|
+
return Boolean(
|
|
104
|
+
process.env.OPENROUTER_API_KEY ||
|
|
105
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
106
|
+
process.env.OPENAI_API_KEY
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function runAuthFlow() {
|
|
111
|
+
const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
|
|
112
|
+
|
|
113
|
+
const piAuthPath = join(homedir(), ".pi", "agent", "auth.json");
|
|
114
|
+
const agentxlAuthPath = join(homedir(), ".agentxl", "auth.json");
|
|
115
|
+
const authPath = existsSync(piAuthPath) ? piAuthPath : agentxlAuthPath;
|
|
116
|
+
const authStorage = AuthStorage.create(authPath);
|
|
117
|
+
|
|
118
|
+
// Check if already authenticated
|
|
119
|
+
if (authStorage.list().length > 0) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(`
|
|
124
|
+
No API credentials found. Paste an API key to get started.
|
|
125
|
+
|
|
126
|
+
Key prefixes:
|
|
127
|
+
sk-or-... → OpenRouter (recommended — 100+ models, free tier available)
|
|
128
|
+
sk-ant-... → Anthropic (Claude)
|
|
129
|
+
sk-... → OpenAI (GPT-4o)
|
|
130
|
+
|
|
131
|
+
No account yet?
|
|
132
|
+
→ Create a free OpenRouter account at https://openrouter.ai/keys
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
const key = await promptSecret(" API key: ");
|
|
136
|
+
|
|
137
|
+
if (!key) {
|
|
138
|
+
console.error("\n No key entered. Run 'agentxl login' to try again.\n");
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Auto-detect provider from key prefix
|
|
143
|
+
let provider;
|
|
144
|
+
if (key.startsWith("sk-ant-")) provider = "anthropic";
|
|
145
|
+
else if (key.startsWith("sk-or-")) provider = "openrouter";
|
|
146
|
+
else if (key.startsWith("sk-")) provider = "openai";
|
|
147
|
+
else {
|
|
148
|
+
const p = await prompt(
|
|
149
|
+
" Could not detect provider. Enter provider name (anthropic/openrouter/openai): "
|
|
150
|
+
);
|
|
151
|
+
provider = p.toLowerCase();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
authStorage.set(provider, { type: "api_key", key });
|
|
155
|
+
console.log(`\n ✅ API key saved for ${provider}\n`);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// Commands
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
function printHelp() {
|
|
164
|
+
console.log(`
|
|
165
|
+
AgentXL v${VERSION} — AI agent for Microsoft Excel
|
|
166
|
+
|
|
167
|
+
Usage:
|
|
168
|
+
agentxl start [options] Start the AgentXL server
|
|
169
|
+
agentxl install Register the add-in with Excel (one-time)
|
|
170
|
+
agentxl login Set up or change API credentials
|
|
171
|
+
agentxl --version Print version
|
|
172
|
+
agentxl --help Show this help
|
|
173
|
+
|
|
174
|
+
Options:
|
|
175
|
+
--port <number> Port to listen on (default: 3001)
|
|
176
|
+
--verbose Log all HTTP requests
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
agentxl install # one-time: register with Excel
|
|
180
|
+
agentxl start # run the server
|
|
181
|
+
agentxl start --port 3002
|
|
182
|
+
agentxl login
|
|
183
|
+
`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function start() {
|
|
187
|
+
const port = parseInt(getFlag("port") || "3001", 10);
|
|
188
|
+
|
|
189
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
190
|
+
console.error(`Error: Invalid port number. Must be 1-65535.`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`
|
|
195
|
+
┌──────────────────────────────────────┐
|
|
196
|
+
│ AgentXL v${VERSION.padEnd(19)}│
|
|
197
|
+
│ AI agent for Microsoft Excel │
|
|
198
|
+
└──────────────────────────────────────┘
|
|
199
|
+
`);
|
|
200
|
+
|
|
201
|
+
// ── Step 1: Load modules ───────────────────────────────────────────────
|
|
202
|
+
let ensureCerts, startServer, stopServer, setVerbose, getFolderPickerStrategy;
|
|
203
|
+
try {
|
|
204
|
+
const certs = await import("../dist/server/certs.js");
|
|
205
|
+
const server = await import("../dist/server/index.js");
|
|
206
|
+
const picker = await import("../dist/server/folder-picker.js");
|
|
207
|
+
ensureCerts = certs.ensureCerts;
|
|
208
|
+
startServer = server.startServer;
|
|
209
|
+
stopServer = server.stopServer;
|
|
210
|
+
setVerbose = server.setVerbose;
|
|
211
|
+
getFolderPickerStrategy = picker.getFolderPickerStrategy;
|
|
212
|
+
} catch (err) {
|
|
213
|
+
step("❌", "Could not load AgentXL server modules");
|
|
214
|
+
console.error(" Run 'npm run build' first to compile TypeScript.");
|
|
215
|
+
console.error(` ${err.message}`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ── Step 2: Check auth ─────────────────────────────────────────────────
|
|
220
|
+
const hasAuth = await checkAuth();
|
|
221
|
+
if (hasAuth) {
|
|
222
|
+
step("✅", "Auth ready");
|
|
223
|
+
} else {
|
|
224
|
+
step("ℹ️", "No API key yet — you can paste one in the AgentXL taskpane");
|
|
225
|
+
step(" ", "Or run 'agentxl login' to set one in the terminal");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ── Step 3: HTTPS certificates ─────────────────────────────────────────
|
|
229
|
+
try {
|
|
230
|
+
const certPair = await ensureCerts();
|
|
231
|
+
step("✅", "HTTPS certificate ready");
|
|
232
|
+
|
|
233
|
+
// ── Step 4: Start server ───────────────────────────────────────────────
|
|
234
|
+
if (hasFlag("verbose")) setVerbose(true);
|
|
235
|
+
await startServer(port, certPair);
|
|
236
|
+
step("✅", `Server running at https://localhost:${port}`);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
step("❌", `Server failed to start: ${err.message}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Step 5: OCR status ─────────────────────────────────────────────────
|
|
243
|
+
if (process.env.AZURE_MISTRAL_ENDPOINT && process.env.AZURE_MISTRAL_API_KEY) {
|
|
244
|
+
step("✅", "OCR ready (Azure Mistral)");
|
|
245
|
+
} else if (process.env.MISTRAL_API_KEY) {
|
|
246
|
+
step("✅", "OCR ready (Mistral direct)");
|
|
247
|
+
} else {
|
|
248
|
+
step("ℹ️", "OCR not configured — scanned PDFs won't be readable");
|
|
249
|
+
step(" ", "Set AZURE_MISTRAL_ENDPOINT + AZURE_MISTRAL_API_KEY in .env");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── Step 6: Folder picker strategy ────────────────────────────────────
|
|
253
|
+
const pickerStrategy = getFolderPickerStrategy();
|
|
254
|
+
const pickerLabels = {
|
|
255
|
+
"native-helper": "Native folder picker helper",
|
|
256
|
+
"powershell": "PowerShell folder picker (fallback)",
|
|
257
|
+
"osascript": "macOS folder picker (osascript)",
|
|
258
|
+
"manual-only": "Manual path entry only",
|
|
259
|
+
};
|
|
260
|
+
const pickerLabel = pickerLabels[pickerStrategy.method] || pickerStrategy.method;
|
|
261
|
+
if (pickerStrategy.method === "native-helper") {
|
|
262
|
+
step("✅", `Folder picker: ${pickerLabel}`);
|
|
263
|
+
} else if (pickerStrategy.method === "powershell" || pickerStrategy.method === "osascript") {
|
|
264
|
+
step("⚠️", `Folder picker: ${pickerLabel}`);
|
|
265
|
+
if (pickerStrategy.platform === "win32") {
|
|
266
|
+
step(" ", "Build the native helper for a better experience:");
|
|
267
|
+
step(" ", " npm run build:folder-picker:win");
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
step("ℹ️", `Folder picker: ${pickerLabel}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── Step 7: Auto-register add-in with Excel (first run) ─────────────
|
|
274
|
+
const manifestPath = resolve(__dirname, "..", "manifest", "manifest.xml");
|
|
275
|
+
|
|
276
|
+
if (existsSync(manifestPath)) {
|
|
277
|
+
let alreadyRegistered = false;
|
|
278
|
+
try {
|
|
279
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
280
|
+
const registered = await devSettings.getRegisteredAddIns();
|
|
281
|
+
alreadyRegistered = registered.some(a => a.manifestPath === manifestPath);
|
|
282
|
+
} catch (_) {}
|
|
283
|
+
|
|
284
|
+
if (!alreadyRegistered) {
|
|
285
|
+
step("⏳", "First run — registering AgentXL with Excel...");
|
|
286
|
+
try {
|
|
287
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
288
|
+
await devSettings.registerAddIn(manifestPath);
|
|
289
|
+
step("✅", "Add-in registered with Excel");
|
|
290
|
+
} catch (err) {
|
|
291
|
+
step("⚠️", `Auto-registration failed: ${err.message}`);
|
|
292
|
+
step(" ", "Run 'agentxl install' manually, or add the manifest folder to Excel Trust Center:");
|
|
293
|
+
step(" ", ` ${dirname(manifestPath)}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
298
|
+
await devSettings.ensureLoopbackIsEnabled(manifestPath, false);
|
|
299
|
+
} catch (_) {}
|
|
300
|
+
} else {
|
|
301
|
+
step("✅", "Excel add-in registered");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── Post-start guidance ────────────────────────────────────────────────
|
|
306
|
+
console.log(`
|
|
307
|
+
─────────────────────────────────────────────────
|
|
308
|
+
All systems go!
|
|
309
|
+
─────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
📎 Open Excel → AgentXL is on the Home ribbon
|
|
312
|
+
(If you don't see it: Insert → My Add-ins → SHARED FOLDER → AgentXL)
|
|
313
|
+
|
|
314
|
+
🌐 Or test in browser first:
|
|
315
|
+
https://localhost:${port}/taskpane/
|
|
316
|
+
|
|
317
|
+
💬 Try: "What can you help me with in this workbook?"
|
|
318
|
+
`);
|
|
319
|
+
|
|
320
|
+
// ── Graceful shutdown ──────────────────────────────────────────────────
|
|
321
|
+
let shuttingDown = false;
|
|
322
|
+
const shutdown = () => {
|
|
323
|
+
if (shuttingDown) return;
|
|
324
|
+
shuttingDown = true;
|
|
325
|
+
console.log("\n AgentXL stopped. Goodbye!\n");
|
|
326
|
+
const forceExit = setTimeout(() => process.exit(0), 2000);
|
|
327
|
+
forceExit.unref?.();
|
|
328
|
+
stopServer().then(() => process.exit(0));
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
process.on("SIGINT", shutdown);
|
|
332
|
+
process.on("SIGTERM", shutdown);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function install() {
|
|
336
|
+
console.log(`
|
|
337
|
+
┌──────────────────────────────────────┐
|
|
338
|
+
│ AgentXL Excel Registration │
|
|
339
|
+
└──────────────────────────────────────┘
|
|
340
|
+
`);
|
|
341
|
+
|
|
342
|
+
const manifestPath = resolve(__dirname, "..", "manifest", "manifest.xml");
|
|
343
|
+
if (!existsSync(manifestPath)) {
|
|
344
|
+
step("❌", `Manifest not found: ${manifestPath}`);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ── Step 1: HTTPS certificates ─────────────────────────────────────────
|
|
349
|
+
try {
|
|
350
|
+
step("⏳", "Ensuring HTTPS certificates are trusted...");
|
|
351
|
+
const devCerts = await import("office-addin-dev-certs");
|
|
352
|
+
await devCerts.ensureCertificatesAreInstalled();
|
|
353
|
+
step("✅", "HTTPS certificate trusted");
|
|
354
|
+
} catch (err) {
|
|
355
|
+
step("⚠️", `Certificate setup: ${err.message}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ── Step 2: Register add-in ────────────────────────────────────────────
|
|
359
|
+
try {
|
|
360
|
+
step("⏳", "Registering AgentXL with Excel...");
|
|
361
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
362
|
+
await devSettings.registerAddIn(manifestPath);
|
|
363
|
+
step("✅", "Add-in registered with Excel");
|
|
364
|
+
} catch (err) {
|
|
365
|
+
step("❌", `Registration failed: ${err.message}`);
|
|
366
|
+
console.log(`
|
|
367
|
+
Fallback: manually add this folder to Excel's Trusted Add-in Catalogs:
|
|
368
|
+
${dirname(manifestPath)}
|
|
369
|
+
|
|
370
|
+
Steps:
|
|
371
|
+
1. Excel → File → Options → Trust Center → Trust Center Settings
|
|
372
|
+
2. Trusted Add-in Catalogs → paste the path above
|
|
373
|
+
3. Check "Show in Menu" → OK → OK
|
|
374
|
+
4. Restart Excel
|
|
375
|
+
5. Insert → My Add-ins → SHARED FOLDER → AgentXL → Add
|
|
376
|
+
`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── Step 3: Enable loopback ────────────────────────────────────────────
|
|
381
|
+
try {
|
|
382
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
383
|
+
await devSettings.ensureLoopbackIsEnabled(manifestPath, false);
|
|
384
|
+
step("✅", "Localhost loopback enabled");
|
|
385
|
+
} catch (err) {
|
|
386
|
+
step("⚠️", `Loopback: ${err.message}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
console.log(`
|
|
390
|
+
─────────────────────────────────────────────────
|
|
391
|
+
Done! AgentXL is registered with Excel.
|
|
392
|
+
─────────────────────────────────────────────────
|
|
393
|
+
|
|
394
|
+
Next steps:
|
|
395
|
+
1. Run: agentxl start
|
|
396
|
+
2. Open Excel → AgentXL appears on the Home ribbon
|
|
397
|
+
|
|
398
|
+
To open Excel with AgentXL right now:
|
|
399
|
+
agentxl install --open
|
|
400
|
+
`);
|
|
401
|
+
|
|
402
|
+
// Optionally open Excel
|
|
403
|
+
if (hasFlag("open")) {
|
|
404
|
+
try {
|
|
405
|
+
const devSettings = await import("office-addin-dev-settings");
|
|
406
|
+
const manifestLib = await import("office-addin-manifest");
|
|
407
|
+
step("⏳", "Opening Excel with AgentXL...");
|
|
408
|
+
await devSettings.sideloadAddIn(manifestPath, manifestLib.OfficeApp.Excel, false, devSettings.AppType.Desktop);
|
|
409
|
+
} catch (err) {
|
|
410
|
+
step("⚠️", `Could not open Excel: ${err.message}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function login() {
|
|
416
|
+
console.log("");
|
|
417
|
+
const authed = await runAuthFlow();
|
|
418
|
+
if (authed) {
|
|
419
|
+
console.log(" Run 'agentxl start' to launch the server.\n");
|
|
420
|
+
}
|
|
421
|
+
process.exit(authed ? 0 : 1);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ---------------------------------------------------------------------------
|
|
425
|
+
// Main
|
|
426
|
+
// ---------------------------------------------------------------------------
|
|
427
|
+
|
|
428
|
+
if (hasFlag("version") || command === "--version") {
|
|
429
|
+
console.log(VERSION);
|
|
430
|
+
process.exit(0);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (hasFlag("help") || command === "--help" || command === "help") {
|
|
434
|
+
printHelp();
|
|
435
|
+
process.exit(0);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (command === "start") {
|
|
439
|
+
start().catch((err) => {
|
|
440
|
+
console.error(`\n ❌ ${err.message || err}\n`);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
});
|
|
443
|
+
} else if (command === "install") {
|
|
444
|
+
install().catch((err) => {
|
|
445
|
+
console.error(`\n ❌ ${err.message || err}\n`);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
});
|
|
448
|
+
} else if (command === "login") {
|
|
449
|
+
login().catch((err) => {
|
|
450
|
+
console.error(`\n ❌ ${err.message || err}\n`);
|
|
451
|
+
process.exit(1);
|
|
452
|
+
});
|
|
453
|
+
} else if (!command) {
|
|
454
|
+
printHelp();
|
|
455
|
+
process.exit(0);
|
|
456
|
+
} else {
|
|
457
|
+
console.error(`Unknown command: ${command}`);
|
|
458
|
+
console.error(`Run 'agentxl --help' for usage.`);
|
|
459
|
+
process.exit(1);
|
|
460
|
+
}
|