@outlit/cli 1.3.1 → 1.5.0
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/cli.js +2434 -948
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -104,7 +104,7 @@ var package_default;
|
|
|
104
104
|
var init_package = __esm(() => {
|
|
105
105
|
package_default = {
|
|
106
106
|
name: "@outlit/cli",
|
|
107
|
-
version: "1.
|
|
107
|
+
version: "1.5.0",
|
|
108
108
|
description: "CLI for Outlit customer intelligence platform",
|
|
109
109
|
license: "Apache-2.0",
|
|
110
110
|
repository: {
|
|
@@ -120,7 +120,9 @@ var init_package = __esm(() => {
|
|
|
120
120
|
bin: {
|
|
121
121
|
outlit: "./dist/cli.js"
|
|
122
122
|
},
|
|
123
|
-
files: [
|
|
123
|
+
files: [
|
|
124
|
+
"dist"
|
|
125
|
+
],
|
|
124
126
|
scripts: {
|
|
125
127
|
dev: "bun run src/cli.ts",
|
|
126
128
|
"dev:watch": "bun --watch src/cli.ts",
|
|
@@ -136,15 +138,46 @@ var init_package = __esm(() => {
|
|
|
136
138
|
"@clack/prompts": "^1.0.1"
|
|
137
139
|
},
|
|
138
140
|
devDependencies: {
|
|
139
|
-
typescript: "^5.
|
|
141
|
+
typescript: "^5.9.3",
|
|
140
142
|
"@types/bun": "latest"
|
|
141
143
|
}
|
|
142
144
|
};
|
|
143
145
|
});
|
|
144
146
|
|
|
145
147
|
// src/lib/tty.ts
|
|
148
|
+
import { execFileSync } from "node:child_process";
|
|
149
|
+
import { createInterface } from "node:readline";
|
|
146
150
|
function openBrowserCmd() {
|
|
147
|
-
return process.platform === "darwin" ? "open" :
|
|
151
|
+
return process.platform === "darwin" ? "open" : "xdg-open";
|
|
152
|
+
}
|
|
153
|
+
function openBrowser(url) {
|
|
154
|
+
try {
|
|
155
|
+
if (process.platform === "win32") {
|
|
156
|
+
execFileSync("cmd", ["/c", "start", "", url.replace(/&/g, "^&")], { stdio: "ignore" });
|
|
157
|
+
} else {
|
|
158
|
+
execFileSync(openBrowserCmd(), [url], { stdio: "ignore" });
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function promptInput(label, opts) {
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
const rl = createInterface({
|
|
168
|
+
input: process.stdin,
|
|
169
|
+
output: opts?.secret ? undefined : process.stderr,
|
|
170
|
+
terminal: !opts?.secret
|
|
171
|
+
});
|
|
172
|
+
process.stderr.write(`${label}: `);
|
|
173
|
+
rl.question("", (answer) => {
|
|
174
|
+
rl.close();
|
|
175
|
+
if (opts?.secret)
|
|
176
|
+
process.stderr.write(`
|
|
177
|
+
`);
|
|
178
|
+
resolve(answer.trim());
|
|
179
|
+
});
|
|
180
|
+
});
|
|
148
181
|
}
|
|
149
182
|
function isInteractive() {
|
|
150
183
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
@@ -189,6 +222,70 @@ var init_output = __esm(() => {
|
|
|
189
222
|
init_tty();
|
|
190
223
|
});
|
|
191
224
|
|
|
225
|
+
// src/lib/config.ts
|
|
226
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
227
|
+
import { homedir } from "node:os";
|
|
228
|
+
import { dirname, join } from "node:path";
|
|
229
|
+
function isEnoentError(err) {
|
|
230
|
+
return err instanceof Error && err.code === "ENOENT";
|
|
231
|
+
}
|
|
232
|
+
function splitCsv(value) {
|
|
233
|
+
return value.split(",").map((s) => s.trim());
|
|
234
|
+
}
|
|
235
|
+
function getConfigDir() {
|
|
236
|
+
if (process.platform === "win32") {
|
|
237
|
+
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "outlit");
|
|
238
|
+
}
|
|
239
|
+
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "outlit");
|
|
240
|
+
}
|
|
241
|
+
function resolveApiKey(flagValue) {
|
|
242
|
+
if (flagValue)
|
|
243
|
+
return { key: flagValue, source: "flag" };
|
|
244
|
+
const envKey = process.env.OUTLIT_API_KEY;
|
|
245
|
+
if (envKey)
|
|
246
|
+
return { key: envKey, source: "env" };
|
|
247
|
+
const credPath = join(getConfigDir(), "credentials.json");
|
|
248
|
+
if (existsSync(credPath)) {
|
|
249
|
+
try {
|
|
250
|
+
const raw = readFileSync(credPath, "utf-8");
|
|
251
|
+
const config = JSON.parse(raw);
|
|
252
|
+
if (config.apiKey)
|
|
253
|
+
return { key: config.apiKey, source: "config" };
|
|
254
|
+
} catch {}
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
function maskKey(key) {
|
|
259
|
+
if (key.length <= 9)
|
|
260
|
+
return key;
|
|
261
|
+
return `${key.slice(0, 5)}...${key.slice(-4)}`;
|
|
262
|
+
}
|
|
263
|
+
function requireCredential(flagApiKey, json) {
|
|
264
|
+
const credential = resolveApiKey(flagApiKey);
|
|
265
|
+
if (!credential) {
|
|
266
|
+
return outputError({
|
|
267
|
+
message: "Not authenticated. Run `outlit auth login` or pass --api-key.",
|
|
268
|
+
code: "not_authenticated"
|
|
269
|
+
}, json);
|
|
270
|
+
}
|
|
271
|
+
return credential;
|
|
272
|
+
}
|
|
273
|
+
function storeApiKey(apiKey) {
|
|
274
|
+
const configDir = getConfigDir();
|
|
275
|
+
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
276
|
+
const credPath = join(configDir, "credentials.json");
|
|
277
|
+
writeFileSync(credPath, JSON.stringify({ apiKey }, null, 2), { mode: 384 });
|
|
278
|
+
return credPath;
|
|
279
|
+
}
|
|
280
|
+
var CLI_VERSION2, DEFAULT_API_URL = "https://app.outlit.ai", OUTLIT_DASHBOARD_URL = "https://app.outlit.ai/workspace-profile", OUTLIT_SIGNUP_URL = "https://app.outlit.ai/sign-up", TICK2;
|
|
281
|
+
var init_config = __esm(() => {
|
|
282
|
+
init_package();
|
|
283
|
+
init_output();
|
|
284
|
+
init_tty();
|
|
285
|
+
CLI_VERSION2 = package_default.version;
|
|
286
|
+
TICK2 = `\x1B[32m${isUnicodeSupported ? String.fromCodePoint(10003) : String.fromCodePoint(8730)}\x1B[0m`;
|
|
287
|
+
});
|
|
288
|
+
|
|
192
289
|
// ../../node_modules/.bun/citty@0.2.1/node_modules/citty/dist/index.mjs
|
|
193
290
|
function defineCommand2(def) {
|
|
194
291
|
return def;
|
|
@@ -679,7 +776,7 @@ var import_picocolors, import_sisteransi, at = (t) => t === 161 || t === 164 ||
|
|
|
679
776
|
` && (r && p && (i += st(r)), n && (i += it(n))), V += h.length, m = A, A = g.next();
|
|
680
777
|
}
|
|
681
778
|
return i;
|
|
682
|
-
}, At, _, bt, z, rt = (t) => ("columns" in t) && typeof t.columns == "number" ? t.columns : 80, nt = (t) => ("rows" in t) && typeof t.rows == "number" ? t.rows : 20, Vt, yt, Mt, Wt;
|
|
779
|
+
}, At, _, bt, z, rt = (t) => ("columns" in t) && typeof t.columns == "number" ? t.columns : 80, nt = (t) => ("rows" in t) && typeof t.rows == "number" ? t.rows : 20, Vt, kt, yt, Mt, Wt;
|
|
683
780
|
var init_dist2 = __esm(() => {
|
|
684
781
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
685
782
|
import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -758,6 +855,23 @@ var init_dist2 = __esm(() => {
|
|
|
758
855
|
}
|
|
759
856
|
}
|
|
760
857
|
};
|
|
858
|
+
kt = class kt extends x {
|
|
859
|
+
get cursor() {
|
|
860
|
+
return this.value ? 0 : 1;
|
|
861
|
+
}
|
|
862
|
+
get _value() {
|
|
863
|
+
return this.cursor === 0;
|
|
864
|
+
}
|
|
865
|
+
constructor(e) {
|
|
866
|
+
super(e, false), this.value = !!e.initialValue, this.on("userInput", () => {
|
|
867
|
+
this.value = this._value;
|
|
868
|
+
}), this.on("confirm", (s) => {
|
|
869
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
|
|
870
|
+
}), this.on("cursor", () => {
|
|
871
|
+
this.value = !this.value;
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
};
|
|
761
875
|
yt = class yt extends x {
|
|
762
876
|
options;
|
|
763
877
|
cursor = 0;
|
|
@@ -1073,6 +1187,32 @@ var import_picocolors2, import_sisteransi2, et2, ct2 = () => process.env.CI ===
|
|
|
1073
1187
|
for (const w of A)
|
|
1074
1188
|
B2.push(w);
|
|
1075
1189
|
return h && B2.push(g), B2;
|
|
1190
|
+
}, Re = (t) => {
|
|
1191
|
+
const r = t.active ?? "Yes", s = t.inactive ?? "No";
|
|
1192
|
+
return new kt({ active: r, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
|
|
1193
|
+
const i = t.withGuide ?? _.withGuide, a = `${i ? `${import_picocolors2.default.gray(d)}
|
|
1194
|
+
` : ""}${W2(this.state)} ${t.message}
|
|
1195
|
+
`, o = this.value ? r : s;
|
|
1196
|
+
switch (this.state) {
|
|
1197
|
+
case "submit": {
|
|
1198
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
1199
|
+
return `${a}${u}${import_picocolors2.default.dim(o)}`;
|
|
1200
|
+
}
|
|
1201
|
+
case "cancel": {
|
|
1202
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
1203
|
+
return `${a}${u}${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}${i ? `
|
|
1204
|
+
${import_picocolors2.default.gray(d)}` : ""}`;
|
|
1205
|
+
}
|
|
1206
|
+
default: {
|
|
1207
|
+
const u = i ? `${import_picocolors2.default.cyan(d)} ` : "", l = i ? import_picocolors2.default.cyan(x2) : "";
|
|
1208
|
+
return `${a}${u}${this.value ? `${import_picocolors2.default.green(Q2)} ${r}` : `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(r)}`}${t.vertical ? i ? `
|
|
1209
|
+
${import_picocolors2.default.cyan(d)} ` : `
|
|
1210
|
+
` : ` ${import_picocolors2.default.dim("/")} `}${this.value ? `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.green(Q2)} ${s}`}
|
|
1211
|
+
${l}
|
|
1212
|
+
`;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
} }).prompt();
|
|
1076
1216
|
}, R2, Ne = (t = "", r) => {
|
|
1077
1217
|
(r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(x2)} ${import_picocolors2.default.red(t)}
|
|
1078
1218
|
|
|
@@ -1304,12 +1444,12 @@ var exports_signup = {};
|
|
|
1304
1444
|
__export(exports_signup, {
|
|
1305
1445
|
default: () => signup_default
|
|
1306
1446
|
});
|
|
1307
|
-
|
|
1308
|
-
var OUTLIT_SIGNUP_URL = "https://app.outlit.ai/sign-up", signup_default;
|
|
1447
|
+
var signup_default;
|
|
1309
1448
|
var init_signup = __esm(() => {
|
|
1310
1449
|
init_dist3();
|
|
1311
1450
|
init_dist();
|
|
1312
1451
|
init_output2();
|
|
1452
|
+
init_config();
|
|
1313
1453
|
init_output();
|
|
1314
1454
|
init_tty();
|
|
1315
1455
|
signup_default = defineCommand2({
|
|
@@ -1335,124 +1475,19 @@ var init_signup = __esm(() => {
|
|
|
1335
1475
|
if (isInteractive()) {
|
|
1336
1476
|
We("Outlit CLI -- Sign Up");
|
|
1337
1477
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1478
|
+
const opened = openBrowser(OUTLIT_SIGNUP_URL);
|
|
1479
|
+
if (isInteractive()) {
|
|
1480
|
+
if (opened) {
|
|
1481
|
+
R2.info(`Opening ${OUTLIT_SIGNUP_URL}`);
|
|
1341
1482
|
} else {
|
|
1342
|
-
|
|
1483
|
+
R2.info(`Could not open browser. Visit ${OUTLIT_SIGNUP_URL}`);
|
|
1343
1484
|
}
|
|
1344
|
-
} catch {}
|
|
1345
|
-
if (isInteractive()) {
|
|
1346
|
-
R2.info(`Opening ${OUTLIT_SIGNUP_URL}`);
|
|
1347
1485
|
Le("Once you've signed up, run `outlit auth login` to store your API key.");
|
|
1348
1486
|
}
|
|
1349
1487
|
}
|
|
1350
1488
|
});
|
|
1351
1489
|
});
|
|
1352
1490
|
|
|
1353
|
-
// src/lib/config.ts
|
|
1354
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1355
|
-
import { homedir } from "node:os";
|
|
1356
|
-
import { dirname, join } from "node:path";
|
|
1357
|
-
function isEnoentError(err) {
|
|
1358
|
-
return err instanceof Error && err.code === "ENOENT";
|
|
1359
|
-
}
|
|
1360
|
-
function splitCsv(value) {
|
|
1361
|
-
return value.split(",").map((s) => s.trim());
|
|
1362
|
-
}
|
|
1363
|
-
function getClaudeDesktopConfigPath() {
|
|
1364
|
-
const home = homedir();
|
|
1365
|
-
switch (process.platform) {
|
|
1366
|
-
case "win32":
|
|
1367
|
-
return join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
1368
|
-
case "darwin":
|
|
1369
|
-
return join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
1370
|
-
default:
|
|
1371
|
-
return join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
function getConfigDir() {
|
|
1375
|
-
if (process.platform === "win32") {
|
|
1376
|
-
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "outlit");
|
|
1377
|
-
}
|
|
1378
|
-
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "outlit");
|
|
1379
|
-
}
|
|
1380
|
-
function resolveApiKey(flagValue) {
|
|
1381
|
-
if (flagValue)
|
|
1382
|
-
return { key: flagValue, source: "flag" };
|
|
1383
|
-
const envKey = process.env.OUTLIT_API_KEY;
|
|
1384
|
-
if (envKey)
|
|
1385
|
-
return { key: envKey, source: "env" };
|
|
1386
|
-
const credPath = join(getConfigDir(), "credentials.json");
|
|
1387
|
-
if (existsSync(credPath)) {
|
|
1388
|
-
try {
|
|
1389
|
-
const raw = readFileSync(credPath, "utf-8");
|
|
1390
|
-
const config = JSON.parse(raw);
|
|
1391
|
-
if (config.apiKey)
|
|
1392
|
-
return { key: config.apiKey, source: "config" };
|
|
1393
|
-
} catch {}
|
|
1394
|
-
}
|
|
1395
|
-
return null;
|
|
1396
|
-
}
|
|
1397
|
-
function maskKey(key) {
|
|
1398
|
-
if (key.length <= 9)
|
|
1399
|
-
return key;
|
|
1400
|
-
return `${key.slice(0, 5)}...${key.slice(-4)}`;
|
|
1401
|
-
}
|
|
1402
|
-
function readJsonConfig(filePath) {
|
|
1403
|
-
if (!existsSync(filePath))
|
|
1404
|
-
return {};
|
|
1405
|
-
try {
|
|
1406
|
-
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
1407
|
-
} catch {
|
|
1408
|
-
return {};
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
function requireCredential(flagApiKey, json) {
|
|
1412
|
-
const credential = resolveApiKey(flagApiKey);
|
|
1413
|
-
if (!credential) {
|
|
1414
|
-
return outputError({
|
|
1415
|
-
message: "Not authenticated. Run `outlit auth login` or pass --api-key.",
|
|
1416
|
-
code: "not_authenticated"
|
|
1417
|
-
}, json);
|
|
1418
|
-
}
|
|
1419
|
-
return credential;
|
|
1420
|
-
}
|
|
1421
|
-
function writeConfigFile(filePath, content, opts) {
|
|
1422
|
-
try {
|
|
1423
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
1424
|
-
writeFileSync(filePath, content);
|
|
1425
|
-
return true;
|
|
1426
|
-
} catch (err) {
|
|
1427
|
-
return outputError({ message: errorMessage(err, `Failed to write ${opts.label}`), code: "write_error" }, opts.json);
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
function mergeOutlitMcpConfig(configPath, serversKey, outlitConfig, opts) {
|
|
1431
|
-
const existing = readJsonConfig(configPath);
|
|
1432
|
-
const existingServers = existing[serversKey] ?? {};
|
|
1433
|
-
const merged = {
|
|
1434
|
-
...existing,
|
|
1435
|
-
[serversKey]: { ...existingServers, outlit: outlitConfig }
|
|
1436
|
-
};
|
|
1437
|
-
writeConfigFile(configPath, `${JSON.stringify(merged, null, 2)}
|
|
1438
|
-
`, opts);
|
|
1439
|
-
}
|
|
1440
|
-
function storeApiKey(apiKey) {
|
|
1441
|
-
const configDir = getConfigDir();
|
|
1442
|
-
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
1443
|
-
const credPath = join(configDir, "credentials.json");
|
|
1444
|
-
writeFileSync(credPath, JSON.stringify({ apiKey }, null, 2), { mode: 384 });
|
|
1445
|
-
return credPath;
|
|
1446
|
-
}
|
|
1447
|
-
var CLI_VERSION2, DEFAULT_MCP_URL = "https://mcp.outlit.ai/mcp", DEFAULT_API_URL = "https://app.outlit.ai", OUTLIT_DASHBOARD_URL = "https://app.outlit.ai/workspace-profile", TICK2;
|
|
1448
|
-
var init_config = __esm(() => {
|
|
1449
|
-
init_package();
|
|
1450
|
-
init_output();
|
|
1451
|
-
init_tty();
|
|
1452
|
-
CLI_VERSION2 = package_default.version;
|
|
1453
|
-
TICK2 = `\x1B[32m${isUnicodeSupported ? String.fromCodePoint(10003) : String.fromCodePoint(8730)}\x1B[0m`;
|
|
1454
|
-
});
|
|
1455
|
-
|
|
1456
1491
|
// src/lib/client.ts
|
|
1457
1492
|
function buildUrl(base, path, params) {
|
|
1458
1493
|
const url = new URL(path, base);
|
|
@@ -1461,6 +1496,8 @@ function buildUrl(base, path, params) {
|
|
|
1461
1496
|
continue;
|
|
1462
1497
|
if (Array.isArray(value)) {
|
|
1463
1498
|
url.searchParams.set(key, value.join(","));
|
|
1499
|
+
} else if (typeof value === "object") {
|
|
1500
|
+
url.searchParams.set(key, JSON.stringify(value));
|
|
1464
1501
|
} else {
|
|
1465
1502
|
url.searchParams.set(key, String(value));
|
|
1466
1503
|
}
|
|
@@ -1518,10 +1555,23 @@ var init_client = __esm(() => {
|
|
|
1518
1555
|
outlit_get_customer: { method: "POST", path: "/api/internal/mcp/customers" },
|
|
1519
1556
|
outlit_list_users: { method: "GET", path: "/api/internal/mcp/users" },
|
|
1520
1557
|
outlit_get_timeline: { method: "POST", path: "/api/internal/mcp/timeline" },
|
|
1521
|
-
|
|
1558
|
+
outlit_list_facts: { method: "POST", path: "/api/internal/mcp/facts" },
|
|
1559
|
+
outlit_get_fact: { method: "POST", path: "/api/internal/mcp/facts/get" },
|
|
1560
|
+
outlit_get_source: { method: "POST", path: "/api/internal/mcp/context-source" },
|
|
1522
1561
|
outlit_schema: { method: "GET", path: "/api/internal/mcp/sql-schema" },
|
|
1523
1562
|
outlit_query: { method: "POST", path: "/api/internal/mcp/sql" },
|
|
1524
|
-
outlit_search_customer_context: { method: "POST", path: "/api/internal/mcp/context-search" }
|
|
1563
|
+
outlit_search_customer_context: { method: "POST", path: "/api/internal/mcp/context-search" },
|
|
1564
|
+
outlit_list_integrations: { method: "GET", path: "/api/internal/mcp/integrations" },
|
|
1565
|
+
outlit_connect_integration: { method: "POST", path: "/api/internal/mcp/integrations/connect" },
|
|
1566
|
+
outlit_connect_status: { method: "GET", path: "/api/internal/mcp/integrations/connect/status" },
|
|
1567
|
+
outlit_disconnect_integration: {
|
|
1568
|
+
method: "POST",
|
|
1569
|
+
path: "/api/internal/mcp/integrations/disconnect"
|
|
1570
|
+
},
|
|
1571
|
+
outlit_integration_sync_status: {
|
|
1572
|
+
method: "GET",
|
|
1573
|
+
path: "/api/internal/mcp/integrations/sync-status"
|
|
1574
|
+
}
|
|
1525
1575
|
};
|
|
1526
1576
|
});
|
|
1527
1577
|
|
|
@@ -1649,8 +1699,26 @@ async function getClientOrExit(flagApiKey, json) {
|
|
|
1649
1699
|
return createClient(flagApiKey).catch((err) => outputError({ message: errorMessage(err, "Authentication failed"), code: "auth_required" }, json));
|
|
1650
1700
|
}
|
|
1651
1701
|
async function pingApiKey(apiKey) {
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1702
|
+
const baseUrl = process.env.OUTLIT_API_URL ?? DEFAULT_API_URL;
|
|
1703
|
+
const url = new URL("/api/internal/mcp/validate-api-key", baseUrl).toString();
|
|
1704
|
+
const response = await globalThis.fetch(url, {
|
|
1705
|
+
method: "POST",
|
|
1706
|
+
headers: {
|
|
1707
|
+
Authorization: `Bearer ${apiKey}`
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
const text = await response.text();
|
|
1711
|
+
const payload = text.length > 0 ? (() => {
|
|
1712
|
+
try {
|
|
1713
|
+
return JSON.parse(text);
|
|
1714
|
+
} catch {
|
|
1715
|
+
return null;
|
|
1716
|
+
}
|
|
1717
|
+
})() : null;
|
|
1718
|
+
if (!response.ok || !payload?.valid) {
|
|
1719
|
+
const message = payload?.error ?? (text.length > 0 ? text : `API error (${response.status})`);
|
|
1720
|
+
throw new Error(message);
|
|
1721
|
+
}
|
|
1654
1722
|
}
|
|
1655
1723
|
async function validateKeyOrExit(apiKey, json) {
|
|
1656
1724
|
try {
|
|
@@ -1702,6 +1770,7 @@ async function runTool(client, toolName, params, json, opts) {
|
|
|
1702
1770
|
}
|
|
1703
1771
|
var init_api = __esm(() => {
|
|
1704
1772
|
init_client();
|
|
1773
|
+
init_config();
|
|
1705
1774
|
init_output();
|
|
1706
1775
|
init_spinner();
|
|
1707
1776
|
init_table();
|
|
@@ -1712,7 +1781,6 @@ var exports_login = {};
|
|
|
1712
1781
|
__export(exports_login, {
|
|
1713
1782
|
default: () => login_default
|
|
1714
1783
|
});
|
|
1715
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
1716
1784
|
var login_default;
|
|
1717
1785
|
var init_login = __esm(() => {
|
|
1718
1786
|
init_dist3();
|
|
@@ -1773,14 +1841,12 @@ Format: ok_ followed by 32+ alphanumeric characters.`
|
|
|
1773
1841
|
if (Ct(method))
|
|
1774
1842
|
cancelLogin();
|
|
1775
1843
|
if (method === "browser") {
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
} catch {}
|
|
1783
|
-
R2.info("Opening browser... paste your key once you have it.");
|
|
1844
|
+
const opened = openBrowser(OUTLIT_DASHBOARD_URL);
|
|
1845
|
+
if (opened) {
|
|
1846
|
+
R2.info("Opening browser... paste your key once you have it.");
|
|
1847
|
+
} else {
|
|
1848
|
+
R2.info(`Could not open browser. Visit ${OUTLIT_DASHBOARD_URL} manually.`);
|
|
1849
|
+
}
|
|
1784
1850
|
}
|
|
1785
1851
|
const result = await He({
|
|
1786
1852
|
message: "Paste your Outlit API key:",
|
|
@@ -1838,8 +1904,8 @@ var exports_logout = {};
|
|
|
1838
1904
|
__export(exports_logout, {
|
|
1839
1905
|
default: () => logout_default
|
|
1840
1906
|
});
|
|
1841
|
-
import { rmSync } from "node:fs";
|
|
1842
|
-
import { join as
|
|
1907
|
+
import { rmSync as rmSync2 } from "node:fs";
|
|
1908
|
+
import { join as join3 } from "node:path";
|
|
1843
1909
|
var logout_default;
|
|
1844
1910
|
var init_logout = __esm(() => {
|
|
1845
1911
|
init_dist();
|
|
@@ -1865,13 +1931,13 @@ var init_logout = __esm(() => {
|
|
|
1865
1931
|
async run({ args }) {
|
|
1866
1932
|
const json = !!args.json;
|
|
1867
1933
|
const configDir = getConfigDir();
|
|
1868
|
-
const credPath =
|
|
1934
|
+
const credPath = join3(configDir, "credentials.json");
|
|
1869
1935
|
if (process.env.OUTLIT_API_KEY) {
|
|
1870
1936
|
process.stderr.write(`Warning: OUTLIT_API_KEY env var is still set and will continue to work after logout.
|
|
1871
1937
|
`);
|
|
1872
1938
|
}
|
|
1873
1939
|
try {
|
|
1874
|
-
|
|
1940
|
+
rmSync2(credPath, { force: true });
|
|
1875
1941
|
} catch (err) {
|
|
1876
1942
|
if (!isEnoentError(err)) {
|
|
1877
1943
|
return outputError({ message: errorMessage(err, "Failed to remove credentials file"), code: "unlink_error" }, json);
|
|
@@ -2026,6 +2092,38 @@ var init_auth2 = __esm(() => {
|
|
|
2026
2092
|
});
|
|
2027
2093
|
|
|
2028
2094
|
// src/args/filters.ts
|
|
2095
|
+
function parseTraitFilterValue(value) {
|
|
2096
|
+
if (value === "true")
|
|
2097
|
+
return true;
|
|
2098
|
+
if (value === "false")
|
|
2099
|
+
return false;
|
|
2100
|
+
const parsed = Number(value);
|
|
2101
|
+
if (value.trim() !== "" && Number.isFinite(parsed)) {
|
|
2102
|
+
return parsed;
|
|
2103
|
+
}
|
|
2104
|
+
return value;
|
|
2105
|
+
}
|
|
2106
|
+
function parseTraitFilters(input) {
|
|
2107
|
+
if (!input)
|
|
2108
|
+
return;
|
|
2109
|
+
const entries = input.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
2110
|
+
if (entries.length === 0)
|
|
2111
|
+
return;
|
|
2112
|
+
const filters = {};
|
|
2113
|
+
for (const entry of entries) {
|
|
2114
|
+
const separatorIndex = entry.indexOf("=");
|
|
2115
|
+
if (separatorIndex <= 0 || separatorIndex === entry.length - 1) {
|
|
2116
|
+
throw new Error(`Invalid trait filter "${entry}". Expected key=value.`);
|
|
2117
|
+
}
|
|
2118
|
+
const key = entry.slice(0, separatorIndex).trim();
|
|
2119
|
+
const rawValue = entry.slice(separatorIndex + 1).trim();
|
|
2120
|
+
if (!/^[A-Za-z0-9_-]{1,100}$/.test(key)) {
|
|
2121
|
+
throw new Error(`Invalid trait filter key "${key}". Keys may only contain letters, numbers, underscores, and dashes.`);
|
|
2122
|
+
}
|
|
2123
|
+
filters[key] = parseTraitFilterValue(rawValue);
|
|
2124
|
+
}
|
|
2125
|
+
return filters;
|
|
2126
|
+
}
|
|
2029
2127
|
function applyListFilters(params, args) {
|
|
2030
2128
|
if (args["no-activity-in"])
|
|
2031
2129
|
params.noActivityInLast = args["no-activity-in"];
|
|
@@ -2038,7 +2136,7 @@ function applyListFilters(params, args) {
|
|
|
2038
2136
|
if (args["order-direction"])
|
|
2039
2137
|
params.orderDirection = args["order-direction"];
|
|
2040
2138
|
}
|
|
2041
|
-
var activityFilterArgs, orderArgs;
|
|
2139
|
+
var activityFilterArgs, orderArgs, traitFilterArgs;
|
|
2042
2140
|
var init_filters = __esm(() => {
|
|
2043
2141
|
activityFilterArgs = {
|
|
2044
2142
|
"no-activity-in": {
|
|
@@ -2062,6 +2160,12 @@ var init_filters = __esm(() => {
|
|
|
2062
2160
|
default: "desc"
|
|
2063
2161
|
}
|
|
2064
2162
|
};
|
|
2163
|
+
traitFilterArgs = {
|
|
2164
|
+
trait: {
|
|
2165
|
+
type: "string",
|
|
2166
|
+
description: "Filter by traits using key=value pairs. Separate multiple filters with commas (e.g. role=admin,active=true)"
|
|
2167
|
+
}
|
|
2168
|
+
};
|
|
2065
2169
|
});
|
|
2066
2170
|
|
|
2067
2171
|
// src/args/pagination.ts
|
|
@@ -2097,6 +2201,148 @@ Use this to fetch the next page of results.`
|
|
|
2097
2201
|
};
|
|
2098
2202
|
});
|
|
2099
2203
|
|
|
2204
|
+
// src/generated/tool-contracts.ts
|
|
2205
|
+
function resolveCustomerContextSearchInput(value) {
|
|
2206
|
+
if (!value.query) {
|
|
2207
|
+
return {
|
|
2208
|
+
ok: false,
|
|
2209
|
+
message: "A query argument is required"
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
const normalizedQuery = value.query.trim();
|
|
2213
|
+
if (normalizedQuery.length < 2) {
|
|
2214
|
+
return {
|
|
2215
|
+
ok: false,
|
|
2216
|
+
message: "Query must be at least 2 non-whitespace characters"
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
if (value.after !== undefined && value.before !== undefined && new Date(value.after).getTime() > new Date(value.before).getTime()) {
|
|
2220
|
+
return {
|
|
2221
|
+
ok: false,
|
|
2222
|
+
message: "--after must be before or equal to --before"
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
return {
|
|
2226
|
+
ok: true,
|
|
2227
|
+
request: {
|
|
2228
|
+
query: normalizedQuery,
|
|
2229
|
+
customer: value.customer,
|
|
2230
|
+
topK: value.topK,
|
|
2231
|
+
after: value.after,
|
|
2232
|
+
before: value.before,
|
|
2233
|
+
sourceTypes: value.sourceTypes
|
|
2234
|
+
}
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
var customerToolContracts, customerBillingStatuses, customerFactStatuses, customerIncludeSections, customerSourceTypes, customerTimeframes, timelineChannels, timelineTimeframes, userJourneyStages, schemaTables;
|
|
2238
|
+
var init_tool_contracts = __esm(() => {
|
|
2239
|
+
customerToolContracts = {
|
|
2240
|
+
outlit_list_customers: {
|
|
2241
|
+
toolName: "outlit_list_customers",
|
|
2242
|
+
description: "Browse and filter customers. Use this to find customers by billing status, activity recency, revenue, or name. Returns a paginated list with summary info (MRR, last activity, status)."
|
|
2243
|
+
},
|
|
2244
|
+
outlit_list_users: {
|
|
2245
|
+
toolName: "outlit_list_users",
|
|
2246
|
+
description: "Browse and filter users. Use this to find users by journey stage, activity recency, customer, or email/name. Returns a paginated list with activity info."
|
|
2247
|
+
},
|
|
2248
|
+
outlit_get_customer: {
|
|
2249
|
+
toolName: "outlit_get_customer",
|
|
2250
|
+
description: "Get full details for a single customer. Use this when you already know which customer you want to inspect. Optionally include related data (users, revenue, recent activity, engagement metrics)."
|
|
2251
|
+
},
|
|
2252
|
+
outlit_get_timeline: {
|
|
2253
|
+
toolName: "outlit_get_timeline",
|
|
2254
|
+
description: "Get the chronological activity timeline for a customer. Use this to see what happened and when — emails, calls, Slack messages, billing events, etc. Supports channel and date filtering."
|
|
2255
|
+
},
|
|
2256
|
+
outlit_list_facts: {
|
|
2257
|
+
toolName: "outlit_list_facts",
|
|
2258
|
+
description: "List structured facts known about a customer. Use filters like status, sourceTypes, and date bounds to narrow the result set. For topic-specific retrieval, use outlit_search_customer_context instead."
|
|
2259
|
+
},
|
|
2260
|
+
outlit_get_fact: {
|
|
2261
|
+
toolName: "outlit_get_fact",
|
|
2262
|
+
description: "Get one exact fact by ID. Returns the canonical fact shape and optionally expands requested related data such as evidence."
|
|
2263
|
+
},
|
|
2264
|
+
outlit_get_source: {
|
|
2265
|
+
toolName: "outlit_get_source",
|
|
2266
|
+
description: "Get one exact source record by generic sourceType and sourceId. Use this when you already know the concrete underlying source you want to inspect."
|
|
2267
|
+
},
|
|
2268
|
+
outlit_search_customer_context: {
|
|
2269
|
+
toolName: "outlit_search_customer_context",
|
|
2270
|
+
description: "Search across all known customer context using a natural-language query. Returns grouped artifact-level results for matching sources and facts. Omit customer to search across all customers in the organization."
|
|
2271
|
+
},
|
|
2272
|
+
outlit_query: {
|
|
2273
|
+
toolName: "outlit_query",
|
|
2274
|
+
description: `Execute raw SQL queries against your analytics data.
|
|
2275
|
+
|
|
2276
|
+
Available tables:
|
|
2277
|
+
- events: Customer activity events (event_type, event_channel, customer_id, occurred_at, properties, ...)
|
|
2278
|
+
- customer_dimensions: Customer attributes (customer_id, domain, name, billing_status, plan, mrr_cents, ...)
|
|
2279
|
+
- user_dimensions: User attributes (user_id, email, name, customer_id, ...)
|
|
2280
|
+
- mrr_snapshots: Revenue snapshots over time (customer_id, snapshot_date, mrr_cents, ...)
|
|
2281
|
+
|
|
2282
|
+
All queries are automatically filtered to your organization's data.
|
|
2283
|
+
Only SELECT queries are allowed.
|
|
2284
|
+
|
|
2285
|
+
Example queries:
|
|
2286
|
+
- SELECT event_type, count(*) FROM events GROUP BY 1 ORDER BY 2 DESC LIMIT 10
|
|
2287
|
+
- SELECT billing_status, sum(mrr_cents)/100 as mrr FROM customer_dimensions GROUP BY 1
|
|
2288
|
+
- SELECT * FROM events WHERE customer_id = 'cust_123' ORDER BY occurred_at DESC LIMIT 50`
|
|
2289
|
+
},
|
|
2290
|
+
outlit_schema: {
|
|
2291
|
+
toolName: "outlit_schema",
|
|
2292
|
+
description: `Get table schemas for available analytics tables.
|
|
2293
|
+
|
|
2294
|
+
Use this to discover column names, types, and descriptions before writing SQL queries.
|
|
2295
|
+
Returns column definitions and example queries for each table.`
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
customerBillingStatuses = [
|
|
2299
|
+
"NONE",
|
|
2300
|
+
"TRIALING",
|
|
2301
|
+
"PAYING",
|
|
2302
|
+
"PAST_DUE",
|
|
2303
|
+
"CHURNED"
|
|
2304
|
+
];
|
|
2305
|
+
customerFactStatuses = [
|
|
2306
|
+
"ACTIVE",
|
|
2307
|
+
"ACKNOWLEDGED",
|
|
2308
|
+
"RESOLVED",
|
|
2309
|
+
"SNOOZED",
|
|
2310
|
+
"CANDIDATE"
|
|
2311
|
+
];
|
|
2312
|
+
customerIncludeSections = [
|
|
2313
|
+
"users",
|
|
2314
|
+
"revenue",
|
|
2315
|
+
"recentTimeline",
|
|
2316
|
+
"behaviorMetrics"
|
|
2317
|
+
];
|
|
2318
|
+
customerSourceTypes = ["EMAIL", "CALL", "CALENDAR_EVENT", "SUPPORT_TICKET"];
|
|
2319
|
+
customerTimeframes = ["7d", "14d", "30d", "90d"];
|
|
2320
|
+
timelineChannels = [
|
|
2321
|
+
"SDK",
|
|
2322
|
+
"EMAIL",
|
|
2323
|
+
"SLACK",
|
|
2324
|
+
"CALL",
|
|
2325
|
+
"CRM",
|
|
2326
|
+
"BILLING",
|
|
2327
|
+
"SUPPORT",
|
|
2328
|
+
"INTERNAL"
|
|
2329
|
+
];
|
|
2330
|
+
timelineTimeframes = ["7d", "14d", "30d", "90d", "all"];
|
|
2331
|
+
userJourneyStages = [
|
|
2332
|
+
"DISCOVERED",
|
|
2333
|
+
"SIGNED_UP",
|
|
2334
|
+
"ACTIVATED",
|
|
2335
|
+
"ENGAGED",
|
|
2336
|
+
"INACTIVE"
|
|
2337
|
+
];
|
|
2338
|
+
schemaTables = [
|
|
2339
|
+
"events",
|
|
2340
|
+
"customer_dimensions",
|
|
2341
|
+
"user_dimensions",
|
|
2342
|
+
"mrr_snapshots"
|
|
2343
|
+
];
|
|
2344
|
+
});
|
|
2345
|
+
|
|
2100
2346
|
// src/lib/format.ts
|
|
2101
2347
|
function formatCents(value) {
|
|
2102
2348
|
if (value == null || typeof value !== "number" || Number.isNaN(value))
|
|
@@ -2137,6 +2383,16 @@ function truncate(value, maxLen) {
|
|
|
2137
2383
|
return "...".slice(0, maxLen);
|
|
2138
2384
|
return `${str.slice(0, maxLen - 3)}...`;
|
|
2139
2385
|
}
|
|
2386
|
+
function capitalize(value) {
|
|
2387
|
+
if (value == null || typeof value !== "string")
|
|
2388
|
+
return "--";
|
|
2389
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
2390
|
+
}
|
|
2391
|
+
function formatNumber(value) {
|
|
2392
|
+
if (value == null || typeof value !== "number" || Number.isNaN(value))
|
|
2393
|
+
return "--";
|
|
2394
|
+
return value.toLocaleString("en-US");
|
|
2395
|
+
}
|
|
2140
2396
|
|
|
2141
2397
|
// src/commands/customers/list.ts
|
|
2142
2398
|
var exports_list = {};
|
|
@@ -2150,6 +2406,7 @@ var init_list = __esm(() => {
|
|
|
2150
2406
|
init_filters();
|
|
2151
2407
|
init_output2();
|
|
2152
2408
|
init_pagination();
|
|
2409
|
+
init_tool_contracts();
|
|
2153
2410
|
init_api();
|
|
2154
2411
|
init_output();
|
|
2155
2412
|
list_default = defineCommand2({
|
|
@@ -2168,7 +2425,7 @@ var init_list = __esm(() => {
|
|
|
2168
2425
|
" outlit customers list --mrr-above 10000 --limit 50 # high-value at-risk",
|
|
2169
2426
|
" outlit customers list --json | jq '.items[].domain' # pipe-friendly",
|
|
2170
2427
|
"",
|
|
2171
|
-
|
|
2428
|
+
`Billing statuses: ${customerBillingStatuses.join(", ")}`,
|
|
2172
2429
|
"Activity periods: 7d, 14d, 30d, 90d",
|
|
2173
2430
|
"",
|
|
2174
2431
|
AGENT_JSON_HINT
|
|
@@ -2180,10 +2437,11 @@ var init_list = __esm(() => {
|
|
|
2180
2437
|
...outputArgs,
|
|
2181
2438
|
...paginationArgs,
|
|
2182
2439
|
...activityFilterArgs,
|
|
2440
|
+
...traitFilterArgs,
|
|
2183
2441
|
...orderArgs,
|
|
2184
2442
|
"billing-status": {
|
|
2185
2443
|
type: "string",
|
|
2186
|
-
description:
|
|
2444
|
+
description: `Filter by billing status (${customerBillingStatuses.join(", ")})`
|
|
2187
2445
|
},
|
|
2188
2446
|
"mrr-above": {
|
|
2189
2447
|
type: "string",
|
|
@@ -2196,14 +2454,6 @@ var init_list = __esm(() => {
|
|
|
2196
2454
|
search: {
|
|
2197
2455
|
type: "string",
|
|
2198
2456
|
description: "Search by customer name or domain"
|
|
2199
|
-
},
|
|
2200
|
-
status: {
|
|
2201
|
-
type: "string",
|
|
2202
|
-
description: "Customer status filter (PROVISIONAL, ACTIVE, CHURNED, MERGED)"
|
|
2203
|
-
},
|
|
2204
|
-
type: {
|
|
2205
|
-
type: "string",
|
|
2206
|
-
description: "Customer type filter (COMPANY, INDIVIDUAL)"
|
|
2207
2457
|
}
|
|
2208
2458
|
},
|
|
2209
2459
|
async run({ args }) {
|
|
@@ -2226,16 +2476,25 @@ var init_list = __esm(() => {
|
|
|
2226
2476
|
}
|
|
2227
2477
|
params.mrrBelow = value;
|
|
2228
2478
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2479
|
+
if (args.trait) {
|
|
2480
|
+
try {
|
|
2481
|
+
const traitFilters = parseTraitFilters(args.trait);
|
|
2482
|
+
if (traitFilters) {
|
|
2483
|
+
params.traitFilters = traitFilters;
|
|
2484
|
+
}
|
|
2485
|
+
} catch (error) {
|
|
2486
|
+
return outputError({
|
|
2487
|
+
message: error instanceof Error ? error.message : "Invalid --trait filter",
|
|
2488
|
+
code: "invalid_input"
|
|
2489
|
+
}, json);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
applyListFilters(params, args);
|
|
2493
|
+
applyPagination(params, args, json);
|
|
2494
|
+
return runTool(client, customerToolContracts.outlit_list_customers.toolName, params, json, {
|
|
2495
|
+
spinnerMessage: "Fetching customers...",
|
|
2496
|
+
table: {
|
|
2497
|
+
columns: [
|
|
2239
2498
|
{ header: "Name", key: "name", format: (v) => truncate(v, 24) },
|
|
2240
2499
|
{ header: "Domain", key: "domain" },
|
|
2241
2500
|
{ header: "Billing", key: "billingStatus" },
|
|
@@ -2258,6 +2517,7 @@ var init_get = __esm(() => {
|
|
|
2258
2517
|
init_dist();
|
|
2259
2518
|
init_auth();
|
|
2260
2519
|
init_output2();
|
|
2520
|
+
init_tool_contracts();
|
|
2261
2521
|
init_api();
|
|
2262
2522
|
init_config();
|
|
2263
2523
|
get_default = defineCommand2({
|
|
@@ -2274,6 +2534,9 @@ var init_get = __esm(() => {
|
|
|
2274
2534
|
'Naming note: --include users returns data under the "contacts" key in',
|
|
2275
2535
|
"the response. This is a server-side naming inconsistency, not a CLI bug.",
|
|
2276
2536
|
"",
|
|
2537
|
+
`Available include sections: ${customerIncludeSections.join(", ")}`,
|
|
2538
|
+
`Timeframes: ${customerTimeframes.join(", ")}`,
|
|
2539
|
+
"",
|
|
2277
2540
|
"Examples:",
|
|
2278
2541
|
" outlit customers get acme.com",
|
|
2279
2542
|
" outlit customers get acme.com --include users,revenue",
|
|
@@ -2295,7 +2558,7 @@ var init_get = __esm(() => {
|
|
|
2295
2558
|
type: "string",
|
|
2296
2559
|
description: [
|
|
2297
2560
|
"Comma-separated sections to include in response.",
|
|
2298
|
-
|
|
2561
|
+
`Available: ${customerIncludeSections.join(", ")}`,
|
|
2299
2562
|
'Note: "users" maps to "contacts" in the response (server naming).'
|
|
2300
2563
|
].join(`
|
|
2301
2564
|
`)
|
|
@@ -2316,7 +2579,7 @@ var init_get = __esm(() => {
|
|
|
2316
2579
|
if (args.include) {
|
|
2317
2580
|
params.include = splitCsv(args.include);
|
|
2318
2581
|
}
|
|
2319
|
-
return runTool(client,
|
|
2582
|
+
return runTool(client, customerToolContracts.outlit_get_customer.toolName, params, json);
|
|
2320
2583
|
}
|
|
2321
2584
|
});
|
|
2322
2585
|
});
|
|
@@ -2332,6 +2595,7 @@ var init_timeline = __esm(() => {
|
|
|
2332
2595
|
init_auth();
|
|
2333
2596
|
init_output2();
|
|
2334
2597
|
init_pagination();
|
|
2598
|
+
init_tool_contracts();
|
|
2335
2599
|
init_api();
|
|
2336
2600
|
init_config();
|
|
2337
2601
|
timeline_default = defineCommand2({
|
|
@@ -2346,6 +2610,8 @@ var init_timeline = __esm(() => {
|
|
|
2346
2610
|
"",
|
|
2347
2611
|
"Timeframe is used when no explicit date range is set.",
|
|
2348
2612
|
"When --start-date or --end-date is provided, --timeframe is ignored.",
|
|
2613
|
+
`Channels: ${timelineChannels.join(", ")}`,
|
|
2614
|
+
`Timeframes: ${timelineTimeframes.join(", ")}`,
|
|
2349
2615
|
"",
|
|
2350
2616
|
"Examples:",
|
|
2351
2617
|
" outlit customers timeline acme.com",
|
|
@@ -2369,7 +2635,7 @@ var init_timeline = __esm(() => {
|
|
|
2369
2635
|
},
|
|
2370
2636
|
channels: {
|
|
2371
2637
|
type: "string",
|
|
2372
|
-
description:
|
|
2638
|
+
description: `Comma-separated list of channels to filter (${timelineChannels.join(", ")})`
|
|
2373
2639
|
},
|
|
2374
2640
|
"event-types": {
|
|
2375
2641
|
type: "string",
|
|
@@ -2377,7 +2643,7 @@ var init_timeline = __esm(() => {
|
|
|
2377
2643
|
},
|
|
2378
2644
|
timeframe: {
|
|
2379
2645
|
type: "string",
|
|
2380
|
-
description:
|
|
2646
|
+
description: `Timeframe for events (${timelineTimeframes.join(", ")}). Ignored when --start-date or --end-date is set.`,
|
|
2381
2647
|
default: "30d"
|
|
2382
2648
|
},
|
|
2383
2649
|
"start-date": {
|
|
@@ -2409,7 +2675,7 @@ var init_timeline = __esm(() => {
|
|
|
2409
2675
|
params.eventTypes = splitCsv(args["event-types"]);
|
|
2410
2676
|
}
|
|
2411
2677
|
applyPagination(params, args, json);
|
|
2412
|
-
return runTool(client,
|
|
2678
|
+
return runTool(client, customerToolContracts.outlit_get_timeline.toolName, params, json);
|
|
2413
2679
|
}
|
|
2414
2680
|
});
|
|
2415
2681
|
});
|
|
@@ -2458,7 +2724,9 @@ var init_list2 = __esm(() => {
|
|
|
2458
2724
|
init_filters();
|
|
2459
2725
|
init_output2();
|
|
2460
2726
|
init_pagination();
|
|
2727
|
+
init_tool_contracts();
|
|
2461
2728
|
init_api();
|
|
2729
|
+
init_output();
|
|
2462
2730
|
list_default2 = defineCommand2({
|
|
2463
2731
|
meta: {
|
|
2464
2732
|
name: "list",
|
|
@@ -2470,7 +2738,7 @@ var init_list2 = __esm(() => {
|
|
|
2470
2738
|
"",
|
|
2471
2739
|
"Examples:",
|
|
2472
2740
|
" outlit users list # all users",
|
|
2473
|
-
" outlit users list --journey-stage
|
|
2741
|
+
" outlit users list --journey-stage ENGAGED # engaged users only",
|
|
2474
2742
|
" outlit users list --customer-id <uuid> # users for a customer",
|
|
2475
2743
|
" outlit users list --no-activity-in 30d # inactive users",
|
|
2476
2744
|
" outlit users list --search alice --order-by last_activity_at",
|
|
@@ -2484,9 +2752,10 @@ var init_list2 = __esm(() => {
|
|
|
2484
2752
|
...authArgs,
|
|
2485
2753
|
...outputArgs,
|
|
2486
2754
|
...paginationArgs,
|
|
2755
|
+
...traitFilterArgs,
|
|
2487
2756
|
"journey-stage": {
|
|
2488
2757
|
type: "string",
|
|
2489
|
-
description:
|
|
2758
|
+
description: `Filter by journey stage (${userJourneyStages.join(", ")})`
|
|
2490
2759
|
},
|
|
2491
2760
|
"customer-id": {
|
|
2492
2761
|
type: "string",
|
|
@@ -2507,9 +2776,22 @@ var init_list2 = __esm(() => {
|
|
|
2507
2776
|
params.journeyStage = args["journey-stage"];
|
|
2508
2777
|
if (args["customer-id"])
|
|
2509
2778
|
params.customerId = args["customer-id"];
|
|
2779
|
+
if (args.trait) {
|
|
2780
|
+
try {
|
|
2781
|
+
const traitFilters = parseTraitFilters(args.trait);
|
|
2782
|
+
if (traitFilters) {
|
|
2783
|
+
params.traitFilters = traitFilters;
|
|
2784
|
+
}
|
|
2785
|
+
} catch (error) {
|
|
2786
|
+
return outputError({
|
|
2787
|
+
message: error instanceof Error ? error.message : "Invalid --trait filter",
|
|
2788
|
+
code: "invalid_input"
|
|
2789
|
+
}, json);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2510
2792
|
applyListFilters(params, args);
|
|
2511
2793
|
applyPagination(params, args, json);
|
|
2512
|
-
return runTool(client,
|
|
2794
|
+
return runTool(client, customerToolContracts.outlit_list_users.toolName, params, json, {
|
|
2513
2795
|
spinnerMessage: "Fetching users...",
|
|
2514
2796
|
table: {
|
|
2515
2797
|
columns: [
|
|
@@ -2553,361 +2835,177 @@ var init_users = __esm(() => {
|
|
|
2553
2835
|
});
|
|
2554
2836
|
});
|
|
2555
2837
|
|
|
2556
|
-
// src/lib/
|
|
2557
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
if (isEnoentError(err)) {
|
|
2573
|
-
return outputError({ message: opts.notFoundMessage, code: opts.notFoundCode }, json);
|
|
2574
|
-
}
|
|
2575
|
-
opts.extraErrorHandler?.(err, json);
|
|
2576
|
-
return outputError({ message: errorMessage(err, `${opts.cliName} mcp add failed`), code: "exec_error" }, json);
|
|
2577
|
-
}
|
|
2578
|
-
return { success: false, error: errorMessage(err, `${opts.cliName} mcp add failed`) };
|
|
2579
|
-
}
|
|
2580
|
-
if (exitOnError) {
|
|
2581
|
-
if (isJsonMode(json)) {
|
|
2582
|
-
outputResult({ success: true, agent: opts.agentId });
|
|
2583
|
-
return { success: true };
|
|
2584
|
-
}
|
|
2585
|
-
console.log(`${TICK2} ${opts.successMessage}`);
|
|
2838
|
+
// src/lib/update.ts
|
|
2839
|
+
import { execFileSync as execFileSync3, spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
|
|
2840
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, realpathSync as realpathSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2841
|
+
import { homedir as homedir3 } from "node:os";
|
|
2842
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2843
|
+
function compareVersions2(a, b) {
|
|
2844
|
+
const aParts = a.split("-")[0]?.split(".").map((part) => Number.parseInt(part, 10) || 0) ?? [];
|
|
2845
|
+
const bParts = b.split("-")[0]?.split(".").map((part) => Number.parseInt(part, 10) || 0) ?? [];
|
|
2846
|
+
const maxLength = Math.max(aParts.length, bParts.length);
|
|
2847
|
+
for (let index = 0;index < maxLength; index++) {
|
|
2848
|
+
const left = aParts[index] ?? 0;
|
|
2849
|
+
const right = bParts[index] ?? 0;
|
|
2850
|
+
if (left > right)
|
|
2851
|
+
return 1;
|
|
2852
|
+
if (left < right)
|
|
2853
|
+
return -1;
|
|
2586
2854
|
}
|
|
2587
|
-
return
|
|
2855
|
+
return 0;
|
|
2588
2856
|
}
|
|
2589
|
-
function
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2857
|
+
function inferInstallerFromUserAgent2(agent) {
|
|
2858
|
+
if (agent.startsWith("bun/"))
|
|
2859
|
+
return "bun";
|
|
2860
|
+
if (agent.startsWith("npm/"))
|
|
2861
|
+
return "npm";
|
|
2862
|
+
if (agent.startsWith("pnpm/"))
|
|
2863
|
+
return "pnpm";
|
|
2864
|
+
if (agent.startsWith("yarn/"))
|
|
2865
|
+
return "yarn";
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2868
|
+
function isUnderPath2(path, parent) {
|
|
2869
|
+
const normalizedPath = normalizeInstallerPath2(path);
|
|
2870
|
+
const normalizedParent = normalizeInstallerPath2(parent);
|
|
2871
|
+
return normalizedPath === normalizedParent || normalizedPath.startsWith(`${normalizedParent}/`);
|
|
2872
|
+
}
|
|
2873
|
+
function normalizeInstallerPath2(path) {
|
|
2874
|
+
return path.replace(/^\/private\/tmp\//, "/tmp/");
|
|
2875
|
+
}
|
|
2876
|
+
function inferInstallerFromInstallation2(opts) {
|
|
2877
|
+
const candidatePaths = [opts.argv1, opts.realExecPath].filter((value) => !!value);
|
|
2878
|
+
if (opts.npmGlobalPrefix) {
|
|
2879
|
+
const npmPackageRoots = [
|
|
2880
|
+
join4(opts.npmGlobalPrefix, "node_modules", PACKAGE_NAME2),
|
|
2881
|
+
join4(opts.npmGlobalPrefix, "lib", "node_modules", PACKAGE_NAME2)
|
|
2882
|
+
];
|
|
2883
|
+
if (candidatePaths.some((path) => npmPackageRoots.some((root) => isUnderPath2(path, root)))) {
|
|
2884
|
+
return "npm";
|
|
2599
2885
|
}
|
|
2600
|
-
throw err;
|
|
2601
2886
|
}
|
|
2602
|
-
if (
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
return
|
|
2887
|
+
if (opts.bunGlobalBin) {
|
|
2888
|
+
const bunGlobalBin = opts.bunGlobalBin;
|
|
2889
|
+
if (candidatePaths.some((path) => isUnderPath2(path, bunGlobalBin))) {
|
|
2890
|
+
return "bun";
|
|
2606
2891
|
}
|
|
2607
|
-
console.log(`${TICK2} ${opts.successMessage}`);
|
|
2608
2892
|
}
|
|
2609
|
-
return
|
|
2893
|
+
return null;
|
|
2610
2894
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
configureSafe: () => configureSafe
|
|
2621
|
-
});
|
|
2622
|
-
function configureSafe(key, json) {
|
|
2623
|
-
return runMcpCliSetup(key, json, getConfig(), false).success;
|
|
2624
|
-
}
|
|
2625
|
-
var getConfig = () => ({
|
|
2626
|
-
cliName: "claude",
|
|
2627
|
-
agentId: "claude-code",
|
|
2628
|
-
notFoundMessage: "claude CLI not found. Install from https://claude.ai/code",
|
|
2629
|
-
notFoundCode: "claude_not_found",
|
|
2630
|
-
successMessage: "Outlit added to Claude Code. Restart Claude Code to apply."
|
|
2631
|
-
}), claude_code_default;
|
|
2632
|
-
var init_claude_code = __esm(() => {
|
|
2633
|
-
init_dist();
|
|
2634
|
-
init_auth();
|
|
2635
|
-
init_output2();
|
|
2636
|
-
init_config();
|
|
2637
|
-
init_setup();
|
|
2638
|
-
claude_code_default = defineCommand2({
|
|
2639
|
-
meta: {
|
|
2640
|
-
name: "claude-code",
|
|
2641
|
-
description: "Register Outlit MCP server with Claude Code via `claude mcp add`."
|
|
2642
|
-
},
|
|
2643
|
-
args: { ...authArgs, ...outputArgs },
|
|
2644
|
-
run({ args }) {
|
|
2645
|
-
const json = !!args.json;
|
|
2646
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
2647
|
-
runMcpCliSetup(key, json, getConfig());
|
|
2648
|
-
}
|
|
2649
|
-
});
|
|
2650
|
-
});
|
|
2651
|
-
|
|
2652
|
-
// src/commands/setup/claude-desktop.ts
|
|
2653
|
-
var exports_claude_desktop = {};
|
|
2654
|
-
__export(exports_claude_desktop, {
|
|
2655
|
-
default: () => claude_desktop_default,
|
|
2656
|
-
configureSafe: () => configureSafe2
|
|
2657
|
-
});
|
|
2658
|
-
function configureSafe2(key, json) {
|
|
2659
|
-
return runMcpFileSetup(key, json, getConfig2(key), false).success;
|
|
2895
|
+
function readCommandOutput2(command, args) {
|
|
2896
|
+
try {
|
|
2897
|
+
return execFileSync3(command, args, {
|
|
2898
|
+
encoding: "utf8",
|
|
2899
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2900
|
+
}).trim();
|
|
2901
|
+
} catch {
|
|
2902
|
+
return null;
|
|
2903
|
+
}
|
|
2660
2904
|
}
|
|
2661
|
-
|
|
2662
|
-
const
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
init_dist();
|
|
2675
|
-
init_auth();
|
|
2676
|
-
init_output2();
|
|
2677
|
-
init_config();
|
|
2678
|
-
init_setup();
|
|
2679
|
-
claude_desktop_default = defineCommand2({
|
|
2680
|
-
meta: {
|
|
2681
|
-
name: "claude-desktop",
|
|
2682
|
-
description: "Add Outlit MCP server to Claude Desktop config."
|
|
2683
|
-
},
|
|
2684
|
-
args: { ...authArgs, ...outputArgs },
|
|
2685
|
-
run({ args }) {
|
|
2686
|
-
const json = !!args.json;
|
|
2687
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
2688
|
-
runMcpFileSetup(key, json, getConfig2(key));
|
|
2689
|
-
}
|
|
2690
|
-
});
|
|
2691
|
-
});
|
|
2692
|
-
|
|
2693
|
-
// src/commands/setup/cursor.ts
|
|
2694
|
-
var exports_cursor = {};
|
|
2695
|
-
__export(exports_cursor, {
|
|
2696
|
-
default: () => cursor_default,
|
|
2697
|
-
configureSafe: () => configureSafe3
|
|
2698
|
-
});
|
|
2699
|
-
import { homedir as homedir2 } from "node:os";
|
|
2700
|
-
import { join as join3 } from "node:path";
|
|
2701
|
-
function configureSafe3(key, json) {
|
|
2702
|
-
return runMcpFileSetup(key, json, getConfig3(), false).success;
|
|
2703
|
-
}
|
|
2704
|
-
var getConfig3 = () => ({
|
|
2705
|
-
agentId: "cursor",
|
|
2706
|
-
configPath: join3(homedir2(), ".cursor", "mcp.json"),
|
|
2707
|
-
serversKey: "mcpServers",
|
|
2708
|
-
label: "cursor mcp.json",
|
|
2709
|
-
successMessage: `Outlit added to Cursor MCP config. Restart Cursor to apply.
|
|
2710
|
-
Config: ~/.cursor/mcp.json`
|
|
2711
|
-
}), cursor_default;
|
|
2712
|
-
var init_cursor = __esm(() => {
|
|
2713
|
-
init_dist();
|
|
2714
|
-
init_auth();
|
|
2715
|
-
init_output2();
|
|
2716
|
-
init_config();
|
|
2717
|
-
init_setup();
|
|
2718
|
-
cursor_default = defineCommand2({
|
|
2719
|
-
meta: {
|
|
2720
|
-
name: "cursor",
|
|
2721
|
-
description: "Add Outlit MCP server to ~/.cursor/mcp.json."
|
|
2722
|
-
},
|
|
2723
|
-
args: { ...authArgs, ...outputArgs },
|
|
2724
|
-
run({ args }) {
|
|
2725
|
-
const json = !!args.json;
|
|
2726
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
2727
|
-
runMcpFileSetup(key, json, getConfig3());
|
|
2728
|
-
}
|
|
2729
|
-
});
|
|
2730
|
-
});
|
|
2731
|
-
|
|
2732
|
-
// src/commands/setup/gemini.ts
|
|
2733
|
-
var exports_gemini = {};
|
|
2734
|
-
__export(exports_gemini, {
|
|
2735
|
-
default: () => gemini_default,
|
|
2736
|
-
configureSafe: () => configureSafe4
|
|
2737
|
-
});
|
|
2738
|
-
function configureSafe4(key, json) {
|
|
2739
|
-
return runMcpCliSetup(key, json, getConfig4(), false).success;
|
|
2740
|
-
}
|
|
2741
|
-
var getConfig4 = () => ({
|
|
2742
|
-
cliName: "gemini",
|
|
2743
|
-
agentId: "gemini",
|
|
2744
|
-
notFoundMessage: "gemini CLI not found. Install from https://github.com/google-gemini/gemini-cli",
|
|
2745
|
-
notFoundCode: "gemini_not_found",
|
|
2746
|
-
successMessage: "Outlit added to Gemini CLI. Restart Gemini CLI to apply."
|
|
2747
|
-
}), gemini_default;
|
|
2748
|
-
var init_gemini = __esm(() => {
|
|
2749
|
-
init_dist();
|
|
2750
|
-
init_auth();
|
|
2751
|
-
init_output2();
|
|
2752
|
-
init_config();
|
|
2753
|
-
init_output();
|
|
2754
|
-
init_setup();
|
|
2755
|
-
gemini_default = defineCommand2({
|
|
2756
|
-
meta: {
|
|
2757
|
-
name: "gemini",
|
|
2758
|
-
description: "Register Outlit MCP server with Gemini CLI via `gemini mcp add`."
|
|
2759
|
-
},
|
|
2760
|
-
args: { ...authArgs, ...outputArgs },
|
|
2761
|
-
run({ args }) {
|
|
2762
|
-
const json = !!args.json;
|
|
2763
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
2764
|
-
runMcpCliSetup(key, json, {
|
|
2765
|
-
...getConfig4(),
|
|
2766
|
-
extraErrorHandler(err, j2) {
|
|
2767
|
-
const msg = err instanceof Error ? err.message.toLowerCase() : "";
|
|
2768
|
-
if (msg.includes("mcp") && msg.includes("not")) {
|
|
2769
|
-
outputError({
|
|
2770
|
-
message: "Your Gemini CLI version does not support 'mcp add'. Run 'gemini update' or reinstall.",
|
|
2771
|
-
code: "gemini_mcp_unsupported"
|
|
2772
|
-
}, j2);
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
});
|
|
2776
|
-
}
|
|
2905
|
+
function inferInstaller2() {
|
|
2906
|
+
const fromAgent = inferInstallerFromUserAgent2(process.env.npm_config_user_agent ?? "");
|
|
2907
|
+
if (fromAgent)
|
|
2908
|
+
return fromAgent;
|
|
2909
|
+
const argv1 = process.argv[1];
|
|
2910
|
+
const realExecPath = argv1 ? readCommandOutput2("realpath", [argv1]) ?? safeRealPath2(argv1) : null;
|
|
2911
|
+
const npmGlobalPrefix = process.env.npm_config_prefix ?? readCommandOutput2("npm", ["prefix", "-g"]);
|
|
2912
|
+
const bunGlobalBin = process.env.BUN_INSTALL ? join4(process.env.BUN_INSTALL, "bin") : readCommandOutput2("bun", ["pm", "bin", "-g"]) ?? join4(homedir3(), ".bun", "bin");
|
|
2913
|
+
return inferInstallerFromInstallation2({
|
|
2914
|
+
argv1,
|
|
2915
|
+
realExecPath,
|
|
2916
|
+
npmGlobalPrefix,
|
|
2917
|
+
bunGlobalBin
|
|
2777
2918
|
});
|
|
2778
|
-
});
|
|
2779
|
-
|
|
2780
|
-
// src/commands/setup/openclaw.ts
|
|
2781
|
-
var exports_openclaw = {};
|
|
2782
|
-
__export(exports_openclaw, {
|
|
2783
|
-
getSkillDir: () => getSkillDir,
|
|
2784
|
-
default: () => openclaw_default,
|
|
2785
|
-
configureSafe: () => configureSafe5,
|
|
2786
|
-
buildSkillContent: () => buildSkillContent
|
|
2787
|
-
});
|
|
2788
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
2789
|
-
import { homedir as homedir3 } from "node:os";
|
|
2790
|
-
import { join as join4 } from "node:path";
|
|
2791
|
-
function getSkillDir() {
|
|
2792
|
-
const home = homedir3();
|
|
2793
|
-
const clawdDir = join4(home, "clawd");
|
|
2794
|
-
if (existsSync2(clawdDir)) {
|
|
2795
|
-
return join4(clawdDir, "skills", "outlit-intelligence");
|
|
2796
|
-
}
|
|
2797
|
-
return join4(home, ".openclaw", "skills", "outlit-intelligence");
|
|
2798
|
-
}
|
|
2799
|
-
function buildSkillContent(maskedKey) {
|
|
2800
|
-
return `---
|
|
2801
|
-
name: outlit-intelligence
|
|
2802
|
-
description: Query customer data, revenue metrics, and analytics via the Outlit CLI.
|
|
2803
|
-
metadata:
|
|
2804
|
-
openclaw:
|
|
2805
|
-
requires:
|
|
2806
|
-
bins: ["outlit"]
|
|
2807
|
-
env: ["OUTLIT_API_KEY"]
|
|
2808
|
-
---
|
|
2809
|
-
|
|
2810
|
-
# Outlit Customer Intelligence
|
|
2811
|
-
|
|
2812
|
-
You have access to the \`outlit\` CLI for querying customer data and analytics.
|
|
2813
|
-
The CLI outputs structured JSON automatically in non-TTY contexts.
|
|
2814
|
-
|
|
2815
|
-
## Authentication
|
|
2816
|
-
|
|
2817
|
-
Set the env var before running any command:
|
|
2818
|
-
|
|
2819
|
-
export OUTLIT_API_KEY=${maskedKey}
|
|
2820
|
-
|
|
2821
|
-
## List customers
|
|
2822
|
-
|
|
2823
|
-
outlit customers list --billing-status PAYING --no-activity-in 30d --order-by mrr_cents
|
|
2824
|
-
|
|
2825
|
-
## Get customer details
|
|
2826
|
-
|
|
2827
|
-
outlit customers get acme.com --include users,revenue,recentTimeline
|
|
2828
|
-
|
|
2829
|
-
## Get customer activity timeline
|
|
2830
|
-
|
|
2831
|
-
outlit customers timeline acme.com --channels EMAIL,SLACK --limit 50
|
|
2832
|
-
|
|
2833
|
-
## List users for a customer
|
|
2834
|
-
|
|
2835
|
-
outlit users list --customer-id <uuid> --journey-stage ACTIVATED
|
|
2836
|
-
|
|
2837
|
-
## Search across customer context
|
|
2838
|
-
|
|
2839
|
-
outlit search "budget concerns" --customer acme.com
|
|
2840
|
-
|
|
2841
|
-
## Get facts about a customer
|
|
2842
|
-
|
|
2843
|
-
outlit facts acme.com --timeframe 90d
|
|
2844
|
-
|
|
2845
|
-
## Run custom SQL
|
|
2846
|
-
|
|
2847
|
-
outlit sql "SELECT event_type, count(*) FROM events GROUP BY 1 ORDER BY 2 DESC LIMIT 10"
|
|
2848
|
-
|
|
2849
|
-
## Query with a file (for complex SQL)
|
|
2850
|
-
|
|
2851
|
-
outlit sql --query-file /tmp/query.sql
|
|
2852
|
-
|
|
2853
|
-
## Discover table schemas
|
|
2854
|
-
|
|
2855
|
-
outlit schema
|
|
2856
|
-
outlit schema events
|
|
2857
|
-
|
|
2858
|
-
## Rules
|
|
2859
|
-
- Always parse the JSON response before reporting to the user
|
|
2860
|
-
- Convert monetary values from cents to dollars (divide by 100)
|
|
2861
|
-
- IDs are UUIDs (e.g., "a1b2c3d4-e5f6-...")
|
|
2862
|
-
- Never show the raw API key to the user
|
|
2863
|
-
- Use --query-file for complex SQL to avoid shell escaping issues
|
|
2864
|
-
- All list responses include pagination.hasMore and pagination.nextCursor
|
|
2865
|
-
`;
|
|
2866
2919
|
}
|
|
2867
|
-
function
|
|
2868
|
-
const skillPath = join4(getSkillDir(), "SKILL.md");
|
|
2920
|
+
function safeRealPath2(filePath) {
|
|
2869
2921
|
try {
|
|
2870
|
-
|
|
2871
|
-
return true;
|
|
2922
|
+
return realpathSync2(filePath);
|
|
2872
2923
|
} catch {
|
|
2873
|
-
return
|
|
2924
|
+
return null;
|
|
2874
2925
|
}
|
|
2875
2926
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2927
|
+
function formatUpdateCommand2(installer = inferInstaller2()) {
|
|
2928
|
+
switch (installer) {
|
|
2929
|
+
case "bun":
|
|
2930
|
+
return "bun add -g @outlit/cli";
|
|
2931
|
+
case "npm":
|
|
2932
|
+
return "npm install -g @outlit/cli";
|
|
2933
|
+
case "pnpm":
|
|
2934
|
+
return "pnpm add -g @outlit/cli";
|
|
2935
|
+
case "yarn":
|
|
2936
|
+
return "yarn global add @outlit/cli";
|
|
2937
|
+
default:
|
|
2938
|
+
return `update ${PACKAGE_NAME2} with your package manager`;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
function getUpgradeCommand(installer = inferInstaller2()) {
|
|
2942
|
+
switch (installer) {
|
|
2943
|
+
case "bun":
|
|
2944
|
+
return {
|
|
2945
|
+
command: "bun",
|
|
2946
|
+
args: ["add", "-g", "@outlit/cli"],
|
|
2947
|
+
displayCommand: "bun add -g @outlit/cli"
|
|
2948
|
+
};
|
|
2949
|
+
case "npm":
|
|
2950
|
+
return {
|
|
2951
|
+
command: "npm",
|
|
2952
|
+
args: ["install", "-g", "@outlit/cli"],
|
|
2953
|
+
displayCommand: "npm install -g @outlit/cli"
|
|
2954
|
+
};
|
|
2955
|
+
case "pnpm":
|
|
2956
|
+
return {
|
|
2957
|
+
command: "pnpm",
|
|
2958
|
+
args: ["add", "-g", "@outlit/cli"],
|
|
2959
|
+
displayCommand: "pnpm add -g @outlit/cli"
|
|
2960
|
+
};
|
|
2961
|
+
case "yarn":
|
|
2962
|
+
return {
|
|
2963
|
+
command: "yarn",
|
|
2964
|
+
args: ["global", "add", "@outlit/cli"],
|
|
2965
|
+
displayCommand: "yarn global add @outlit/cli"
|
|
2966
|
+
};
|
|
2967
|
+
default:
|
|
2968
|
+
return null;
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
async function fetchLatestCliVersion2() {
|
|
2972
|
+
const response = await fetch(LATEST_VERSION_URL2, { signal: AbortSignal.timeout(5000) });
|
|
2973
|
+
if (!response.ok)
|
|
2974
|
+
throw new Error("registry unavailable");
|
|
2975
|
+
const data = await response.json();
|
|
2976
|
+
if (!data.version)
|
|
2977
|
+
throw new Error("registry returned no version");
|
|
2978
|
+
return data.version;
|
|
2979
|
+
}
|
|
2980
|
+
function runUpgradeCommand(command) {
|
|
2981
|
+
const result = spawnSync2(command.command, command.args, { stdio: "inherit" });
|
|
2982
|
+
if (result.error)
|
|
2983
|
+
throw result.error;
|
|
2984
|
+
if (result.status !== 0 || result.signal) {
|
|
2985
|
+
throw new Error(`Upgrade command failed: ${command.displayCommand}`);
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
var UPDATE_CHECK_INTERVAL_MS2, PACKAGE_NAME2 = "@outlit/cli", LATEST_VERSION_URL2 = "https://registry.npmjs.org/@outlit%2Fcli/latest";
|
|
2989
|
+
var init_update = __esm(() => {
|
|
2881
2990
|
init_config();
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
meta: {
|
|
2885
|
-
name: "openclaw",
|
|
2886
|
-
description: "Write Outlit intelligence skill to ~/clawd/skills/ for OpenClaw."
|
|
2887
|
-
},
|
|
2888
|
-
args: { ...authArgs, ...outputArgs },
|
|
2889
|
-
run({ args }) {
|
|
2890
|
-
const json = !!args.json;
|
|
2891
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
2892
|
-
const skillPath = join4(getSkillDir(), "SKILL.md");
|
|
2893
|
-
const content = buildSkillContent(maskKey(key));
|
|
2894
|
-
writeConfigFile(skillPath, content, { json, label: "SKILL.md" });
|
|
2895
|
-
if (isJsonMode(json)) {
|
|
2896
|
-
return outputResult({ success: true, path: skillPath, agent: "openclaw" });
|
|
2897
|
-
}
|
|
2898
|
-
console.log(`${TICK2} Outlit skill written to ${skillPath}. OpenClaw will load it automatically.`);
|
|
2899
|
-
}
|
|
2900
|
-
});
|
|
2991
|
+
init_tty();
|
|
2992
|
+
UPDATE_CHECK_INTERVAL_MS2 = 12 * 60 * 60 * 1000;
|
|
2901
2993
|
});
|
|
2902
2994
|
|
|
2903
2995
|
// src/commands/setup/skills.ts
|
|
2904
2996
|
var exports_skills = {};
|
|
2905
2997
|
__export(exports_skills, {
|
|
2906
2998
|
runSkillsInstall: () => runSkillsInstall,
|
|
2999
|
+
runAgentSkillsInstall: () => runAgentSkillsInstall,
|
|
3000
|
+
getSkillAgentId: () => getSkillAgentId,
|
|
2907
3001
|
detectPackageRunner: () => detectPackageRunner,
|
|
2908
|
-
default: () => skills_default
|
|
3002
|
+
default: () => skills_default,
|
|
3003
|
+
SKILLS_REPO_URL: () => SKILLS_REPO_URL
|
|
2909
3004
|
});
|
|
2910
3005
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
3006
|
+
function getSkillAgentId(agent) {
|
|
3007
|
+
return skillAgentMap[agent];
|
|
3008
|
+
}
|
|
2911
3009
|
function detectPackageRunner() {
|
|
2912
3010
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
2913
3011
|
for (const runner of ["npx", "bunx", "pnpx"]) {
|
|
@@ -2918,16 +3016,32 @@ function detectPackageRunner() {
|
|
|
2918
3016
|
}
|
|
2919
3017
|
return null;
|
|
2920
3018
|
}
|
|
2921
|
-
function buildRunnerArgs(runner) {
|
|
3019
|
+
function buildRunnerArgs(runner, opts) {
|
|
2922
3020
|
const args = runner === "npx" ? ["-y"] : [];
|
|
2923
3021
|
args.push("skills", "add", SKILLS_REPO_URL);
|
|
2924
|
-
for (const
|
|
2925
|
-
args.push("--skill",
|
|
3022
|
+
for (const skillName of opts.skillNames ?? []) {
|
|
3023
|
+
args.push("--skill", skillName);
|
|
3024
|
+
}
|
|
3025
|
+
for (const agent of opts.agents ?? []) {
|
|
3026
|
+
args.push("--agent", agent);
|
|
3027
|
+
}
|
|
3028
|
+
if (opts.autoConfirm) {
|
|
3029
|
+
args.push("-y");
|
|
3030
|
+
}
|
|
3031
|
+
if (opts.global !== false) {
|
|
3032
|
+
args.push("-g");
|
|
2926
3033
|
}
|
|
2927
|
-
args.push("-y", "-g");
|
|
2928
3034
|
return args;
|
|
2929
3035
|
}
|
|
2930
|
-
function runSkillsInstall(
|
|
3036
|
+
function runSkillsInstall(opts) {
|
|
3037
|
+
const {
|
|
3038
|
+
json,
|
|
3039
|
+
exitOnError = true,
|
|
3040
|
+
agents,
|
|
3041
|
+
skillNames,
|
|
3042
|
+
reportedAgent = "skills",
|
|
3043
|
+
autoConfirm = false
|
|
3044
|
+
} = opts;
|
|
2931
3045
|
const runner = detectPackageRunner();
|
|
2932
3046
|
if (!runner) {
|
|
2933
3047
|
if (exitOnError) {
|
|
@@ -2938,8 +3052,11 @@ function runSkillsInstall(json, exitOnError = true) {
|
|
|
2938
3052
|
}
|
|
2939
3053
|
return { success: false, error: "No package runner found" };
|
|
2940
3054
|
}
|
|
3055
|
+
const isInteractiveInstall = (agents?.length ?? 0) === 0 && (skillNames?.length ?? 0) === 0 && !autoConfirm && !isJsonMode(json);
|
|
2941
3056
|
try {
|
|
2942
|
-
execFileSync4(runner, buildRunnerArgs(runner
|
|
3057
|
+
execFileSync4(runner, buildRunnerArgs(runner, { agents, skillNames, autoConfirm }), {
|
|
3058
|
+
stdio: isInteractiveInstall ? "inherit" : "pipe"
|
|
3059
|
+
});
|
|
2943
3060
|
} catch (err) {
|
|
2944
3061
|
if (exitOnError) {
|
|
2945
3062
|
if (isEnoentError(err)) {
|
|
@@ -2958,210 +3075,355 @@ function runSkillsInstall(json, exitOnError = true) {
|
|
|
2958
3075
|
}
|
|
2959
3076
|
if (exitOnError) {
|
|
2960
3077
|
if (isJsonMode(json)) {
|
|
2961
|
-
outputResult({ success: true, agent:
|
|
3078
|
+
outputResult({ success: true, agent: reportedAgent, runner });
|
|
2962
3079
|
return { success: true, runner };
|
|
2963
3080
|
}
|
|
2964
|
-
|
|
3081
|
+
if (reportedAgent === "skills") {
|
|
3082
|
+
console.log(`${TICK2} Outlit skills installer completed (${runner})`);
|
|
3083
|
+
} else {
|
|
3084
|
+
console.log(`${TICK2} Outlit skill installed for ${reportedAgent}`);
|
|
3085
|
+
}
|
|
2965
3086
|
}
|
|
2966
3087
|
return { success: true, runner };
|
|
2967
3088
|
}
|
|
2968
|
-
|
|
3089
|
+
function runAgentSkillsInstall(agent, json, exitOnError = true) {
|
|
3090
|
+
return runSkillsInstall({
|
|
3091
|
+
json,
|
|
3092
|
+
exitOnError,
|
|
3093
|
+
agents: [getSkillAgentId(agent)],
|
|
3094
|
+
skillNames: [DEFAULT_SKILL_NAME],
|
|
3095
|
+
reportedAgent: agent,
|
|
3096
|
+
autoConfirm: true
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
var SKILLS_REPO_URL = "https://github.com/OutlitAI/outlit-agent-skills", DEFAULT_SKILL_NAME = "outlit", skillAgentMap, skills_default;
|
|
2969
3100
|
var init_skills = __esm(() => {
|
|
2970
3101
|
init_dist();
|
|
2971
3102
|
init_output2();
|
|
2972
3103
|
init_config();
|
|
2973
3104
|
init_output();
|
|
2974
|
-
|
|
3105
|
+
skillAgentMap = {
|
|
3106
|
+
"claude-code": "claude-code",
|
|
3107
|
+
codex: "codex",
|
|
3108
|
+
gemini: "gemini-cli",
|
|
3109
|
+
droid: "droid",
|
|
3110
|
+
opencode: "opencode",
|
|
3111
|
+
pi: "pi",
|
|
3112
|
+
openclaw: "openclaw"
|
|
3113
|
+
};
|
|
2975
3114
|
skills_default = defineCommand2({
|
|
2976
3115
|
meta: {
|
|
2977
3116
|
name: "skills",
|
|
2978
3117
|
description: [
|
|
2979
|
-
"
|
|
3118
|
+
"Launch the interactive Skills installer for Outlit.",
|
|
2980
3119
|
"",
|
|
2981
|
-
"
|
|
3120
|
+
"Uses the Outlit skills repo and lets you choose `outlit` and optional extras like `outlit-sdk`.",
|
|
2982
3121
|
"No API key required."
|
|
2983
3122
|
].join(`
|
|
2984
3123
|
`)
|
|
2985
3124
|
},
|
|
2986
3125
|
args: { ...outputArgs },
|
|
2987
3126
|
run({ args }) {
|
|
2988
|
-
|
|
2989
|
-
runSkillsInstall(json);
|
|
3127
|
+
runSkillsInstall({ json: !!args.json });
|
|
2990
3128
|
}
|
|
2991
3129
|
});
|
|
2992
3130
|
});
|
|
2993
3131
|
|
|
2994
|
-
// src/commands/setup/
|
|
2995
|
-
var
|
|
2996
|
-
__export(
|
|
2997
|
-
default: () =>
|
|
2998
|
-
configureSafe: () => configureSafe6
|
|
3132
|
+
// src/commands/setup/claude-code.ts
|
|
3133
|
+
var exports_claude_code = {};
|
|
3134
|
+
__export(exports_claude_code, {
|
|
3135
|
+
default: () => claude_code_default
|
|
2999
3136
|
});
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
return runMcpFileSetup(key, json, getConfig5(), false).success;
|
|
3003
|
-
}
|
|
3004
|
-
var getConfig5 = () => ({
|
|
3005
|
-
agentId: "vscode",
|
|
3006
|
-
configPath: join5(process.cwd(), ".vscode", "mcp.json"),
|
|
3007
|
-
serversKey: "servers",
|
|
3008
|
-
label: ".vscode/mcp.json",
|
|
3009
|
-
successMessage: "Outlit MCP config written to .vscode/mcp.json. Restart VS Code to apply."
|
|
3010
|
-
}), vscode_default;
|
|
3011
|
-
var init_vscode = __esm(() => {
|
|
3137
|
+
var claude_code_default;
|
|
3138
|
+
var init_claude_code = __esm(() => {
|
|
3012
3139
|
init_dist();
|
|
3013
|
-
init_auth();
|
|
3014
3140
|
init_output2();
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
vscode_default = defineCommand2({
|
|
3141
|
+
init_skills();
|
|
3142
|
+
claude_code_default = defineCommand2({
|
|
3018
3143
|
meta: {
|
|
3019
|
-
name: "
|
|
3020
|
-
description: "
|
|
3144
|
+
name: "claude-code",
|
|
3145
|
+
description: "Install the Outlit skill for Claude Code."
|
|
3021
3146
|
},
|
|
3022
|
-
args: { ...
|
|
3147
|
+
args: { ...outputArgs },
|
|
3023
3148
|
run({ args }) {
|
|
3024
|
-
|
|
3025
|
-
const { key } = requireCredential(args["api-key"], json);
|
|
3026
|
-
runMcpFileSetup(key, json, getConfig5());
|
|
3149
|
+
runAgentSkillsInstall("claude-code", !!args.json);
|
|
3027
3150
|
}
|
|
3028
3151
|
});
|
|
3029
3152
|
});
|
|
3030
3153
|
|
|
3031
|
-
// src/commands/setup/
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
3039
|
-
execFileSync5(whichCmd, [cmd], { stdio: "ignore" });
|
|
3040
|
-
return true;
|
|
3041
|
-
} catch {
|
|
3042
|
-
return false;
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
function detectAgents() {
|
|
3046
|
-
const home = homedir4();
|
|
3047
|
-
const detected = [];
|
|
3048
|
-
if (existsSync3(join6(home, ".cursor")))
|
|
3049
|
-
detected.push("cursor");
|
|
3050
|
-
if (isCommandAvailable("claude"))
|
|
3051
|
-
detected.push("claude-code");
|
|
3052
|
-
if (existsSync3(getClaudeDesktopConfigPath()))
|
|
3053
|
-
detected.push("claude-desktop");
|
|
3054
|
-
if (isCommandAvailable("code") || existsSync3(join6(process.cwd(), ".vscode")))
|
|
3055
|
-
detected.push("vscode");
|
|
3056
|
-
if (isCommandAvailable("gemini"))
|
|
3057
|
-
detected.push("gemini");
|
|
3058
|
-
if (existsSync3(join6(home, "clawd", "skills")) || existsSync3(join6(home, ".openclaw", "skills")))
|
|
3059
|
-
detected.push("openclaw");
|
|
3060
|
-
return detected;
|
|
3061
|
-
}
|
|
3062
|
-
var agentLabels, configurators, setup_default;
|
|
3063
|
-
var init_setup2 = __esm(() => {
|
|
3154
|
+
// src/commands/setup/codex.ts
|
|
3155
|
+
var exports_codex = {};
|
|
3156
|
+
__export(exports_codex, {
|
|
3157
|
+
default: () => codex_default
|
|
3158
|
+
});
|
|
3159
|
+
var codex_default;
|
|
3160
|
+
var init_codex = __esm(() => {
|
|
3064
3161
|
init_dist();
|
|
3065
|
-
init_auth();
|
|
3066
3162
|
init_output2();
|
|
3067
|
-
init_config();
|
|
3068
|
-
init_output();
|
|
3069
|
-
init_claude_code();
|
|
3070
|
-
init_claude_desktop();
|
|
3071
|
-
init_cursor();
|
|
3072
|
-
init_gemini();
|
|
3073
|
-
init_openclaw();
|
|
3074
3163
|
init_skills();
|
|
3075
|
-
|
|
3076
|
-
agentLabels = {
|
|
3077
|
-
cursor: { label: "Cursor", hint: "~/.cursor/" },
|
|
3078
|
-
"claude-code": { label: "Claude Code", hint: "claude CLI found" },
|
|
3079
|
-
"claude-desktop": { label: "Claude Desktop", hint: "config file found" },
|
|
3080
|
-
vscode: { label: "VS Code", hint: "code CLI or .vscode/ found" },
|
|
3081
|
-
gemini: { label: "Gemini CLI", hint: "gemini CLI found" },
|
|
3082
|
-
openclaw: { label: "OpenClaw", hint: "skills directory found" }
|
|
3083
|
-
};
|
|
3084
|
-
configurators = {
|
|
3085
|
-
cursor: configureSafe3,
|
|
3086
|
-
"claude-code": configureSafe,
|
|
3087
|
-
"claude-desktop": configureSafe2,
|
|
3088
|
-
vscode: configureSafe6,
|
|
3089
|
-
gemini: configureSafe4,
|
|
3090
|
-
openclaw: configureSafe5
|
|
3091
|
-
};
|
|
3092
|
-
setup_default = defineCommand2({
|
|
3164
|
+
codex_default = defineCommand2({
|
|
3093
3165
|
meta: {
|
|
3094
|
-
name: "
|
|
3095
|
-
description:
|
|
3096
|
-
"Configure Outlit for AI agent tools.",
|
|
3097
|
-
"",
|
|
3098
|
-
"Without a subcommand, auto-detects installed agents and configures them all.",
|
|
3099
|
-
"Subcommands: cursor, claude-code, claude-desktop, vscode, gemini, openclaw, skills"
|
|
3100
|
-
].join(`
|
|
3101
|
-
`)
|
|
3166
|
+
name: "codex",
|
|
3167
|
+
description: "Install the Outlit skill for Codex."
|
|
3102
3168
|
},
|
|
3103
|
-
args: {
|
|
3104
|
-
|
|
3169
|
+
args: { ...outputArgs },
|
|
3170
|
+
run({ args }) {
|
|
3171
|
+
runAgentSkillsInstall("codex", !!args.json);
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
});
|
|
3175
|
+
|
|
3176
|
+
// src/commands/setup/gemini.ts
|
|
3177
|
+
var exports_gemini = {};
|
|
3178
|
+
__export(exports_gemini, {
|
|
3179
|
+
default: () => gemini_default
|
|
3180
|
+
});
|
|
3181
|
+
var gemini_default;
|
|
3182
|
+
var init_gemini = __esm(() => {
|
|
3183
|
+
init_dist();
|
|
3184
|
+
init_output2();
|
|
3185
|
+
init_skills();
|
|
3186
|
+
gemini_default = defineCommand2({
|
|
3187
|
+
meta: {
|
|
3188
|
+
name: "gemini",
|
|
3189
|
+
description: "Install the Outlit skill for Gemini CLI."
|
|
3190
|
+
},
|
|
3191
|
+
args: { ...outputArgs },
|
|
3192
|
+
run({ args }) {
|
|
3193
|
+
runAgentSkillsInstall("gemini", !!args.json);
|
|
3194
|
+
}
|
|
3195
|
+
});
|
|
3196
|
+
});
|
|
3197
|
+
|
|
3198
|
+
// src/commands/setup/droid.ts
|
|
3199
|
+
var exports_droid = {};
|
|
3200
|
+
__export(exports_droid, {
|
|
3201
|
+
default: () => droid_default
|
|
3202
|
+
});
|
|
3203
|
+
var droid_default;
|
|
3204
|
+
var init_droid = __esm(() => {
|
|
3205
|
+
init_dist();
|
|
3206
|
+
init_output2();
|
|
3207
|
+
init_skills();
|
|
3208
|
+
droid_default = defineCommand2({
|
|
3209
|
+
meta: {
|
|
3210
|
+
name: "droid",
|
|
3211
|
+
description: "Install the Outlit skill for Droid."
|
|
3212
|
+
},
|
|
3213
|
+
args: { ...outputArgs },
|
|
3214
|
+
run({ args }) {
|
|
3215
|
+
runAgentSkillsInstall("droid", !!args.json);
|
|
3216
|
+
}
|
|
3217
|
+
});
|
|
3218
|
+
});
|
|
3219
|
+
|
|
3220
|
+
// src/commands/setup/opencode.ts
|
|
3221
|
+
var exports_opencode = {};
|
|
3222
|
+
__export(exports_opencode, {
|
|
3223
|
+
default: () => opencode_default
|
|
3224
|
+
});
|
|
3225
|
+
var opencode_default;
|
|
3226
|
+
var init_opencode = __esm(() => {
|
|
3227
|
+
init_dist();
|
|
3228
|
+
init_output2();
|
|
3229
|
+
init_skills();
|
|
3230
|
+
opencode_default = defineCommand2({
|
|
3231
|
+
meta: {
|
|
3232
|
+
name: "opencode",
|
|
3233
|
+
description: "Install the Outlit skill for OpenCode."
|
|
3234
|
+
},
|
|
3235
|
+
args: { ...outputArgs },
|
|
3236
|
+
run({ args }) {
|
|
3237
|
+
runAgentSkillsInstall("opencode", !!args.json);
|
|
3238
|
+
}
|
|
3239
|
+
});
|
|
3240
|
+
});
|
|
3241
|
+
|
|
3242
|
+
// src/commands/setup/pi.ts
|
|
3243
|
+
var exports_pi = {};
|
|
3244
|
+
__export(exports_pi, {
|
|
3245
|
+
default: () => pi_default
|
|
3246
|
+
});
|
|
3247
|
+
var pi_default;
|
|
3248
|
+
var init_pi = __esm(() => {
|
|
3249
|
+
init_dist();
|
|
3250
|
+
init_output2();
|
|
3251
|
+
init_skills();
|
|
3252
|
+
pi_default = defineCommand2({
|
|
3253
|
+
meta: {
|
|
3254
|
+
name: "pi",
|
|
3255
|
+
description: "Install the Outlit skill for Pi."
|
|
3256
|
+
},
|
|
3257
|
+
args: { ...outputArgs },
|
|
3258
|
+
run({ args }) {
|
|
3259
|
+
runAgentSkillsInstall("pi", !!args.json);
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
});
|
|
3263
|
+
|
|
3264
|
+
// src/commands/setup/openclaw.ts
|
|
3265
|
+
var exports_openclaw = {};
|
|
3266
|
+
__export(exports_openclaw, {
|
|
3267
|
+
default: () => openclaw_default
|
|
3268
|
+
});
|
|
3269
|
+
var openclaw_default;
|
|
3270
|
+
var init_openclaw = __esm(() => {
|
|
3271
|
+
init_dist();
|
|
3272
|
+
init_output2();
|
|
3273
|
+
init_skills();
|
|
3274
|
+
openclaw_default = defineCommand2({
|
|
3275
|
+
meta: {
|
|
3276
|
+
name: "openclaw",
|
|
3277
|
+
description: "Install the Outlit skill for OpenClaw."
|
|
3278
|
+
},
|
|
3279
|
+
args: { ...outputArgs },
|
|
3280
|
+
run({ args }) {
|
|
3281
|
+
runAgentSkillsInstall("openclaw", !!args.json);
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
});
|
|
3285
|
+
|
|
3286
|
+
// src/commands/setup/index.ts
|
|
3287
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3288
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
3289
|
+
import { homedir as homedir4 } from "node:os";
|
|
3290
|
+
import { join as join5 } from "node:path";
|
|
3291
|
+
function isCommandAvailable(cmd) {
|
|
3292
|
+
try {
|
|
3293
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
3294
|
+
execFileSync5(whichCmd, [cmd], { stdio: "ignore" });
|
|
3295
|
+
return true;
|
|
3296
|
+
} catch {
|
|
3297
|
+
return false;
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
function getHomeDir() {
|
|
3301
|
+
return process.env.HOME?.trim() || homedir4();
|
|
3302
|
+
}
|
|
3303
|
+
function detectAgents() {
|
|
3304
|
+
const home = getHomeDir();
|
|
3305
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || join5(home, ".config");
|
|
3306
|
+
const detected = [];
|
|
3307
|
+
if (isCommandAvailable("claude"))
|
|
3308
|
+
detected.push("claude-code");
|
|
3309
|
+
if (isCommandAvailable("codex"))
|
|
3310
|
+
detected.push("codex");
|
|
3311
|
+
if (isCommandAvailable("gemini"))
|
|
3312
|
+
detected.push("gemini");
|
|
3313
|
+
if (existsSync4(join5(home, ".factory")))
|
|
3314
|
+
detected.push("droid");
|
|
3315
|
+
if (existsSync4(join5(configHome, "opencode")))
|
|
3316
|
+
detected.push("opencode");
|
|
3317
|
+
if (existsSync4(join5(home, ".pi", "agent")))
|
|
3318
|
+
detected.push("pi");
|
|
3319
|
+
if (existsSync4(join5(home, ".openclaw")) || existsSync4(join5(home, ".clawdbot")) || existsSync4(join5(home, ".moltbot"))) {
|
|
3320
|
+
detected.push("openclaw");
|
|
3321
|
+
}
|
|
3322
|
+
return detected;
|
|
3323
|
+
}
|
|
3324
|
+
var agentLabels, setupSubcommandNames, setup_default;
|
|
3325
|
+
var init_setup = __esm(() => {
|
|
3326
|
+
init_dist();
|
|
3327
|
+
init_output2();
|
|
3328
|
+
init_config();
|
|
3329
|
+
init_output();
|
|
3330
|
+
init_skills();
|
|
3331
|
+
agentLabels = {
|
|
3332
|
+
"claude-code": { label: "Claude Code", hint: "claude CLI found" },
|
|
3333
|
+
codex: { label: "Codex", hint: "codex CLI found" },
|
|
3334
|
+
gemini: { label: "Gemini CLI", hint: "gemini CLI found" },
|
|
3335
|
+
droid: { label: "Droid", hint: ".factory config found" },
|
|
3336
|
+
opencode: { label: "OpenCode", hint: "opencode config found" },
|
|
3337
|
+
pi: { label: "Pi", hint: ".pi/agent config found" },
|
|
3338
|
+
openclaw: { label: "OpenClaw", hint: "OpenClaw config found" }
|
|
3339
|
+
};
|
|
3340
|
+
setupSubcommandNames = new Set([
|
|
3341
|
+
"claude-code",
|
|
3342
|
+
"codex",
|
|
3343
|
+
"gemini",
|
|
3344
|
+
"droid",
|
|
3345
|
+
"opencode",
|
|
3346
|
+
"pi",
|
|
3347
|
+
"openclaw",
|
|
3348
|
+
"skills"
|
|
3349
|
+
]);
|
|
3350
|
+
setup_default = defineCommand2({
|
|
3351
|
+
meta: {
|
|
3352
|
+
name: "setup",
|
|
3353
|
+
description: [
|
|
3354
|
+
"Install the Outlit skill for coding agents.",
|
|
3355
|
+
"",
|
|
3356
|
+
"Without a subcommand, auto-detects supported coding agents and installs `outlit` for all of them.",
|
|
3357
|
+
"Subcommands: claude-code, codex, gemini, droid, opencode, pi, openclaw, skills"
|
|
3358
|
+
].join(`
|
|
3359
|
+
`)
|
|
3360
|
+
},
|
|
3361
|
+
args: {
|
|
3105
3362
|
...outputArgs,
|
|
3106
3363
|
yes: {
|
|
3107
3364
|
type: "boolean",
|
|
3108
|
-
description: "
|
|
3365
|
+
description: "Install for all detected coding agents without prompting."
|
|
3109
3366
|
}
|
|
3110
3367
|
},
|
|
3111
3368
|
subCommands: {
|
|
3112
|
-
cursor: () => Promise.resolve().then(() => (init_cursor(), exports_cursor)).then((m) => m.default),
|
|
3113
3369
|
"claude-code": () => Promise.resolve().then(() => (init_claude_code(), exports_claude_code)).then((m) => m.default),
|
|
3114
|
-
|
|
3115
|
-
vscode: () => Promise.resolve().then(() => (init_vscode(), exports_vscode)).then((m) => m.default),
|
|
3370
|
+
codex: () => Promise.resolve().then(() => (init_codex(), exports_codex)).then((m) => m.default),
|
|
3116
3371
|
gemini: () => Promise.resolve().then(() => (init_gemini(), exports_gemini)).then((m) => m.default),
|
|
3372
|
+
droid: () => Promise.resolve().then(() => (init_droid(), exports_droid)).then((m) => m.default),
|
|
3373
|
+
opencode: () => Promise.resolve().then(() => (init_opencode(), exports_opencode)).then((m) => m.default),
|
|
3374
|
+
pi: () => Promise.resolve().then(() => (init_pi(), exports_pi)).then((m) => m.default),
|
|
3117
3375
|
openclaw: () => Promise.resolve().then(() => (init_openclaw(), exports_openclaw)).then((m) => m.default),
|
|
3118
3376
|
skills: () => Promise.resolve().then(() => (init_skills(), exports_skills)).then((m) => m.default)
|
|
3119
3377
|
},
|
|
3120
|
-
async run({ args }) {
|
|
3378
|
+
async run({ args, rawArgs }) {
|
|
3379
|
+
const setupRawArgs = rawArgs ?? [];
|
|
3380
|
+
const subcommandName = setupRawArgs.find((arg) => !arg.startsWith("-"));
|
|
3381
|
+
if (subcommandName && setupSubcommandNames.has(subcommandName)) {
|
|
3382
|
+
return;
|
|
3383
|
+
}
|
|
3121
3384
|
const json = !!args.json;
|
|
3122
|
-
const credential = requireCredential(args["api-key"], json);
|
|
3123
3385
|
const detected = detectAgents();
|
|
3124
3386
|
if (detected.length === 0) {
|
|
3125
3387
|
if (isJsonMode(json)) {
|
|
3126
|
-
return outputResult({ detected: [], configured: [], failed: [],
|
|
3388
|
+
return outputResult({ detected: [], configured: [], failed: [], runner: null });
|
|
3127
3389
|
}
|
|
3128
|
-
console.log("No supported
|
|
3390
|
+
console.log("No supported coding agents detected.");
|
|
3129
3391
|
return;
|
|
3130
3392
|
}
|
|
3131
3393
|
if (!isJsonMode(json) && !args.yes) {
|
|
3132
|
-
console.log("Detected agents:");
|
|
3394
|
+
console.log("Detected coding agents:");
|
|
3133
3395
|
for (const agentId of detected) {
|
|
3134
3396
|
const { label, hint } = agentLabels[agentId];
|
|
3135
3397
|
console.log(` ${TICK2} ${label.padEnd(14)} -- ${hint}`);
|
|
3136
3398
|
}
|
|
3137
3399
|
console.log(`
|
|
3138
|
-
|
|
3139
|
-
}
|
|
3140
|
-
const configured = [];
|
|
3141
|
-
const failed = [];
|
|
3142
|
-
for (const agentId of detected) {
|
|
3143
|
-
const ok = configurators[agentId](credential.key, json);
|
|
3144
|
-
if (ok) {
|
|
3145
|
-
configured.push(agentId);
|
|
3146
|
-
} else {
|
|
3147
|
-
failed.push(agentId);
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
const skills = runSkillsInstall(json, false);
|
|
3151
|
-
if (!isJsonMode(json) && !skills.success) {
|
|
3152
|
-
console.log(`
|
|
3153
|
-
! Agent skills installation failed: ${skills.error ?? "unknown error"}`);
|
|
3154
|
-
console.log(" Run `outlit setup skills` to retry.");
|
|
3400
|
+
Installing Outlit skill...`);
|
|
3155
3401
|
}
|
|
3402
|
+
const install = runSkillsInstall({
|
|
3403
|
+
json,
|
|
3404
|
+
exitOnError: false,
|
|
3405
|
+
agents: detected.map(getSkillAgentId),
|
|
3406
|
+
skillNames: ["outlit"],
|
|
3407
|
+
autoConfirm: true
|
|
3408
|
+
});
|
|
3409
|
+
const configured = install.success ? detected : [];
|
|
3410
|
+
const failed = install.success ? [] : detected;
|
|
3156
3411
|
if (isJsonMode(json)) {
|
|
3157
|
-
return outputResult({
|
|
3412
|
+
return outputResult({
|
|
3413
|
+
detected,
|
|
3414
|
+
configured,
|
|
3415
|
+
failed,
|
|
3416
|
+
runner: install.runner ?? null
|
|
3417
|
+
});
|
|
3158
3418
|
}
|
|
3159
|
-
if (
|
|
3419
|
+
if (!install.success) {
|
|
3160
3420
|
console.log(`
|
|
3161
|
-
|
|
3421
|
+
! Outlit skill install failed: ${install.error ?? "unknown error"}`);
|
|
3422
|
+
console.log(" Run `outlit setup skills` to retry manually.");
|
|
3423
|
+
return;
|
|
3162
3424
|
}
|
|
3163
3425
|
console.log(`
|
|
3164
|
-
Done. ${configured.length}
|
|
3426
|
+
Done. Installed Outlit for ${configured.length} coding agent(s).`);
|
|
3165
3427
|
}
|
|
3166
3428
|
});
|
|
3167
3429
|
});
|
|
@@ -3169,21 +3431,16 @@ Done. ${configured.length}/${detected.length} agent(s) configured successfully.`
|
|
|
3169
3431
|
// src/commands/doctor.ts
|
|
3170
3432
|
var exports_doctor = {};
|
|
3171
3433
|
__export(exports_doctor, {
|
|
3172
|
-
default: () => doctor_default
|
|
3434
|
+
default: () => doctor_default,
|
|
3435
|
+
buildAgentChecks: () => buildAgentChecks
|
|
3173
3436
|
});
|
|
3174
|
-
import { existsSync as
|
|
3437
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
3175
3438
|
import { homedir as homedir5 } from "node:os";
|
|
3176
|
-
import { join as
|
|
3439
|
+
import { join as join6 } from "node:path";
|
|
3177
3440
|
async function checkCliVersion() {
|
|
3178
3441
|
const current = CLI_VERSION2;
|
|
3179
3442
|
try {
|
|
3180
|
-
const
|
|
3181
|
-
signal: AbortSignal.timeout(5000)
|
|
3182
|
-
});
|
|
3183
|
-
if (!res.ok)
|
|
3184
|
-
throw new Error("registry unavailable");
|
|
3185
|
-
const data = await res.json();
|
|
3186
|
-
const latest = data.version ?? "unknown";
|
|
3443
|
+
const latest = await fetchLatestCliVersion2();
|
|
3187
3444
|
if (latest === current) {
|
|
3188
3445
|
return { name: "CLI version", status: "pass", message: `v${current} (latest)` };
|
|
3189
3446
|
}
|
|
@@ -3191,7 +3448,7 @@ async function checkCliVersion() {
|
|
|
3191
3448
|
name: "CLI version",
|
|
3192
3449
|
status: "warn",
|
|
3193
3450
|
message: `v${current} installed, v${latest} available`,
|
|
3194
|
-
detail:
|
|
3451
|
+
detail: `Run \`${formatUpdateCommand2()}\` to update`
|
|
3195
3452
|
};
|
|
3196
3453
|
} catch {
|
|
3197
3454
|
return {
|
|
@@ -3237,8 +3494,83 @@ async function validateApiKey(apiKey) {
|
|
|
3237
3494
|
};
|
|
3238
3495
|
}
|
|
3239
3496
|
}
|
|
3240
|
-
function
|
|
3241
|
-
|
|
3497
|
+
async function checkIntegrations(apiKey) {
|
|
3498
|
+
try {
|
|
3499
|
+
const client = await createClient(apiKey);
|
|
3500
|
+
const timeout = new Promise((_2, reject) => setTimeout(() => reject(new Error("Integrations check timed out")), 1e4));
|
|
3501
|
+
const data = await Promise.race([
|
|
3502
|
+
client.callTool("outlit_list_integrations", {}),
|
|
3503
|
+
timeout
|
|
3504
|
+
]);
|
|
3505
|
+
const items = data.items ?? [];
|
|
3506
|
+
const connected = items.filter((i) => i.status === "connected").length;
|
|
3507
|
+
const errors = items.filter((i) => i.status === "error").length;
|
|
3508
|
+
if (errors > 0) {
|
|
3509
|
+
return {
|
|
3510
|
+
name: "Integrations",
|
|
3511
|
+
status: "warn",
|
|
3512
|
+
message: `${connected} connected, ${errors} with errors`,
|
|
3513
|
+
detail: "Run `outlit integrations status` for details"
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
if (connected === 0) {
|
|
3517
|
+
return {
|
|
3518
|
+
name: "Integrations",
|
|
3519
|
+
status: "pass",
|
|
3520
|
+
message: "No integrations connected",
|
|
3521
|
+
detail: "Run `outlit integrations list` to see available integrations"
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
return {
|
|
3525
|
+
name: "Integrations",
|
|
3526
|
+
status: "pass",
|
|
3527
|
+
message: `${connected} integration(s) connected`
|
|
3528
|
+
};
|
|
3529
|
+
} catch {
|
|
3530
|
+
return {
|
|
3531
|
+
name: "Integrations",
|
|
3532
|
+
status: "warn",
|
|
3533
|
+
message: "Could not check integrations",
|
|
3534
|
+
detail: "Integration status endpoint may not be available yet"
|
|
3535
|
+
};
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
function getHomeDir2(options) {
|
|
3539
|
+
return options?.homeDir?.trim() || process.env.HOME?.trim() || homedir5();
|
|
3540
|
+
}
|
|
3541
|
+
function getSharedSkillsDir(options) {
|
|
3542
|
+
return join6(getHomeDir2(options), ".agents", "skills");
|
|
3543
|
+
}
|
|
3544
|
+
function getOpenClawHome(options) {
|
|
3545
|
+
const home = getHomeDir2(options);
|
|
3546
|
+
if (existsSync5(join6(home, ".openclaw")))
|
|
3547
|
+
return join6(home, ".openclaw");
|
|
3548
|
+
if (existsSync5(join6(home, ".clawdbot")))
|
|
3549
|
+
return join6(home, ".clawdbot");
|
|
3550
|
+
if (existsSync5(join6(home, ".moltbot")))
|
|
3551
|
+
return join6(home, ".moltbot");
|
|
3552
|
+
return join6(home, ".openclaw");
|
|
3553
|
+
}
|
|
3554
|
+
function getAgentSkillDir(agentId, options) {
|
|
3555
|
+
const home = getHomeDir2(options);
|
|
3556
|
+
switch (agentId) {
|
|
3557
|
+
case "claude-code":
|
|
3558
|
+
return join6(options?.claudeConfigDir?.trim() || process.env.CLAUDE_CONFIG_DIR?.trim() || join6(home, ".claude"), "skills");
|
|
3559
|
+
case "codex":
|
|
3560
|
+
return getSharedSkillsDir(options);
|
|
3561
|
+
case "gemini":
|
|
3562
|
+
return getSharedSkillsDir(options);
|
|
3563
|
+
case "droid":
|
|
3564
|
+
return join6(home, ".factory", "skills");
|
|
3565
|
+
case "opencode":
|
|
3566
|
+
return getSharedSkillsDir(options);
|
|
3567
|
+
case "pi":
|
|
3568
|
+
return join6(home, ".pi", "agent", "skills");
|
|
3569
|
+
case "openclaw":
|
|
3570
|
+
return join6(getOpenClawHome(options), "skills");
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
function buildAgentChecks(detected = detectAgents(), options) {
|
|
3242
3574
|
if (detected.length === 0) {
|
|
3243
3575
|
return [
|
|
3244
3576
|
{
|
|
@@ -3249,66 +3581,15 @@ function detectAgents2() {
|
|
|
3249
3581
|
];
|
|
3250
3582
|
}
|
|
3251
3583
|
const results = [];
|
|
3252
|
-
const home = homedir5();
|
|
3253
3584
|
for (const agentId of detected) {
|
|
3254
3585
|
const meta = agentChecks[agentId];
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
detail: hasSkill ? undefined : "Run `outlit setup openclaw` to configure"
|
|
3263
|
-
});
|
|
3264
|
-
continue;
|
|
3265
|
-
}
|
|
3266
|
-
if (!meta.configCheck) {
|
|
3267
|
-
results.push({
|
|
3268
|
-
name: meta.name,
|
|
3269
|
-
status: "warn",
|
|
3270
|
-
message: "Installed, but Outlit MCP not verified",
|
|
3271
|
-
detail: `Run \`outlit setup ${agentId}\` to configure`
|
|
3272
|
-
});
|
|
3273
|
-
if (agentId === "claude-code") {
|
|
3274
|
-
const skillsDir = join7(home, ".claude", "skills");
|
|
3275
|
-
const hasSkills = existsSync4(join7(skillsDir, "outlit-cli")) || existsSync4(join7(skillsDir, "outlit-sdk")) || existsSync4(join7(skillsDir, "outlit-mcp"));
|
|
3276
|
-
results.push({
|
|
3277
|
-
name: "Agent Skills",
|
|
3278
|
-
status: hasSkills ? "pass" : "warn",
|
|
3279
|
-
message: hasSkills ? "Outlit agent skills installed" : "Outlit agent skills not found",
|
|
3280
|
-
detail: hasSkills ? undefined : "Run `outlit setup skills` to install deeper AI agent context"
|
|
3281
|
-
});
|
|
3282
|
-
}
|
|
3283
|
-
continue;
|
|
3284
|
-
}
|
|
3285
|
-
const { path, key } = meta.configCheck;
|
|
3286
|
-
if (!existsSync4(path)) {
|
|
3287
|
-
results.push({
|
|
3288
|
-
name: meta.name,
|
|
3289
|
-
status: "warn",
|
|
3290
|
-
message: "Installed, but config file not found",
|
|
3291
|
-
detail: `Run \`outlit setup ${agentId}\` to configure`
|
|
3292
|
-
});
|
|
3293
|
-
continue;
|
|
3294
|
-
}
|
|
3295
|
-
try {
|
|
3296
|
-
const config = readJsonConfig(path);
|
|
3297
|
-
const configured = !!config[key]?.outlit;
|
|
3298
|
-
results.push({
|
|
3299
|
-
name: meta.name,
|
|
3300
|
-
status: configured ? "pass" : "warn",
|
|
3301
|
-
message: configured ? "Installed, Outlit MCP configured" : "Installed, but Outlit MCP not configured",
|
|
3302
|
-
detail: configured ? undefined : `Run \`outlit setup ${agentId}\` to configure`
|
|
3303
|
-
});
|
|
3304
|
-
} catch {
|
|
3305
|
-
results.push({
|
|
3306
|
-
name: meta.name,
|
|
3307
|
-
status: "warn",
|
|
3308
|
-
message: "Installed, but config file is malformed",
|
|
3309
|
-
detail: `Check ${path} for JSON syntax errors`
|
|
3310
|
-
});
|
|
3311
|
-
}
|
|
3586
|
+
const hasSkill = existsSync5(join6(getAgentSkillDir(agentId, options), "outlit", "SKILL.md"));
|
|
3587
|
+
results.push({
|
|
3588
|
+
name: meta.name,
|
|
3589
|
+
status: hasSkill ? "pass" : "warn",
|
|
3590
|
+
message: hasSkill ? "Outlit skill installed" : "Installed, but Outlit skill not found",
|
|
3591
|
+
detail: hasSkill ? undefined : meta.missingDetail
|
|
3592
|
+
});
|
|
3312
3593
|
}
|
|
3313
3594
|
return results;
|
|
3314
3595
|
}
|
|
@@ -3339,10 +3620,12 @@ var init_doctor = __esm(() => {
|
|
|
3339
3620
|
init_auth();
|
|
3340
3621
|
init_output2();
|
|
3341
3622
|
init_api();
|
|
3623
|
+
init_client();
|
|
3342
3624
|
init_config();
|
|
3343
3625
|
init_output();
|
|
3344
3626
|
init_tty();
|
|
3345
|
-
|
|
3627
|
+
init_update();
|
|
3628
|
+
init_setup();
|
|
3346
3629
|
FAIL_SYMBOL2 = isUnicodeSupported ? String.fromCodePoint(10007) : "x";
|
|
3347
3630
|
STATUS_ICONS = {
|
|
3348
3631
|
pass: TICK2,
|
|
@@ -3359,7 +3642,7 @@ var init_doctor = __esm(() => {
|
|
|
3359
3642
|
" 1. CLI version -- compares against npm registry",
|
|
3360
3643
|
" 2. API key -- checks presence and format (ok_ prefix)",
|
|
3361
3644
|
" 3. API validation -- makes a live test call to verify the key works",
|
|
3362
|
-
" 4. Agent detection -- detects
|
|
3645
|
+
" 4. Agent detection -- detects supported coding agents and whether the Outlit skill is installed",
|
|
3363
3646
|
"",
|
|
3364
3647
|
"Exit code: 0 if all checks pass or warn, 1 if any check fails.",
|
|
3365
3648
|
"",
|
|
@@ -3383,7 +3666,11 @@ var init_doctor = __esm(() => {
|
|
|
3383
3666
|
const credential = resolveApiKey(args["api-key"]);
|
|
3384
3667
|
checks.push(checkApiKeyPresence(credential));
|
|
3385
3668
|
if (credential) {
|
|
3386
|
-
|
|
3669
|
+
const apiCheck = await validateApiKey(credential.key);
|
|
3670
|
+
checks.push(apiCheck);
|
|
3671
|
+
if (apiCheck.status !== "fail") {
|
|
3672
|
+
checks.push(await checkIntegrations(credential.key));
|
|
3673
|
+
}
|
|
3387
3674
|
} else {
|
|
3388
3675
|
checks.push({
|
|
3389
3676
|
name: "API validation",
|
|
@@ -3391,60 +3678,149 @@ var init_doctor = __esm(() => {
|
|
|
3391
3678
|
message: "Skipped -- no API key found"
|
|
3392
3679
|
});
|
|
3393
3680
|
}
|
|
3394
|
-
checks.push(...
|
|
3681
|
+
checks.push(...buildAgentChecks());
|
|
3395
3682
|
const hasFail = checks.some((c) => c.status === "fail");
|
|
3396
3683
|
if (isJsonMode(json)) {
|
|
3397
3684
|
outputResult({ ok: !hasFail, checks });
|
|
3398
3685
|
} else {
|
|
3399
3686
|
printChecks(checks);
|
|
3400
3687
|
}
|
|
3401
|
-
|
|
3688
|
+
if (hasFail)
|
|
3689
|
+
process.exit(1);
|
|
3402
3690
|
}
|
|
3403
3691
|
});
|
|
3404
3692
|
agentChecks = {
|
|
3405
|
-
|
|
3406
|
-
name: "
|
|
3407
|
-
|
|
3693
|
+
"claude-code": {
|
|
3694
|
+
name: "Claude Code",
|
|
3695
|
+
missingDetail: "Run `outlit setup claude-code` to install the Outlit skill"
|
|
3696
|
+
},
|
|
3697
|
+
codex: {
|
|
3698
|
+
name: "Codex",
|
|
3699
|
+
missingDetail: "Run `outlit setup codex` to install the Outlit skill"
|
|
3408
3700
|
},
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
configCheck: { path: getClaudeDesktopConfigPath(), key: "mcpServers" }
|
|
3701
|
+
gemini: {
|
|
3702
|
+
name: "Gemini CLI",
|
|
3703
|
+
missingDetail: "Run `outlit setup gemini` to install the Outlit skill"
|
|
3413
3704
|
},
|
|
3414
|
-
|
|
3415
|
-
name: "
|
|
3416
|
-
|
|
3705
|
+
droid: {
|
|
3706
|
+
name: "Droid",
|
|
3707
|
+
missingDetail: "Run `outlit setup droid` to install the Outlit skill"
|
|
3417
3708
|
},
|
|
3418
|
-
|
|
3419
|
-
|
|
3709
|
+
opencode: {
|
|
3710
|
+
name: "OpenCode",
|
|
3711
|
+
missingDetail: "Run `outlit setup opencode` to install the Outlit skill"
|
|
3712
|
+
},
|
|
3713
|
+
pi: {
|
|
3714
|
+
name: "Pi",
|
|
3715
|
+
missingDetail: "Run `outlit setup pi` to install the Outlit skill"
|
|
3716
|
+
},
|
|
3717
|
+
openclaw: {
|
|
3718
|
+
name: "OpenClaw",
|
|
3719
|
+
missingDetail: "Run `outlit setup openclaw` to install the Outlit skill"
|
|
3720
|
+
}
|
|
3420
3721
|
};
|
|
3421
3722
|
});
|
|
3422
3723
|
|
|
3423
|
-
// src/commands/
|
|
3424
|
-
var
|
|
3425
|
-
__export(
|
|
3426
|
-
default: () =>
|
|
3724
|
+
// src/commands/upgrade.ts
|
|
3725
|
+
var exports_upgrade = {};
|
|
3726
|
+
__export(exports_upgrade, {
|
|
3727
|
+
default: () => upgrade_default
|
|
3427
3728
|
});
|
|
3428
|
-
var
|
|
3429
|
-
var
|
|
3729
|
+
var upgrade_default;
|
|
3730
|
+
var init_upgrade = __esm(() => {
|
|
3731
|
+
init_dist();
|
|
3732
|
+
init_config();
|
|
3733
|
+
init_output();
|
|
3734
|
+
init_update();
|
|
3735
|
+
upgrade_default = defineCommand2({
|
|
3736
|
+
meta: {
|
|
3737
|
+
name: "upgrade",
|
|
3738
|
+
description: [
|
|
3739
|
+
"Upgrade the Outlit CLI using the same package manager it was installed with.",
|
|
3740
|
+
"",
|
|
3741
|
+
"Checks npm for the latest published version first.",
|
|
3742
|
+
"If the current version is already latest, no install command is run.",
|
|
3743
|
+
"",
|
|
3744
|
+
"Examples:",
|
|
3745
|
+
" outlit upgrade"
|
|
3746
|
+
].join(`
|
|
3747
|
+
`)
|
|
3748
|
+
},
|
|
3749
|
+
async run() {
|
|
3750
|
+
const upgradeCommand = getUpgradeCommand();
|
|
3751
|
+
if (!upgradeCommand) {
|
|
3752
|
+
return outputError({
|
|
3753
|
+
message: "Could not determine how Outlit CLI was installed. Update it manually with your package manager, for example `bun add -g @outlit/cli` or `npm install -g @outlit/cli`.",
|
|
3754
|
+
code: "unknown_installer"
|
|
3755
|
+
}, false);
|
|
3756
|
+
}
|
|
3757
|
+
let latestVersion;
|
|
3758
|
+
try {
|
|
3759
|
+
latestVersion = await fetchLatestCliVersion2();
|
|
3760
|
+
} catch {
|
|
3761
|
+
return outputError({
|
|
3762
|
+
message: "Could not check for CLI updates. Try again later or update manually.",
|
|
3763
|
+
code: "update_check_failed"
|
|
3764
|
+
}, false);
|
|
3765
|
+
}
|
|
3766
|
+
if (compareVersions2(CLI_VERSION2, latestVersion) >= 0) {
|
|
3767
|
+
console.log(`Outlit CLI is already up to date (v${CLI_VERSION2})`);
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3770
|
+
try {
|
|
3771
|
+
runUpgradeCommand(upgradeCommand);
|
|
3772
|
+
} catch (err) {
|
|
3773
|
+
return outputError({
|
|
3774
|
+
message: errorMessage(err, `Failed to run ${upgradeCommand.displayCommand}`),
|
|
3775
|
+
code: "upgrade_failed"
|
|
3776
|
+
}, false);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
});
|
|
3780
|
+
});
|
|
3781
|
+
|
|
3782
|
+
// src/commands/facts/list.ts
|
|
3783
|
+
var exports_list3 = {};
|
|
3784
|
+
__export(exports_list3, {
|
|
3785
|
+
default: () => list_default3
|
|
3786
|
+
});
|
|
3787
|
+
function parseCsvArg(value) {
|
|
3788
|
+
if (!value)
|
|
3789
|
+
return;
|
|
3790
|
+
const items = splitCsv(value).map((item) => item.trim()).filter(Boolean);
|
|
3791
|
+
return items.length > 0 ? items : undefined;
|
|
3792
|
+
}
|
|
3793
|
+
function invalidValues(values, allowed) {
|
|
3794
|
+
if (!values)
|
|
3795
|
+
return [];
|
|
3796
|
+
return values.filter((value) => !allowed.includes(value));
|
|
3797
|
+
}
|
|
3798
|
+
var list_default3;
|
|
3799
|
+
var init_list3 = __esm(() => {
|
|
3430
3800
|
init_dist();
|
|
3431
3801
|
init_auth();
|
|
3432
3802
|
init_output2();
|
|
3433
3803
|
init_pagination();
|
|
3804
|
+
init_tool_contracts();
|
|
3434
3805
|
init_api();
|
|
3435
|
-
|
|
3806
|
+
init_config();
|
|
3807
|
+
init_output();
|
|
3808
|
+
list_default3 = defineCommand2({
|
|
3436
3809
|
meta: {
|
|
3437
|
-
name: "
|
|
3810
|
+
name: "list",
|
|
3438
3811
|
description: [
|
|
3439
|
-
"
|
|
3812
|
+
"List structured facts for a customer.",
|
|
3440
3813
|
"",
|
|
3441
|
-
"
|
|
3442
|
-
"specified customer within the given timeframe.",
|
|
3814
|
+
"Filter by fact status, source type, or occurrence date range.",
|
|
3443
3815
|
"",
|
|
3444
3816
|
"Examples:",
|
|
3445
|
-
" outlit facts acme.com",
|
|
3446
|
-
" outlit facts acme.com --
|
|
3447
|
-
" outlit facts acme.com --
|
|
3817
|
+
" outlit facts list acme.com",
|
|
3818
|
+
" outlit facts list acme.com --status ACTIVE",
|
|
3819
|
+
" outlit facts list acme.com --source-types CALL,EMAIL --after 2025-01-01T00:00:00Z",
|
|
3820
|
+
" outlit facts list acme.com --limit 50 --json",
|
|
3821
|
+
"",
|
|
3822
|
+
`Statuses: ${customerFactStatuses.join(", ")}`,
|
|
3823
|
+
`Source types: ${customerSourceTypes.join(", ")}`,
|
|
3448
3824
|
"",
|
|
3449
3825
|
AGENT_JSON_HINT
|
|
3450
3826
|
].join(`
|
|
@@ -3459,51 +3835,817 @@ var init_facts = __esm(() => {
|
|
|
3459
3835
|
description: "Customer UUID or domain to retrieve facts for",
|
|
3460
3836
|
required: true
|
|
3461
3837
|
},
|
|
3462
|
-
|
|
3838
|
+
status: {
|
|
3463
3839
|
type: "string",
|
|
3464
|
-
description:
|
|
3465
|
-
|
|
3840
|
+
description: `Comma-separated fact statuses (${customerFactStatuses.join(", ")})`
|
|
3841
|
+
},
|
|
3842
|
+
"source-types": {
|
|
3843
|
+
type: "string",
|
|
3844
|
+
description: `Comma-separated generic source type filter (${customerSourceTypes.join(", ")})`
|
|
3845
|
+
},
|
|
3846
|
+
after: {
|
|
3847
|
+
type: "string",
|
|
3848
|
+
description: "Filter to facts occurring after this ISO 8601 datetime"
|
|
3849
|
+
},
|
|
3850
|
+
before: {
|
|
3851
|
+
type: "string",
|
|
3852
|
+
description: "Filter to facts occurring before this ISO 8601 datetime"
|
|
3466
3853
|
}
|
|
3467
3854
|
},
|
|
3468
3855
|
async run({ args }) {
|
|
3469
3856
|
const json = !!args.json;
|
|
3470
|
-
const
|
|
3471
|
-
const
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3857
|
+
const statuses = parseCsvArg(args.status);
|
|
3858
|
+
const sourceTypes = parseCsvArg(args["source-types"]);
|
|
3859
|
+
const invalidStatuses = invalidValues(statuses, customerFactStatuses);
|
|
3860
|
+
if (invalidStatuses.length > 0) {
|
|
3861
|
+
return outputError({
|
|
3862
|
+
message: `Unknown fact statuses: ${invalidStatuses.join(", ")}. Allowed: ${customerFactStatuses.join(", ")}`,
|
|
3863
|
+
code: "invalid_input"
|
|
3864
|
+
}, json);
|
|
3865
|
+
}
|
|
3866
|
+
const invalidSourceTypes = invalidValues(sourceTypes, customerSourceTypes);
|
|
3867
|
+
if (invalidSourceTypes.length > 0) {
|
|
3868
|
+
return outputError({
|
|
3869
|
+
message: `Unknown source types: ${invalidSourceTypes.join(", ")}. Allowed: ${customerSourceTypes.join(", ")}`,
|
|
3870
|
+
code: "invalid_input"
|
|
3871
|
+
}, json);
|
|
3872
|
+
}
|
|
3873
|
+
const afterDate = args.after ? new Date(args.after) : null;
|
|
3874
|
+
const beforeDate = args.before ? new Date(args.before) : null;
|
|
3875
|
+
if (afterDate && Number.isNaN(afterDate.getTime())) {
|
|
3876
|
+
return outputError({
|
|
3877
|
+
message: "--after must be a valid ISO 8601 datetime",
|
|
3878
|
+
code: "invalid_input"
|
|
3879
|
+
}, json);
|
|
3880
|
+
}
|
|
3881
|
+
if (beforeDate && Number.isNaN(beforeDate.getTime())) {
|
|
3882
|
+
return outputError({
|
|
3883
|
+
message: "--before must be a valid ISO 8601 datetime",
|
|
3884
|
+
code: "invalid_input"
|
|
3885
|
+
}, json);
|
|
3886
|
+
}
|
|
3887
|
+
if (afterDate && beforeDate && afterDate.getTime() > beforeDate.getTime()) {
|
|
3888
|
+
return outputError({
|
|
3889
|
+
message: "--after must be before or equal to --before",
|
|
3890
|
+
code: "invalid_input"
|
|
3891
|
+
}, json);
|
|
3892
|
+
}
|
|
3893
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
3894
|
+
const params = {
|
|
3895
|
+
customer: args.customer
|
|
3896
|
+
};
|
|
3897
|
+
if (statuses)
|
|
3898
|
+
params.status = statuses;
|
|
3899
|
+
if (sourceTypes)
|
|
3900
|
+
params.sourceTypes = sourceTypes;
|
|
3901
|
+
if (args.after)
|
|
3902
|
+
params.after = args.after;
|
|
3903
|
+
if (args.before)
|
|
3904
|
+
params.before = args.before;
|
|
3905
|
+
applyPagination(params, args, json);
|
|
3906
|
+
return runTool(client, customerToolContracts.outlit_list_facts.toolName, params, json);
|
|
3907
|
+
}
|
|
3908
|
+
});
|
|
3909
|
+
});
|
|
3910
|
+
|
|
3911
|
+
// src/commands/facts/get.ts
|
|
3912
|
+
var exports_get2 = {};
|
|
3913
|
+
__export(exports_get2, {
|
|
3914
|
+
default: () => get_default2
|
|
3915
|
+
});
|
|
3916
|
+
function parseCsvArg2(value) {
|
|
3917
|
+
if (!value)
|
|
3918
|
+
return;
|
|
3919
|
+
const items = splitCsv(value).map((item) => item.trim()).filter(Boolean);
|
|
3920
|
+
return items.length > 0 ? items : undefined;
|
|
3921
|
+
}
|
|
3922
|
+
var get_default2;
|
|
3923
|
+
var init_get2 = __esm(() => {
|
|
3924
|
+
init_dist();
|
|
3925
|
+
init_auth();
|
|
3926
|
+
init_output2();
|
|
3927
|
+
init_tool_contracts();
|
|
3928
|
+
init_api();
|
|
3929
|
+
init_config();
|
|
3930
|
+
get_default2 = defineCommand2({
|
|
3931
|
+
meta: {
|
|
3932
|
+
name: "get",
|
|
3933
|
+
description: [
|
|
3934
|
+
"Get one exact fact by ID.",
|
|
3935
|
+
"",
|
|
3936
|
+
"Use --include evidence to request best-effort evidence expansion.",
|
|
3937
|
+
"",
|
|
3938
|
+
"Examples:",
|
|
3939
|
+
" outlit facts get --fact-id fact_123",
|
|
3940
|
+
" outlit facts get --fact-id fact_123 --include evidence",
|
|
3941
|
+
"",
|
|
3942
|
+
AGENT_JSON_HINT
|
|
3943
|
+
].join(`
|
|
3944
|
+
`)
|
|
3945
|
+
},
|
|
3946
|
+
args: {
|
|
3947
|
+
...authArgs,
|
|
3948
|
+
...outputArgs,
|
|
3949
|
+
"fact-id": {
|
|
3950
|
+
type: "string",
|
|
3951
|
+
description: "Fact ID to fetch",
|
|
3952
|
+
required: true
|
|
3953
|
+
},
|
|
3954
|
+
include: {
|
|
3955
|
+
type: "string",
|
|
3956
|
+
description: "Comma-separated best-effort expansions (for example: evidence)"
|
|
3957
|
+
}
|
|
3958
|
+
},
|
|
3959
|
+
async run({ args }) {
|
|
3960
|
+
const json = !!args.json;
|
|
3961
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
3962
|
+
const params = {
|
|
3963
|
+
factId: args["fact-id"]
|
|
3964
|
+
};
|
|
3965
|
+
const include = parseCsvArg2(args.include);
|
|
3966
|
+
if (include)
|
|
3967
|
+
params.include = include;
|
|
3968
|
+
return runTool(client, customerToolContracts.outlit_get_fact.toolName, params, json);
|
|
3969
|
+
}
|
|
3970
|
+
});
|
|
3971
|
+
});
|
|
3972
|
+
|
|
3973
|
+
// src/commands/facts/index.ts
|
|
3974
|
+
var exports_facts = {};
|
|
3975
|
+
__export(exports_facts, {
|
|
3976
|
+
default: () => facts_default
|
|
3977
|
+
});
|
|
3978
|
+
var facts_default;
|
|
3979
|
+
var init_facts = __esm(() => {
|
|
3980
|
+
init_dist();
|
|
3981
|
+
init_output2();
|
|
3982
|
+
facts_default = defineCommand2({
|
|
3983
|
+
meta: {
|
|
3984
|
+
name: "facts",
|
|
3985
|
+
description: [
|
|
3986
|
+
"Query structured customer facts.",
|
|
3987
|
+
"",
|
|
3988
|
+
"Subcommands:",
|
|
3989
|
+
" list -- list facts for a customer with filters",
|
|
3990
|
+
" get -- fetch one exact fact by id",
|
|
3991
|
+
"",
|
|
3992
|
+
AGENT_JSON_HINT
|
|
3993
|
+
].join(`
|
|
3994
|
+
`)
|
|
3995
|
+
},
|
|
3996
|
+
subCommands: {
|
|
3997
|
+
list: () => Promise.resolve().then(() => (init_list3(), exports_list3)).then((m) => m.default),
|
|
3998
|
+
get: () => Promise.resolve().then(() => (init_get2(), exports_get2)).then((m) => m.default)
|
|
3999
|
+
}
|
|
4000
|
+
});
|
|
4001
|
+
});
|
|
4002
|
+
|
|
4003
|
+
// src/commands/sources/get.ts
|
|
4004
|
+
var exports_get3 = {};
|
|
4005
|
+
__export(exports_get3, {
|
|
4006
|
+
default: () => get_default3
|
|
4007
|
+
});
|
|
4008
|
+
var get_default3;
|
|
4009
|
+
var init_get3 = __esm(() => {
|
|
4010
|
+
init_dist();
|
|
4011
|
+
init_auth();
|
|
4012
|
+
init_output2();
|
|
4013
|
+
init_tool_contracts();
|
|
4014
|
+
init_api();
|
|
4015
|
+
init_output();
|
|
4016
|
+
get_default3 = defineCommand2({
|
|
4017
|
+
meta: {
|
|
4018
|
+
name: "get",
|
|
4019
|
+
description: [
|
|
4020
|
+
"Get one exact source by source type and source id.",
|
|
4021
|
+
"",
|
|
4022
|
+
"Examples:",
|
|
4023
|
+
" outlit sources get --source-type CALL --source-id call_123",
|
|
4024
|
+
" outlit sources get --source-type SUPPORT_TICKET --source-id ticket_456 --json",
|
|
4025
|
+
"",
|
|
4026
|
+
`Source types: ${customerSourceTypes.join(", ")}`,
|
|
4027
|
+
"",
|
|
4028
|
+
AGENT_JSON_HINT
|
|
4029
|
+
].join(`
|
|
4030
|
+
`)
|
|
4031
|
+
},
|
|
4032
|
+
args: {
|
|
4033
|
+
...authArgs,
|
|
4034
|
+
...outputArgs,
|
|
4035
|
+
"source-type": {
|
|
4036
|
+
type: "string",
|
|
4037
|
+
description: "Canonical source type",
|
|
4038
|
+
required: true
|
|
4039
|
+
},
|
|
4040
|
+
"source-id": {
|
|
4041
|
+
type: "string",
|
|
4042
|
+
description: "Exact source id",
|
|
4043
|
+
required: true
|
|
4044
|
+
}
|
|
4045
|
+
},
|
|
4046
|
+
async run({ args }) {
|
|
4047
|
+
const json = !!args.json;
|
|
4048
|
+
if (!customerSourceTypes.includes(args["source-type"])) {
|
|
4049
|
+
return outputError({
|
|
4050
|
+
message: `--source-type must be one of ${customerSourceTypes.join(", ")}`,
|
|
4051
|
+
code: "invalid_input"
|
|
4052
|
+
}, json);
|
|
4053
|
+
}
|
|
4054
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
4055
|
+
return runTool(client, customerToolContracts.outlit_get_source.toolName, {
|
|
4056
|
+
sourceType: args["source-type"],
|
|
4057
|
+
sourceId: args["source-id"]
|
|
4058
|
+
}, json);
|
|
4059
|
+
}
|
|
4060
|
+
});
|
|
4061
|
+
});
|
|
4062
|
+
|
|
4063
|
+
// src/commands/sources/index.ts
|
|
4064
|
+
var exports_sources = {};
|
|
4065
|
+
__export(exports_sources, {
|
|
4066
|
+
default: () => sources_default
|
|
4067
|
+
});
|
|
4068
|
+
var sources_default;
|
|
4069
|
+
var init_sources = __esm(() => {
|
|
4070
|
+
init_dist();
|
|
4071
|
+
init_output2();
|
|
4072
|
+
sources_default = defineCommand2({
|
|
4073
|
+
meta: {
|
|
4074
|
+
name: "sources",
|
|
4075
|
+
description: [
|
|
4076
|
+
"Fetch concrete customer sources by type and id.",
|
|
4077
|
+
"",
|
|
4078
|
+
"Subcommands:",
|
|
4079
|
+
" get -- fetch one exact source by sourceType and sourceId",
|
|
4080
|
+
"",
|
|
4081
|
+
AGENT_JSON_HINT
|
|
4082
|
+
].join(`
|
|
4083
|
+
`)
|
|
4084
|
+
},
|
|
4085
|
+
subCommands: {
|
|
4086
|
+
get: () => Promise.resolve().then(() => (init_get3(), exports_get3)).then((m) => m.default)
|
|
4087
|
+
}
|
|
4088
|
+
});
|
|
4089
|
+
});
|
|
4090
|
+
|
|
4091
|
+
// src/commands/search.ts
|
|
4092
|
+
var exports_search = {};
|
|
4093
|
+
__export(exports_search, {
|
|
3484
4094
|
default: () => search_default
|
|
3485
4095
|
});
|
|
3486
4096
|
var search_default;
|
|
3487
4097
|
var init_search = __esm(() => {
|
|
4098
|
+
init_dist();
|
|
4099
|
+
init_auth();
|
|
4100
|
+
init_output2();
|
|
4101
|
+
init_tool_contracts();
|
|
4102
|
+
init_api();
|
|
4103
|
+
init_config();
|
|
4104
|
+
init_output();
|
|
4105
|
+
search_default = defineCommand2({
|
|
4106
|
+
meta: {
|
|
4107
|
+
name: "search",
|
|
4108
|
+
description: [
|
|
4109
|
+
"Search customer context using natural language.",
|
|
4110
|
+
"",
|
|
4111
|
+
"Performs a semantic search over grouped source and fact results.",
|
|
4112
|
+
"Optionally scope to a specific customer with --customer.",
|
|
4113
|
+
"",
|
|
4114
|
+
"Examples:",
|
|
4115
|
+
" outlit search 'pricing objections last quarter'",
|
|
4116
|
+
" outlit search 'churn risk signals' --customer acme.com",
|
|
4117
|
+
" outlit search 'expansion opportunities' --top-k 50 --json",
|
|
4118
|
+
" outlit search 'support escalations' --after 2025-01-01T00:00:00Z --before 2025-03-31T23:59:59Z",
|
|
4119
|
+
" outlit search 'onboarding issues' --source-types CALL,EMAIL",
|
|
4120
|
+
"",
|
|
4121
|
+
AGENT_JSON_HINT
|
|
4122
|
+
].join(`
|
|
4123
|
+
`)
|
|
4124
|
+
},
|
|
4125
|
+
args: {
|
|
4126
|
+
...authArgs,
|
|
4127
|
+
...outputArgs,
|
|
4128
|
+
query: {
|
|
4129
|
+
type: "positional",
|
|
4130
|
+
description: "Natural language search query",
|
|
4131
|
+
required: true
|
|
4132
|
+
},
|
|
4133
|
+
customer: {
|
|
4134
|
+
type: "string",
|
|
4135
|
+
description: "Scope search to a specific customer (UUID or domain)"
|
|
4136
|
+
},
|
|
4137
|
+
"top-k": {
|
|
4138
|
+
type: "string",
|
|
4139
|
+
description: "Maximum number of results to return (1–50). Default: 20."
|
|
4140
|
+
},
|
|
4141
|
+
after: {
|
|
4142
|
+
type: "string",
|
|
4143
|
+
description: "Filter to events occurring after this datetime (ISO 8601, e.g. 2025-01-01T00:00:00Z)"
|
|
4144
|
+
},
|
|
4145
|
+
before: {
|
|
4146
|
+
type: "string",
|
|
4147
|
+
description: "Filter to events occurring before this datetime (ISO 8601, e.g. 2025-03-31T23:59:59Z)"
|
|
4148
|
+
},
|
|
4149
|
+
"source-types": {
|
|
4150
|
+
type: "string",
|
|
4151
|
+
description: `Comma-separated generic source type filter (${customerSourceTypes.join(", ")})`
|
|
4152
|
+
}
|
|
4153
|
+
},
|
|
4154
|
+
async run({ args }) {
|
|
4155
|
+
const json = !!args.json;
|
|
4156
|
+
const topK = args["top-k"] ? Number(args["top-k"]) : undefined;
|
|
4157
|
+
if (topK !== undefined && (!Number.isFinite(topK) || !Number.isInteger(topK) || topK < 1 || topK > 50)) {
|
|
4158
|
+
return outputError({ message: "--top-k must be an integer between 1 and 50", code: "invalid_input" }, json);
|
|
4159
|
+
}
|
|
4160
|
+
const sourceTypes = args["source-types"] ? splitCsv(args["source-types"]).map((item) => item.trim()).filter(Boolean) : undefined;
|
|
4161
|
+
const invalidSourceTypes = sourceTypes?.filter((value) => !customerSourceTypes.includes(value));
|
|
4162
|
+
if (invalidSourceTypes && invalidSourceTypes.length > 0) {
|
|
4163
|
+
return outputError({
|
|
4164
|
+
message: `Unknown source types: ${invalidSourceTypes.join(", ")}. Allowed: ${customerSourceTypes.join(", ")}`,
|
|
4165
|
+
code: "invalid_input"
|
|
4166
|
+
}, json);
|
|
4167
|
+
}
|
|
4168
|
+
const resolved = resolveCustomerContextSearchInput({
|
|
4169
|
+
query: args.query,
|
|
4170
|
+
customer: args.customer,
|
|
4171
|
+
topK,
|
|
4172
|
+
after: args.after,
|
|
4173
|
+
before: args.before,
|
|
4174
|
+
sourceTypes
|
|
4175
|
+
});
|
|
4176
|
+
if (!resolved.ok) {
|
|
4177
|
+
return outputError({
|
|
4178
|
+
message: resolved.message,
|
|
4179
|
+
code: "invalid_input"
|
|
4180
|
+
}, json);
|
|
4181
|
+
}
|
|
4182
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
4183
|
+
return runTool(client, customerToolContracts.outlit_search_customer_context.toolName, resolved.request, json);
|
|
4184
|
+
}
|
|
4185
|
+
});
|
|
4186
|
+
});
|
|
4187
|
+
|
|
4188
|
+
// src/commands/sql.ts
|
|
4189
|
+
var exports_sql = {};
|
|
4190
|
+
__export(exports_sql, {
|
|
4191
|
+
default: () => sql_default
|
|
4192
|
+
});
|
|
4193
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
4194
|
+
var sql_default;
|
|
4195
|
+
var init_sql = __esm(() => {
|
|
4196
|
+
init_dist();
|
|
4197
|
+
init_auth();
|
|
4198
|
+
init_output2();
|
|
4199
|
+
init_tool_contracts();
|
|
4200
|
+
init_api();
|
|
4201
|
+
init_output();
|
|
4202
|
+
sql_default = defineCommand2({
|
|
4203
|
+
meta: {
|
|
4204
|
+
name: "sql",
|
|
4205
|
+
description: [
|
|
4206
|
+
"Run a SQL query against Outlit's analytics database.",
|
|
4207
|
+
"",
|
|
4208
|
+
"Provide the query as a positional argument or via --query-file.",
|
|
4209
|
+
"When both are provided, --query-file takes precedence.",
|
|
4210
|
+
"",
|
|
4211
|
+
`Available tables: ${schemaTables.join(", ")}`,
|
|
4212
|
+
"",
|
|
4213
|
+
"Examples:",
|
|
4214
|
+
" outlit sql 'SELECT * FROM events LIMIT 10'",
|
|
4215
|
+
" outlit sql --query-file ./my-query.sql",
|
|
4216
|
+
" outlit sql 'SELECT count(*) FROM events' --limit 1 --json",
|
|
4217
|
+
"",
|
|
4218
|
+
AGENT_JSON_HINT
|
|
4219
|
+
].join(`
|
|
4220
|
+
`)
|
|
4221
|
+
},
|
|
4222
|
+
args: {
|
|
4223
|
+
...authArgs,
|
|
4224
|
+
...outputArgs,
|
|
4225
|
+
query: {
|
|
4226
|
+
type: "positional",
|
|
4227
|
+
description: "SQL query string to execute",
|
|
4228
|
+
required: false
|
|
4229
|
+
},
|
|
4230
|
+
"query-file": {
|
|
4231
|
+
type: "string",
|
|
4232
|
+
description: "Path to a .sql file to read the query from (takes precedence over positional)"
|
|
4233
|
+
},
|
|
4234
|
+
limit: {
|
|
4235
|
+
type: "string",
|
|
4236
|
+
description: "Maximum number of rows to return. Default: 1000.",
|
|
4237
|
+
default: "1000"
|
|
4238
|
+
}
|
|
4239
|
+
},
|
|
4240
|
+
async run({ args }) {
|
|
4241
|
+
const json = !!args.json;
|
|
4242
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
4243
|
+
let sql;
|
|
4244
|
+
if (args["query-file"]) {
|
|
4245
|
+
try {
|
|
4246
|
+
sql = readFileSync4(args["query-file"], "utf-8");
|
|
4247
|
+
} catch (err) {
|
|
4248
|
+
return outputError({
|
|
4249
|
+
message: `Cannot read file: ${errorMessage(err, "unknown error")}`,
|
|
4250
|
+
code: "file_error"
|
|
4251
|
+
}, json);
|
|
4252
|
+
}
|
|
4253
|
+
} else if (args.query) {
|
|
4254
|
+
sql = args.query;
|
|
4255
|
+
} else {
|
|
4256
|
+
return outputError({ message: "Provide a SQL query or --query-file", code: "missing_input" }, json);
|
|
4257
|
+
}
|
|
4258
|
+
const limit = Number(args.limit);
|
|
4259
|
+
if (!Number.isFinite(limit) || limit <= 0) {
|
|
4260
|
+
return outputError({ message: "--limit must be a positive number", code: "invalid_input" }, json);
|
|
4261
|
+
}
|
|
4262
|
+
return runTool(client, customerToolContracts.outlit_query.toolName, { sql, limit }, json);
|
|
4263
|
+
}
|
|
4264
|
+
});
|
|
4265
|
+
});
|
|
4266
|
+
|
|
4267
|
+
// src/commands/schema.ts
|
|
4268
|
+
var exports_schema = {};
|
|
4269
|
+
__export(exports_schema, {
|
|
4270
|
+
default: () => schema_default
|
|
4271
|
+
});
|
|
4272
|
+
var schema_default;
|
|
4273
|
+
var init_schema = __esm(() => {
|
|
4274
|
+
init_dist();
|
|
4275
|
+
init_auth();
|
|
4276
|
+
init_output2();
|
|
4277
|
+
init_tool_contracts();
|
|
4278
|
+
init_api();
|
|
4279
|
+
schema_default = defineCommand2({
|
|
4280
|
+
meta: {
|
|
4281
|
+
name: "schema",
|
|
4282
|
+
description: [
|
|
4283
|
+
"Describe the analytics database schema.",
|
|
4284
|
+
"",
|
|
4285
|
+
"Without a table name, returns the full schema for all tables.",
|
|
4286
|
+
"With a table name, returns detailed column info for that table.",
|
|
4287
|
+
"",
|
|
4288
|
+
`Available tables: ${schemaTables.join(", ")}`,
|
|
4289
|
+
"",
|
|
4290
|
+
"Examples:",
|
|
4291
|
+
" outlit schema",
|
|
4292
|
+
" outlit schema events",
|
|
4293
|
+
" outlit schema customer_dimensions --json",
|
|
4294
|
+
"",
|
|
4295
|
+
AGENT_JSON_HINT
|
|
4296
|
+
].join(`
|
|
4297
|
+
`)
|
|
4298
|
+
},
|
|
4299
|
+
args: {
|
|
4300
|
+
...authArgs,
|
|
4301
|
+
...outputArgs,
|
|
4302
|
+
table: {
|
|
4303
|
+
type: "positional",
|
|
4304
|
+
description: `Table to describe (${schemaTables.join(", ")}). Optional.`,
|
|
4305
|
+
required: false
|
|
4306
|
+
}
|
|
4307
|
+
},
|
|
4308
|
+
async run({ args }) {
|
|
4309
|
+
const json = !!args.json;
|
|
4310
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
4311
|
+
const params = {};
|
|
4312
|
+
if (args.table)
|
|
4313
|
+
params.table = args.table;
|
|
4314
|
+
return runTool(client, customerToolContracts.outlit_schema.toolName, params, json);
|
|
4315
|
+
}
|
|
4316
|
+
});
|
|
4317
|
+
});
|
|
4318
|
+
|
|
4319
|
+
// src/lib/providers.ts
|
|
4320
|
+
function resolveProvider(input) {
|
|
4321
|
+
const normalized = input.toLowerCase().trim();
|
|
4322
|
+
const provider = INTEGRATION_PROVIDERS[normalized];
|
|
4323
|
+
if (provider)
|
|
4324
|
+
return { provider, cliName: normalized };
|
|
4325
|
+
const suggestion = findClosestMatch(normalized);
|
|
4326
|
+
const base = `Unknown integration: "${input}". Available: ${PROVIDER_NAMES.join(", ")}`;
|
|
4327
|
+
if (suggestion) {
|
|
4328
|
+
return { error: `${base}
|
|
4329
|
+
|
|
4330
|
+
Did you mean "${suggestion}"?`, suggestion };
|
|
4331
|
+
}
|
|
4332
|
+
return { error: base };
|
|
4333
|
+
}
|
|
4334
|
+
function resolveProviderOrExit(input, json) {
|
|
4335
|
+
const result = resolveProvider(input);
|
|
4336
|
+
if ("error" in result) {
|
|
4337
|
+
return outputError({ message: result.error, code: "unknown_provider" }, json);
|
|
4338
|
+
}
|
|
4339
|
+
return result;
|
|
4340
|
+
}
|
|
4341
|
+
function findClosestMatch(input) {
|
|
4342
|
+
return PROVIDER_NAMES.find((name) => name.startsWith(input) || input.startsWith(name)) ?? null;
|
|
4343
|
+
}
|
|
4344
|
+
var INTEGRATION_PROVIDERS, PROVIDER_NAMES;
|
|
4345
|
+
var init_providers = __esm(() => {
|
|
4346
|
+
init_output();
|
|
4347
|
+
INTEGRATION_PROVIDERS = {
|
|
4348
|
+
slack: { id: "slack", name: "Slack", category: "communication", authType: "oauth" },
|
|
4349
|
+
gmail: { id: "google-mail", name: "Gmail", category: "communication", authType: "oauth" },
|
|
4350
|
+
"google-calendar": {
|
|
4351
|
+
id: "google-calendar",
|
|
4352
|
+
name: "Google Calendar",
|
|
4353
|
+
category: "calendar",
|
|
4354
|
+
authType: "oauth"
|
|
4355
|
+
},
|
|
4356
|
+
pylon: {
|
|
4357
|
+
id: "pylon",
|
|
4358
|
+
name: "Pylon",
|
|
4359
|
+
category: "support",
|
|
4360
|
+
authType: "api_key",
|
|
4361
|
+
configFields: [{ key: "apiKey", label: "API Key", secret: true }]
|
|
4362
|
+
},
|
|
4363
|
+
stripe: {
|
|
4364
|
+
id: "brex-api-key",
|
|
4365
|
+
name: "Stripe",
|
|
4366
|
+
category: "billing",
|
|
4367
|
+
authType: "api_key",
|
|
4368
|
+
configFields: [{ key: "apiKey", label: "API Key", secret: true }]
|
|
4369
|
+
},
|
|
4370
|
+
fireflies: {
|
|
4371
|
+
id: "fireflies",
|
|
4372
|
+
name: "Fireflies",
|
|
4373
|
+
category: "calls",
|
|
4374
|
+
authType: "api_key",
|
|
4375
|
+
configFields: [{ key: "apiKey", label: "API Key", secret: true }]
|
|
4376
|
+
},
|
|
4377
|
+
posthog: {
|
|
4378
|
+
id: "posthog",
|
|
4379
|
+
name: "PostHog",
|
|
4380
|
+
category: "analytics",
|
|
4381
|
+
authType: "api_key",
|
|
4382
|
+
configFields: [
|
|
4383
|
+
{ key: "apiKey", label: "API Key", secret: true },
|
|
4384
|
+
{ key: "region", label: "Region (us or eu)" },
|
|
4385
|
+
{ key: "projectId", label: "Project ID" }
|
|
4386
|
+
]
|
|
4387
|
+
},
|
|
4388
|
+
supabase: {
|
|
4389
|
+
id: "supabase",
|
|
4390
|
+
name: "Supabase",
|
|
4391
|
+
category: "data",
|
|
4392
|
+
authType: "api_key",
|
|
4393
|
+
configFields: [
|
|
4394
|
+
{ key: "projectUrl", label: "Project URL" },
|
|
4395
|
+
{ key: "serviceRoleKey", label: "Service Role Key", secret: true }
|
|
4396
|
+
]
|
|
4397
|
+
},
|
|
4398
|
+
clerk: {
|
|
4399
|
+
id: "clerk",
|
|
4400
|
+
name: "Clerk",
|
|
4401
|
+
category: "auth",
|
|
4402
|
+
authType: "api_key",
|
|
4403
|
+
configFields: [{ key: "secretKey", label: "Secret Key", secret: true }]
|
|
4404
|
+
}
|
|
4405
|
+
};
|
|
4406
|
+
PROVIDER_NAMES = Object.keys(INTEGRATION_PROVIDERS).sort();
|
|
4407
|
+
});
|
|
4408
|
+
|
|
4409
|
+
// src/commands/integrations/list.ts
|
|
4410
|
+
var exports_list4 = {};
|
|
4411
|
+
__export(exports_list4, {
|
|
4412
|
+
default: () => list_default4
|
|
4413
|
+
});
|
|
4414
|
+
var list_default4;
|
|
4415
|
+
var init_list4 = __esm(() => {
|
|
4416
|
+
init_dist();
|
|
4417
|
+
init_auth();
|
|
4418
|
+
init_output2();
|
|
4419
|
+
init_api();
|
|
4420
|
+
list_default4 = defineCommand2({
|
|
4421
|
+
meta: {
|
|
4422
|
+
name: "list",
|
|
4423
|
+
description: [
|
|
4424
|
+
"List available integrations and their connection status.",
|
|
4425
|
+
"",
|
|
4426
|
+
"Shows all supported third-party integrations with whether they",
|
|
4427
|
+
"are connected, available, or in an error state.",
|
|
4428
|
+
"",
|
|
4429
|
+
"Examples:",
|
|
4430
|
+
" outlit integrations list",
|
|
4431
|
+
" outlit integrations list --json",
|
|
4432
|
+
"",
|
|
4433
|
+
AGENT_JSON_HINT
|
|
4434
|
+
].join(`
|
|
4435
|
+
`)
|
|
4436
|
+
},
|
|
4437
|
+
args: {
|
|
4438
|
+
...authArgs,
|
|
4439
|
+
...outputArgs
|
|
4440
|
+
},
|
|
4441
|
+
async run({ args }) {
|
|
4442
|
+
const json = !!args.json;
|
|
4443
|
+
const client = await getClientOrExit(args["api-key"], json);
|
|
4444
|
+
return runTool(client, "outlit_list_integrations", {}, json, {
|
|
4445
|
+
spinnerMessage: "Fetching integrations...",
|
|
4446
|
+
table: {
|
|
4447
|
+
columns: [
|
|
4448
|
+
{ header: "Name", key: "name", format: (v) => truncate(v, 24) },
|
|
4449
|
+
{ header: "Category", key: "category", format: capitalize },
|
|
4450
|
+
{ header: "Status", key: "status" },
|
|
4451
|
+
{ header: "Last Synced", key: "lastDataReceivedAt", format: relativeDate }
|
|
4452
|
+
]
|
|
4453
|
+
}
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
});
|
|
4457
|
+
});
|
|
4458
|
+
|
|
4459
|
+
// src/lib/poll.ts
|
|
4460
|
+
async function pollUntil(fn, predicate, opts = {}) {
|
|
4461
|
+
const intervalMs = opts.intervalMs ?? 2000;
|
|
4462
|
+
const timeoutMs = opts.timeoutMs ?? 300000;
|
|
4463
|
+
const start = Date.now();
|
|
4464
|
+
while (Date.now() - start < timeoutMs) {
|
|
4465
|
+
try {
|
|
4466
|
+
const result = await fn();
|
|
4467
|
+
if (predicate(result))
|
|
4468
|
+
return result;
|
|
4469
|
+
} catch {}
|
|
4470
|
+
if (opts.spinner && opts.spinnerMessage) {
|
|
4471
|
+
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
4472
|
+
opts.spinner.update(`${opts.spinnerMessage} (${elapsed}s)`);
|
|
4473
|
+
}
|
|
4474
|
+
await sleep(intervalMs);
|
|
4475
|
+
}
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
function sleep(ms) {
|
|
4479
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4482
|
+
// src/commands/integrations/add.ts
|
|
4483
|
+
var exports_add = {};
|
|
4484
|
+
__export(exports_add, {
|
|
4485
|
+
default: () => add_default
|
|
4486
|
+
});
|
|
4487
|
+
async function addApiKeyProvider(client, provider, cliName, json, rawConfig) {
|
|
4488
|
+
const fields = provider.configFields ?? [];
|
|
4489
|
+
let config;
|
|
4490
|
+
if (rawConfig) {
|
|
4491
|
+
try {
|
|
4492
|
+
const parsed = JSON.parse(rawConfig);
|
|
4493
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4494
|
+
return outputError({ message: "Invalid JSON in --config. Expected a JSON object.", code: "invalid_config" }, json);
|
|
4495
|
+
}
|
|
4496
|
+
config = parsed;
|
|
4497
|
+
} catch {
|
|
4498
|
+
return outputError({ message: "Invalid JSON in --config. Expected a JSON object.", code: "invalid_config" }, json);
|
|
4499
|
+
}
|
|
4500
|
+
const missing = fields.filter((f) => !config[f.key]);
|
|
4501
|
+
if (missing.length > 0) {
|
|
4502
|
+
return outputError({
|
|
4503
|
+
message: `Missing required fields: ${missing.map((f) => f.key).join(", ")}`,
|
|
4504
|
+
code: "invalid_config"
|
|
4505
|
+
}, json);
|
|
4506
|
+
}
|
|
4507
|
+
} else if (isInteractive() && !isJsonMode(json)) {
|
|
4508
|
+
console.log(`
|
|
4509
|
+
Configure ${provider.name}:
|
|
4510
|
+
`);
|
|
4511
|
+
config = {};
|
|
4512
|
+
for (const field of fields) {
|
|
4513
|
+
config[field.key] = await promptInput(` ${field.label}`, { secret: field.secret });
|
|
4514
|
+
}
|
|
4515
|
+
console.log();
|
|
4516
|
+
} else {
|
|
4517
|
+
return outputResult({
|
|
4518
|
+
status: "config_required",
|
|
4519
|
+
provider: cliName,
|
|
4520
|
+
message: `${provider.name} requires API key configuration. Pass --config with the required fields.`,
|
|
4521
|
+
requiredFields: fields.map((f) => ({ key: f.key, label: f.label }))
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
const spinner = createSpinner(`Connecting ${provider.name}...`);
|
|
4525
|
+
try {
|
|
4526
|
+
await client.callTool("outlit_connect_integration", {
|
|
4527
|
+
provider: provider.id,
|
|
4528
|
+
config
|
|
4529
|
+
});
|
|
4530
|
+
spinner.stop(`${provider.name} connected successfully!`);
|
|
4531
|
+
if (isJsonMode(json)) {
|
|
4532
|
+
return outputResult({
|
|
4533
|
+
status: "connected",
|
|
4534
|
+
provider: cliName
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
console.log(` Use \`outlit integrations status ${cliName}\` to check sync progress.`);
|
|
4538
|
+
} catch (err) {
|
|
4539
|
+
spinner.fail(`Failed to connect ${provider.name}`);
|
|
4540
|
+
return outputError({ message: errorMessage(err, "Failed to connect integration"), code: "api_error" }, json);
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
async function addOAuthProvider(client, provider, cliName, json, force) {
|
|
4544
|
+
const spinner = createSpinner(`Connecting ${provider.name}...`);
|
|
4545
|
+
let connectData;
|
|
4546
|
+
try {
|
|
4547
|
+
connectData = await client.callTool("outlit_connect_integration", {
|
|
4548
|
+
provider: provider.id
|
|
4549
|
+
});
|
|
4550
|
+
} catch (err) {
|
|
4551
|
+
spinner.fail(`Failed to initiate ${provider.name} connection`);
|
|
4552
|
+
return outputError({ message: errorMessage(err, "Failed to start connection flow"), code: "api_error" }, json);
|
|
4553
|
+
}
|
|
4554
|
+
if (connectData.alreadyConnected && !force) {
|
|
4555
|
+
spinner.stop(`${provider.name} is already connected`);
|
|
4556
|
+
if (isJsonMode(json)) {
|
|
4557
|
+
return outputResult({
|
|
4558
|
+
status: "already_connected",
|
|
4559
|
+
provider: cliName,
|
|
4560
|
+
message: `${provider.name} is already connected. Use --force to reconnect.`
|
|
4561
|
+
});
|
|
4562
|
+
}
|
|
4563
|
+
console.log(`
|
|
4564
|
+
${provider.name} is already connected.`);
|
|
4565
|
+
console.log(` To reconnect, run: outlit integrations add ${cliName} --force`);
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
const integrationsUrl = `${client.baseUrl}/integrations`;
|
|
4569
|
+
spinner.update(`Opening browser for ${provider.name} authentication...`);
|
|
4570
|
+
const opened = openBrowser(integrationsUrl);
|
|
4571
|
+
if (!opened) {
|
|
4572
|
+
spinner.stop("Could not open browser automatically");
|
|
4573
|
+
if (isJsonMode(json)) {
|
|
4574
|
+
return outputResult({
|
|
4575
|
+
status: "browser_failed",
|
|
4576
|
+
provider: cliName,
|
|
4577
|
+
url: integrationsUrl,
|
|
4578
|
+
sessionId: connectData.sessionId
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
console.log(`
|
|
4582
|
+
Open this URL in your browser to connect ${provider.name}:`);
|
|
4583
|
+
console.log(` ${integrationsUrl}
|
|
4584
|
+
`);
|
|
4585
|
+
} else {
|
|
4586
|
+
spinner.stop(`Browser opened for ${provider.name} authentication`);
|
|
4587
|
+
}
|
|
4588
|
+
if (isJsonMode(json)) {
|
|
4589
|
+
return outputResult({
|
|
4590
|
+
status: "awaiting_auth",
|
|
4591
|
+
provider: cliName,
|
|
4592
|
+
sessionId: connectData.sessionId
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4595
|
+
await waitForConnection(client, connectData.sessionId, provider, cliName);
|
|
4596
|
+
}
|
|
4597
|
+
async function waitForConnection(client, sessionId, provider, cliName) {
|
|
4598
|
+
const spinner = createSpinner(`Waiting for ${provider.name} authentication...`);
|
|
4599
|
+
const result = await pollUntil(() => client.callTool("outlit_connect_status", { sessionId }).then((r) => r), (r) => r.status !== "pending", {
|
|
4600
|
+
intervalMs: 2000,
|
|
4601
|
+
timeoutMs: 300000,
|
|
4602
|
+
spinner,
|
|
4603
|
+
spinnerMessage: `Waiting for ${provider.name} authentication...`
|
|
4604
|
+
});
|
|
4605
|
+
if (!result || result.status === "expired") {
|
|
4606
|
+
spinner.fail("Connection timed out");
|
|
4607
|
+
console.log(`
|
|
4608
|
+
The authentication session expired.`);
|
|
4609
|
+
console.log(` Run \`outlit integrations add ${cliName}\` to try again.`);
|
|
4610
|
+
process.exit(1);
|
|
4611
|
+
}
|
|
4612
|
+
if (result.status === "failed") {
|
|
4613
|
+
spinner.fail(`${provider.name} connection failed`);
|
|
4614
|
+
if (result.error)
|
|
4615
|
+
console.log(`
|
|
4616
|
+
${result.error}`);
|
|
4617
|
+
process.exit(1);
|
|
4618
|
+
}
|
|
4619
|
+
spinner.stop(`${provider.name} connected successfully!`);
|
|
4620
|
+
console.log(` Sync will begin automatically.`);
|
|
4621
|
+
console.log(` Use \`outlit integrations status ${cliName}\` to check progress.`);
|
|
4622
|
+
}
|
|
4623
|
+
var add_default;
|
|
4624
|
+
var init_add = __esm(() => {
|
|
3488
4625
|
init_dist();
|
|
3489
4626
|
init_auth();
|
|
3490
4627
|
init_output2();
|
|
3491
4628
|
init_api();
|
|
3492
4629
|
init_output();
|
|
3493
|
-
|
|
4630
|
+
init_providers();
|
|
4631
|
+
init_spinner();
|
|
4632
|
+
init_tty();
|
|
4633
|
+
add_default = defineCommand2({
|
|
3494
4634
|
meta: {
|
|
3495
|
-
name: "
|
|
4635
|
+
name: "add",
|
|
3496
4636
|
description: [
|
|
3497
|
-
"
|
|
4637
|
+
"Connect a new integration.",
|
|
3498
4638
|
"",
|
|
3499
|
-
"
|
|
3500
|
-
"
|
|
4639
|
+
"For OAuth providers (Slack, Gmail, etc.), opens the browser for authentication.",
|
|
4640
|
+
"For API-key providers (Stripe, PostHog, etc.), accepts credentials via --config or interactive prompts.",
|
|
3501
4641
|
"",
|
|
3502
4642
|
"Examples:",
|
|
3503
|
-
" outlit
|
|
3504
|
-
|
|
3505
|
-
" outlit
|
|
3506
|
-
" outlit
|
|
4643
|
+
" outlit integrations add slack",
|
|
4644
|
+
` outlit integrations add stripe --config '{"apiKey": "rk_xxx"}'`,
|
|
4645
|
+
" outlit integrations add posthog --json # outputs required fields as JSON",
|
|
4646
|
+
" outlit integrations add slack --force # reconnect if already connected",
|
|
4647
|
+
"",
|
|
4648
|
+
`Providers: ${PROVIDER_NAMES.join(", ")}`,
|
|
3507
4649
|
"",
|
|
3508
4650
|
AGENT_JSON_HINT
|
|
3509
4651
|
].join(`
|
|
@@ -3512,79 +4654,61 @@ var init_search = __esm(() => {
|
|
|
3512
4654
|
args: {
|
|
3513
4655
|
...authArgs,
|
|
3514
4656
|
...outputArgs,
|
|
3515
|
-
|
|
4657
|
+
provider: {
|
|
3516
4658
|
type: "positional",
|
|
3517
|
-
description: "
|
|
4659
|
+
description: "Integration provider to connect",
|
|
3518
4660
|
required: true
|
|
3519
4661
|
},
|
|
3520
|
-
|
|
3521
|
-
type: "
|
|
3522
|
-
description: "
|
|
3523
|
-
},
|
|
3524
|
-
"top-k": {
|
|
3525
|
-
type: "string",
|
|
3526
|
-
description: "Maximum number of results to return. Default: 20.",
|
|
3527
|
-
default: "20"
|
|
3528
|
-
},
|
|
3529
|
-
after: {
|
|
3530
|
-
type: "string",
|
|
3531
|
-
description: "Filter to events occurring after this date (ISO 8601, e.g. 2025-01-01)"
|
|
4662
|
+
force: {
|
|
4663
|
+
type: "boolean",
|
|
4664
|
+
description: "Reconnect even if the integration is already connected."
|
|
3532
4665
|
},
|
|
3533
|
-
|
|
4666
|
+
config: {
|
|
3534
4667
|
type: "string",
|
|
3535
|
-
description:
|
|
4668
|
+
description: `JSON configuration for API-key integrations (e.g. '{"apiKey": "sk_xxx"}')`
|
|
3536
4669
|
}
|
|
3537
4670
|
},
|
|
3538
4671
|
async run({ args }) {
|
|
3539
4672
|
const json = !!args.json;
|
|
3540
4673
|
const client = await getClientOrExit(args["api-key"], json);
|
|
3541
|
-
const
|
|
3542
|
-
if (
|
|
3543
|
-
|
|
4674
|
+
const { provider, cliName } = resolveProviderOrExit(args.provider, json);
|
|
4675
|
+
if (provider.authType === "api_key") {
|
|
4676
|
+
await addApiKeyProvider(client, provider, cliName, json, args.config);
|
|
4677
|
+
} else {
|
|
4678
|
+
await addOAuthProvider(client, provider, cliName, json, !!args.force);
|
|
3544
4679
|
}
|
|
3545
|
-
const params = {
|
|
3546
|
-
query: args.query,
|
|
3547
|
-
topK
|
|
3548
|
-
};
|
|
3549
|
-
if (args.customer)
|
|
3550
|
-
params.customer = args.customer;
|
|
3551
|
-
if (args.after)
|
|
3552
|
-
params.occurredAfter = args.after;
|
|
3553
|
-
if (args.before)
|
|
3554
|
-
params.occurredBefore = args.before;
|
|
3555
|
-
return runTool(client, "outlit_search_customer_context", params, json);
|
|
3556
4680
|
}
|
|
3557
4681
|
});
|
|
3558
4682
|
});
|
|
3559
4683
|
|
|
3560
|
-
// src/commands/
|
|
3561
|
-
var
|
|
3562
|
-
__export(
|
|
3563
|
-
default: () =>
|
|
4684
|
+
// src/commands/integrations/remove.ts
|
|
4685
|
+
var exports_remove = {};
|
|
4686
|
+
__export(exports_remove, {
|
|
4687
|
+
default: () => remove_default
|
|
3564
4688
|
});
|
|
3565
|
-
|
|
3566
|
-
var
|
|
3567
|
-
|
|
4689
|
+
var remove_default;
|
|
4690
|
+
var init_remove = __esm(() => {
|
|
4691
|
+
init_dist3();
|
|
3568
4692
|
init_dist();
|
|
3569
4693
|
init_auth();
|
|
3570
4694
|
init_output2();
|
|
3571
4695
|
init_api();
|
|
3572
4696
|
init_output();
|
|
3573
|
-
|
|
4697
|
+
init_providers();
|
|
4698
|
+
init_spinner();
|
|
4699
|
+
init_tty();
|
|
4700
|
+
remove_default = defineCommand2({
|
|
3574
4701
|
meta: {
|
|
3575
|
-
name: "
|
|
4702
|
+
name: "remove",
|
|
3576
4703
|
description: [
|
|
3577
|
-
"
|
|
3578
|
-
"",
|
|
3579
|
-
"Provide the query as a positional argument or via --query-file.",
|
|
3580
|
-
"When both are provided, --query-file takes precedence.",
|
|
4704
|
+
"Disconnect an integration and remove all synced data.",
|
|
3581
4705
|
"",
|
|
3582
|
-
"
|
|
4706
|
+
"This permanently deletes all data that was synced from the integration",
|
|
4707
|
+
"(e.g., opportunities, contacts, messages). The action cannot be undone.",
|
|
3583
4708
|
"",
|
|
3584
4709
|
"Examples:",
|
|
3585
|
-
" outlit
|
|
3586
|
-
" outlit
|
|
3587
|
-
" outlit sql 'SELECT count(*) FROM events' --limit 1 --json",
|
|
4710
|
+
" outlit integrations remove posthog",
|
|
4711
|
+
" outlit integrations remove slack --yes # skip confirmation prompt",
|
|
3588
4712
|
"",
|
|
3589
4713
|
AGENT_JSON_HINT
|
|
3590
4714
|
].join(`
|
|
@@ -3593,74 +4717,84 @@ var init_sql = __esm(() => {
|
|
|
3593
4717
|
args: {
|
|
3594
4718
|
...authArgs,
|
|
3595
4719
|
...outputArgs,
|
|
3596
|
-
|
|
4720
|
+
provider: {
|
|
3597
4721
|
type: "positional",
|
|
3598
|
-
description: "
|
|
3599
|
-
required:
|
|
3600
|
-
},
|
|
3601
|
-
"query-file": {
|
|
3602
|
-
type: "string",
|
|
3603
|
-
description: "Path to a .sql file to read the query from (takes precedence over positional)"
|
|
4722
|
+
description: "Integration provider to disconnect",
|
|
4723
|
+
required: true
|
|
3604
4724
|
},
|
|
3605
|
-
|
|
3606
|
-
type: "
|
|
3607
|
-
description: "
|
|
3608
|
-
default: "1000"
|
|
4725
|
+
yes: {
|
|
4726
|
+
type: "boolean",
|
|
4727
|
+
description: "Skip confirmation prompt (required in non-interactive mode)."
|
|
3609
4728
|
}
|
|
3610
4729
|
},
|
|
3611
4730
|
async run({ args }) {
|
|
3612
4731
|
const json = !!args.json;
|
|
4732
|
+
const { provider, cliName } = resolveProviderOrExit(args.provider, json);
|
|
3613
4733
|
const client = await getClientOrExit(args["api-key"], json);
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
try {
|
|
3617
|
-
sql = readFileSync2(args["query-file"], "utf-8");
|
|
3618
|
-
} catch (err) {
|
|
4734
|
+
if (!args.yes) {
|
|
4735
|
+
if (!isInteractive() || json) {
|
|
3619
4736
|
return outputError({
|
|
3620
|
-
message: `
|
|
3621
|
-
code: "
|
|
4737
|
+
message: `Disconnecting ${provider.name} requires confirmation. Use --yes to confirm in non-interactive or JSON mode.`,
|
|
4738
|
+
code: "confirmation_required"
|
|
3622
4739
|
}, json);
|
|
3623
4740
|
}
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
4741
|
+
R2.warn(`This will disconnect ${provider.name} and delete all synced data.
|
|
4742
|
+
This action cannot be undone.`);
|
|
4743
|
+
const confirmed = await Re({ message: `Disconnect ${provider.name}?` });
|
|
4744
|
+
if (Ct(confirmed) || !confirmed) {
|
|
4745
|
+
Ne("Cancelled.");
|
|
4746
|
+
return;
|
|
4747
|
+
}
|
|
3628
4748
|
}
|
|
3629
|
-
const
|
|
3630
|
-
|
|
3631
|
-
|
|
4749
|
+
const spinner = createSpinner(`Disconnecting ${provider.name}...`);
|
|
4750
|
+
try {
|
|
4751
|
+
const result = await client.callTool("outlit_disconnect_integration", {
|
|
4752
|
+
provider: provider.id
|
|
4753
|
+
});
|
|
4754
|
+
if (result.success) {
|
|
4755
|
+
spinner.stop(`${provider.name} disconnected. All synced data has been removed.`);
|
|
4756
|
+
if (isJsonMode(json)) {
|
|
4757
|
+
return outputResult({ success: true, provider: cliName });
|
|
4758
|
+
}
|
|
4759
|
+
} else {
|
|
4760
|
+
spinner.fail(`Failed to disconnect ${provider.name}`);
|
|
4761
|
+
return outputError({
|
|
4762
|
+
message: result.message ?? `Failed to disconnect ${provider.name}`,
|
|
4763
|
+
code: "disconnect_failed"
|
|
4764
|
+
}, json);
|
|
4765
|
+
}
|
|
4766
|
+
} catch (err) {
|
|
4767
|
+
spinner.fail(`Failed to disconnect ${provider.name}`);
|
|
4768
|
+
return outputError({ message: errorMessage(err, "Disconnect request failed"), code: "api_error" }, json);
|
|
3632
4769
|
}
|
|
3633
|
-
return runTool(client, "outlit_query", { sql, limit }, json);
|
|
3634
4770
|
}
|
|
3635
4771
|
});
|
|
3636
4772
|
});
|
|
3637
4773
|
|
|
3638
|
-
// src/commands/
|
|
3639
|
-
var
|
|
3640
|
-
__export(
|
|
3641
|
-
default: () =>
|
|
4774
|
+
// src/commands/integrations/status.ts
|
|
4775
|
+
var exports_status2 = {};
|
|
4776
|
+
__export(exports_status2, {
|
|
4777
|
+
default: () => status_default2
|
|
3642
4778
|
});
|
|
3643
|
-
var
|
|
3644
|
-
var
|
|
4779
|
+
var status_default2;
|
|
4780
|
+
var init_status2 = __esm(() => {
|
|
3645
4781
|
init_dist();
|
|
3646
4782
|
init_auth();
|
|
3647
4783
|
init_output2();
|
|
3648
4784
|
init_api();
|
|
3649
|
-
|
|
4785
|
+
init_providers();
|
|
4786
|
+
status_default2 = defineCommand2({
|
|
3650
4787
|
meta: {
|
|
3651
|
-
name: "
|
|
4788
|
+
name: "status",
|
|
3652
4789
|
description: [
|
|
3653
|
-
"
|
|
3654
|
-
"",
|
|
3655
|
-
"Without a table name, returns the full schema for all tables.",
|
|
3656
|
-
"With a table name, returns detailed column info for that table.",
|
|
4790
|
+
"Show sync status for connected integrations.",
|
|
3657
4791
|
"",
|
|
3658
|
-
"
|
|
4792
|
+
"Without a provider name, shows a summary of all connected integrations.",
|
|
4793
|
+
"With a provider name, shows detailed per-model sync status.",
|
|
3659
4794
|
"",
|
|
3660
4795
|
"Examples:",
|
|
3661
|
-
" outlit
|
|
3662
|
-
" outlit
|
|
3663
|
-
" outlit schema customer_dimensions --json",
|
|
4796
|
+
" outlit integrations status # summary of all",
|
|
4797
|
+
" outlit integrations status stripe # detailed Stripe sync status",
|
|
3664
4798
|
"",
|
|
3665
4799
|
AGENT_JSON_HINT
|
|
3666
4800
|
].join(`
|
|
@@ -3669,19 +4803,78 @@ var init_schema = __esm(() => {
|
|
|
3669
4803
|
args: {
|
|
3670
4804
|
...authArgs,
|
|
3671
4805
|
...outputArgs,
|
|
3672
|
-
|
|
4806
|
+
provider: {
|
|
3673
4807
|
type: "positional",
|
|
3674
|
-
description: "
|
|
4808
|
+
description: "Provider name to show detailed status for (optional)",
|
|
3675
4809
|
required: false
|
|
3676
4810
|
}
|
|
3677
4811
|
},
|
|
3678
4812
|
async run({ args }) {
|
|
3679
4813
|
const json = !!args.json;
|
|
3680
4814
|
const client = await getClientOrExit(args["api-key"], json);
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
4815
|
+
if (args.provider) {
|
|
4816
|
+
const { provider } = resolveProviderOrExit(args.provider, json);
|
|
4817
|
+
return runTool(client, "outlit_integration_sync_status", { provider: provider.id }, json, {
|
|
4818
|
+
spinnerMessage: "Fetching sync status...",
|
|
4819
|
+
table: {
|
|
4820
|
+
columns: [
|
|
4821
|
+
{ header: "Model", key: "model" },
|
|
4822
|
+
{ header: "Status", key: "status" },
|
|
4823
|
+
{ header: "Records", key: "recordCount", format: formatNumber },
|
|
4824
|
+
{ header: "Last Synced", key: "lastSyncedAt", format: relativeDate }
|
|
4825
|
+
],
|
|
4826
|
+
itemsKey: "syncs"
|
|
4827
|
+
}
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4830
|
+
return runTool(client, "outlit_list_integrations", { connectedOnly: true }, json, {
|
|
4831
|
+
spinnerMessage: "Fetching integration status...",
|
|
4832
|
+
table: {
|
|
4833
|
+
columns: [
|
|
4834
|
+
{ header: "Name", key: "name", format: (v) => truncate(v, 24) },
|
|
4835
|
+
{ header: "Category", key: "category", format: capitalize },
|
|
4836
|
+
{ header: "Sync Status", key: "syncStatus" },
|
|
4837
|
+
{ header: "Last Synced", key: "lastDataReceivedAt", format: relativeDate }
|
|
4838
|
+
]
|
|
4839
|
+
}
|
|
4840
|
+
});
|
|
4841
|
+
}
|
|
4842
|
+
});
|
|
4843
|
+
});
|
|
4844
|
+
|
|
4845
|
+
// src/commands/integrations/index.ts
|
|
4846
|
+
var exports_integrations = {};
|
|
4847
|
+
__export(exports_integrations, {
|
|
4848
|
+
default: () => integrations_default
|
|
4849
|
+
});
|
|
4850
|
+
var integrations_default;
|
|
4851
|
+
var init_integrations = __esm(() => {
|
|
4852
|
+
init_dist();
|
|
4853
|
+
init_providers();
|
|
4854
|
+
integrations_default = defineCommand2({
|
|
4855
|
+
meta: {
|
|
4856
|
+
name: "integrations",
|
|
4857
|
+
description: [
|
|
4858
|
+
"Manage platform integrations (communication, analytics, billing, etc.).",
|
|
4859
|
+
"",
|
|
4860
|
+
"Connect third-party services like Slack, Stripe, and PostHog",
|
|
4861
|
+
"to sync data into your Outlit workspace.",
|
|
4862
|
+
"",
|
|
4863
|
+
"Commands:",
|
|
4864
|
+
" list List available integrations and connection status",
|
|
4865
|
+
" add <provider> Connect a new integration (opens browser for OAuth)",
|
|
4866
|
+
" remove <provider> Disconnect an integration and remove synced data",
|
|
4867
|
+
" status [provider] Show sync status for connected integrations",
|
|
4868
|
+
"",
|
|
4869
|
+
`Providers: ${PROVIDER_NAMES.join(", ")}`
|
|
4870
|
+
].join(`
|
|
4871
|
+
`)
|
|
4872
|
+
},
|
|
4873
|
+
subCommands: {
|
|
4874
|
+
list: () => Promise.resolve().then(() => (init_list4(), exports_list4)).then((m) => m.default),
|
|
4875
|
+
add: () => Promise.resolve().then(() => (init_add(), exports_add)).then((m) => m.default),
|
|
4876
|
+
remove: () => Promise.resolve().then(() => (init_remove(), exports_remove)).then((m) => m.default),
|
|
4877
|
+
status: () => Promise.resolve().then(() => (init_status2(), exports_status2)).then((m) => m.default)
|
|
3685
4878
|
}
|
|
3686
4879
|
});
|
|
3687
4880
|
});
|
|
@@ -3756,7 +4949,10 @@ function zshDescribe(items) {
|
|
|
3756
4949
|
function generateZsh() {
|
|
3757
4950
|
const topLevel = zshDescribe(COMMANDS);
|
|
3758
4951
|
const subCases = cmdsWithSubs.map((c) => {
|
|
3759
|
-
const items = [
|
|
4952
|
+
const items = [
|
|
4953
|
+
...c.subs.map((s) => ({ name: s.name, desc: s.desc })),
|
|
4954
|
+
...(c.flags ?? []).map((f) => ({ name: f.name, desc: f.desc }))
|
|
4955
|
+
];
|
|
3760
4956
|
return ` ${c.name})
|
|
3761
4957
|
completions=(${zshDescribe(items)})
|
|
3762
4958
|
_describe 'subcommand' completions
|
|
@@ -3887,7 +5083,6 @@ function generateFish() {
|
|
|
3887
5083
|
var JSON_F, API_KEY_F, LIMIT_F, CURSOR_F, COMMON, PAGINATED, ACTIVITY_ORDER, COMMANDS, cmdsWithSubs, leafCmds, SCRIPTS, completions_default;
|
|
3888
5084
|
var init_completions = __esm(() => {
|
|
3889
5085
|
init_dist();
|
|
3890
|
-
init_output2();
|
|
3891
5086
|
init_output();
|
|
3892
5087
|
JSON_F = { name: "--json", desc: "Force JSON output" };
|
|
3893
5088
|
API_KEY_F = { name: "--api-key", desc: "Outlit API key" };
|
|
@@ -3907,7 +5102,11 @@ var init_completions = __esm(() => {
|
|
|
3907
5102
|
desc: "Manage authentication",
|
|
3908
5103
|
subs: [
|
|
3909
5104
|
{ name: "signup", desc: "Create an Outlit account", flags: [JSON_F] },
|
|
3910
|
-
{
|
|
5105
|
+
{
|
|
5106
|
+
name: "login",
|
|
5107
|
+
desc: "Store API key",
|
|
5108
|
+
flags: [JSON_F, { name: "--key", desc: "API key to store" }]
|
|
5109
|
+
},
|
|
3911
5110
|
{ name: "logout", desc: "Remove stored key", flags: [JSON_F] },
|
|
3912
5111
|
{ name: "status", desc: "Check auth state", flags: [...COMMON] },
|
|
3913
5112
|
{ name: "whoami", desc: "Print masked key", flags: [...COMMON] }
|
|
@@ -3923,12 +5122,11 @@ var init_completions = __esm(() => {
|
|
|
3923
5122
|
flags: [
|
|
3924
5123
|
...PAGINATED,
|
|
3925
5124
|
...ACTIVITY_ORDER,
|
|
5125
|
+
{ name: "--trait", desc: "Filter by trait key=value pairs" },
|
|
3926
5126
|
{ name: "--billing-status", desc: "Filter by billing status" },
|
|
3927
5127
|
{ name: "--mrr-above", desc: "MRR above threshold (cents)" },
|
|
3928
5128
|
{ name: "--mrr-below", desc: "MRR below threshold (cents)" },
|
|
3929
|
-
{ name: "--search", desc: "Search name or domain" }
|
|
3930
|
-
{ name: "--status", desc: "Customer status filter" },
|
|
3931
|
-
{ name: "--type", desc: "Customer type filter" }
|
|
5129
|
+
{ name: "--search", desc: "Search name or domain" }
|
|
3932
5130
|
]
|
|
3933
5131
|
},
|
|
3934
5132
|
{
|
|
@@ -3964,6 +5162,7 @@ var init_completions = __esm(() => {
|
|
|
3964
5162
|
flags: [
|
|
3965
5163
|
...PAGINATED,
|
|
3966
5164
|
...ACTIVITY_ORDER,
|
|
5165
|
+
{ name: "--trait", desc: "Filter by trait key=value pairs" },
|
|
3967
5166
|
{ name: "--journey-stage", desc: "Filter by journey stage" },
|
|
3968
5167
|
{ name: "--customer-id", desc: "Filter by customer UUID" },
|
|
3969
5168
|
{ name: "--search", desc: "Search name or email" }
|
|
@@ -3974,7 +5173,43 @@ var init_completions = __esm(() => {
|
|
|
3974
5173
|
{
|
|
3975
5174
|
name: "facts",
|
|
3976
5175
|
desc: "Get customer facts",
|
|
3977
|
-
|
|
5176
|
+
subs: [
|
|
5177
|
+
{
|
|
5178
|
+
name: "list",
|
|
5179
|
+
desc: "List customer facts",
|
|
5180
|
+
flags: [
|
|
5181
|
+
...PAGINATED,
|
|
5182
|
+
{ name: "--status", desc: "Filter by fact status" },
|
|
5183
|
+
{ name: "--source-types", desc: "Filter by source types" },
|
|
5184
|
+
{ name: "--after", desc: "Facts after date (ISO 8601)" },
|
|
5185
|
+
{ name: "--before", desc: "Facts before date (ISO 8601)" }
|
|
5186
|
+
]
|
|
5187
|
+
},
|
|
5188
|
+
{
|
|
5189
|
+
name: "get",
|
|
5190
|
+
desc: "Get a single fact by ID",
|
|
5191
|
+
flags: [
|
|
5192
|
+
...COMMON,
|
|
5193
|
+
{ name: "--fact-id", desc: "Fact ID to fetch" },
|
|
5194
|
+
{ name: "--include", desc: "Best-effort expansions" }
|
|
5195
|
+
]
|
|
5196
|
+
}
|
|
5197
|
+
]
|
|
5198
|
+
},
|
|
5199
|
+
{
|
|
5200
|
+
name: "sources",
|
|
5201
|
+
desc: "Get a concrete source by type and id",
|
|
5202
|
+
subs: [
|
|
5203
|
+
{
|
|
5204
|
+
name: "get",
|
|
5205
|
+
desc: "Get one exact source record",
|
|
5206
|
+
flags: [
|
|
5207
|
+
...COMMON,
|
|
5208
|
+
{ name: "--source-type", desc: "Canonical source type" },
|
|
5209
|
+
{ name: "--source-id", desc: "Exact source ID" }
|
|
5210
|
+
]
|
|
5211
|
+
}
|
|
5212
|
+
]
|
|
3978
5213
|
},
|
|
3979
5214
|
{
|
|
3980
5215
|
name: "search",
|
|
@@ -3984,7 +5219,8 @@ var init_completions = __esm(() => {
|
|
|
3984
5219
|
{ name: "--customer", desc: "Scope to customer (UUID or domain)" },
|
|
3985
5220
|
{ name: "--top-k", desc: "Max results" },
|
|
3986
5221
|
{ name: "--after", desc: "Events after date (ISO 8601)" },
|
|
3987
|
-
{ name: "--before", desc: "Events before date (ISO 8601)" }
|
|
5222
|
+
{ name: "--before", desc: "Events before date (ISO 8601)" },
|
|
5223
|
+
{ name: "--source-types", desc: "Broad source type filter" }
|
|
3988
5224
|
]
|
|
3989
5225
|
},
|
|
3990
5226
|
{
|
|
@@ -3997,21 +5233,46 @@ var init_completions = __esm(() => {
|
|
|
3997
5233
|
]
|
|
3998
5234
|
},
|
|
3999
5235
|
{ name: "schema", desc: "Discover table schemas", flags: [...COMMON] },
|
|
5236
|
+
{
|
|
5237
|
+
name: "integrations",
|
|
5238
|
+
desc: "Manage platform integrations",
|
|
5239
|
+
subs: [
|
|
5240
|
+
{ name: "list", desc: "List integrations and status", flags: [...COMMON] },
|
|
5241
|
+
{
|
|
5242
|
+
name: "add",
|
|
5243
|
+
desc: "Connect an integration",
|
|
5244
|
+
flags: [
|
|
5245
|
+
...COMMON,
|
|
5246
|
+
{ name: "--config", desc: "JSON config for API-key providers" },
|
|
5247
|
+
{ name: "--force", desc: "Reconnect if already connected" }
|
|
5248
|
+
]
|
|
5249
|
+
},
|
|
5250
|
+
{
|
|
5251
|
+
name: "remove",
|
|
5252
|
+
desc: "Disconnect an integration",
|
|
5253
|
+
flags: [...COMMON, { name: "--yes", desc: "Skip confirmation" }]
|
|
5254
|
+
},
|
|
5255
|
+
{ name: "status", desc: "Show sync status", flags: [...COMMON] }
|
|
5256
|
+
]
|
|
5257
|
+
},
|
|
4000
5258
|
{
|
|
4001
5259
|
name: "setup",
|
|
4002
|
-
desc: "
|
|
4003
|
-
flags: [
|
|
5260
|
+
desc: "Install Outlit skills for coding agents",
|
|
5261
|
+
flags: [JSON_F, { name: "--yes", desc: "Skip prompts" }],
|
|
4004
5262
|
subs: [
|
|
4005
|
-
{ name: "
|
|
4006
|
-
{ name: "
|
|
4007
|
-
{ name: "
|
|
4008
|
-
{ name: "
|
|
4009
|
-
{ name: "
|
|
4010
|
-
{ name: "
|
|
5263
|
+
{ name: "claude-code", desc: "Install the Outlit skill for Claude Code", flags: [JSON_F] },
|
|
5264
|
+
{ name: "codex", desc: "Install the Outlit skill for Codex", flags: [JSON_F] },
|
|
5265
|
+
{ name: "gemini", desc: "Install the Outlit skill for Gemini CLI", flags: [JSON_F] },
|
|
5266
|
+
{ name: "droid", desc: "Install the Outlit skill for Droid", flags: [JSON_F] },
|
|
5267
|
+
{ name: "opencode", desc: "Install the Outlit skill for OpenCode", flags: [JSON_F] },
|
|
5268
|
+
{ name: "pi", desc: "Install the Outlit skill for Pi", flags: [JSON_F] },
|
|
5269
|
+
{ name: "openclaw", desc: "Install the Outlit skill for OpenClaw", flags: [JSON_F] },
|
|
5270
|
+
{ name: "skills", desc: "Launch the interactive Outlit skills installer", flags: [JSON_F] }
|
|
4011
5271
|
]
|
|
4012
5272
|
},
|
|
5273
|
+
{ name: "upgrade", desc: "Upgrade the CLI", flags: [] },
|
|
4013
5274
|
{ name: "doctor", desc: "Diagnose environment", flags: [...COMMON] },
|
|
4014
|
-
{ name: "completions", desc: "Generate shell completions", flags: [
|
|
5275
|
+
{ name: "completions", desc: "Generate shell completions", flags: [] }
|
|
4015
5276
|
];
|
|
4016
5277
|
cmdsWithSubs = COMMANDS.filter((c) => c.subs?.length);
|
|
4017
5278
|
leafCmds = COMMANDS.filter((c) => !c.subs?.length && c.flags?.length);
|
|
@@ -4038,7 +5299,6 @@ var init_completions = __esm(() => {
|
|
|
4038
5299
|
`)
|
|
4039
5300
|
},
|
|
4040
5301
|
args: {
|
|
4041
|
-
...outputArgs,
|
|
4042
5302
|
shell: {
|
|
4043
5303
|
type: "positional",
|
|
4044
5304
|
description: "Shell to generate completions for (bash, zsh, fish)",
|
|
@@ -4046,14 +5306,13 @@ var init_completions = __esm(() => {
|
|
|
4046
5306
|
}
|
|
4047
5307
|
},
|
|
4048
5308
|
run({ args }) {
|
|
4049
|
-
const json = !!args.json;
|
|
4050
5309
|
const shell = args.shell;
|
|
4051
5310
|
const generate = SCRIPTS[shell];
|
|
4052
5311
|
if (!generate) {
|
|
4053
5312
|
return outputError({
|
|
4054
5313
|
message: `Unknown shell: ${shell}. Supported: bash, zsh, fish`,
|
|
4055
5314
|
code: "unknown_shell"
|
|
4056
|
-
},
|
|
5315
|
+
}, false);
|
|
4057
5316
|
}
|
|
4058
5317
|
process.stdout.write(generate());
|
|
4059
5318
|
}
|
|
@@ -4064,13 +5323,13 @@ var init_completions = __esm(() => {
|
|
|
4064
5323
|
var exports_setup = {};
|
|
4065
5324
|
__export(exports_setup, {
|
|
4066
5325
|
isCommandAvailable: () => isCommandAvailable2,
|
|
4067
|
-
detectAgents: () =>
|
|
5326
|
+
detectAgents: () => detectAgents2,
|
|
4068
5327
|
default: () => setup_default2
|
|
4069
5328
|
});
|
|
4070
5329
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
4071
|
-
import { existsSync as
|
|
5330
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
4072
5331
|
import { homedir as homedir6 } from "node:os";
|
|
4073
|
-
import { join as
|
|
5332
|
+
import { join as join7 } from "node:path";
|
|
4074
5333
|
function isCommandAvailable2(cmd) {
|
|
4075
5334
|
try {
|
|
4076
5335
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -4080,126 +5339,133 @@ function isCommandAvailable2(cmd) {
|
|
|
4080
5339
|
return false;
|
|
4081
5340
|
}
|
|
4082
5341
|
}
|
|
4083
|
-
function
|
|
4084
|
-
|
|
5342
|
+
function getHomeDir3() {
|
|
5343
|
+
return process.env.HOME?.trim() || homedir6();
|
|
5344
|
+
}
|
|
5345
|
+
function detectAgents2() {
|
|
5346
|
+
const home = getHomeDir3();
|
|
5347
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || join7(home, ".config");
|
|
4085
5348
|
const detected = [];
|
|
4086
|
-
if (existsSync5(join8(home, ".cursor")))
|
|
4087
|
-
detected.push("cursor");
|
|
4088
5349
|
if (isCommandAvailable2("claude"))
|
|
4089
5350
|
detected.push("claude-code");
|
|
4090
|
-
if (
|
|
4091
|
-
detected.push("
|
|
4092
|
-
if (isCommandAvailable2("code") || existsSync5(join8(process.cwd(), ".vscode")))
|
|
4093
|
-
detected.push("vscode");
|
|
5351
|
+
if (isCommandAvailable2("codex"))
|
|
5352
|
+
detected.push("codex");
|
|
4094
5353
|
if (isCommandAvailable2("gemini"))
|
|
4095
5354
|
detected.push("gemini");
|
|
4096
|
-
if (
|
|
5355
|
+
if (existsSync6(join7(home, ".factory")))
|
|
5356
|
+
detected.push("droid");
|
|
5357
|
+
if (existsSync6(join7(configHome, "opencode")))
|
|
5358
|
+
detected.push("opencode");
|
|
5359
|
+
if (existsSync6(join7(home, ".pi", "agent")))
|
|
5360
|
+
detected.push("pi");
|
|
5361
|
+
if (existsSync6(join7(home, ".openclaw")) || existsSync6(join7(home, ".clawdbot")) || existsSync6(join7(home, ".moltbot"))) {
|
|
4097
5362
|
detected.push("openclaw");
|
|
5363
|
+
}
|
|
4098
5364
|
return detected;
|
|
4099
5365
|
}
|
|
4100
|
-
var agentLabels2,
|
|
4101
|
-
var
|
|
5366
|
+
var agentLabels2, setupSubcommandNames2, setup_default2;
|
|
5367
|
+
var init_setup2 = __esm(() => {
|
|
4102
5368
|
init_dist();
|
|
4103
|
-
init_auth();
|
|
4104
5369
|
init_output2();
|
|
4105
5370
|
init_config();
|
|
4106
5371
|
init_output();
|
|
4107
|
-
init_claude_code();
|
|
4108
|
-
init_claude_desktop();
|
|
4109
|
-
init_cursor();
|
|
4110
|
-
init_gemini();
|
|
4111
|
-
init_openclaw();
|
|
4112
5372
|
init_skills();
|
|
4113
|
-
init_vscode();
|
|
4114
5373
|
agentLabels2 = {
|
|
4115
|
-
cursor: { label: "Cursor", hint: "~/.cursor/" },
|
|
4116
5374
|
"claude-code": { label: "Claude Code", hint: "claude CLI found" },
|
|
4117
|
-
|
|
4118
|
-
vscode: { label: "VS Code", hint: "code CLI or .vscode/ found" },
|
|
5375
|
+
codex: { label: "Codex", hint: "codex CLI found" },
|
|
4119
5376
|
gemini: { label: "Gemini CLI", hint: "gemini CLI found" },
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
"claude-code": configureSafe,
|
|
4125
|
-
"claude-desktop": configureSafe2,
|
|
4126
|
-
vscode: configureSafe6,
|
|
4127
|
-
gemini: configureSafe4,
|
|
4128
|
-
openclaw: configureSafe5
|
|
5377
|
+
droid: { label: "Droid", hint: ".factory config found" },
|
|
5378
|
+
opencode: { label: "OpenCode", hint: "opencode config found" },
|
|
5379
|
+
pi: { label: "Pi", hint: ".pi/agent config found" },
|
|
5380
|
+
openclaw: { label: "OpenClaw", hint: "OpenClaw config found" }
|
|
4129
5381
|
};
|
|
5382
|
+
setupSubcommandNames2 = new Set([
|
|
5383
|
+
"claude-code",
|
|
5384
|
+
"codex",
|
|
5385
|
+
"gemini",
|
|
5386
|
+
"droid",
|
|
5387
|
+
"opencode",
|
|
5388
|
+
"pi",
|
|
5389
|
+
"openclaw",
|
|
5390
|
+
"skills"
|
|
5391
|
+
]);
|
|
4130
5392
|
setup_default2 = defineCommand2({
|
|
4131
5393
|
meta: {
|
|
4132
5394
|
name: "setup",
|
|
4133
5395
|
description: [
|
|
4134
|
-
"
|
|
5396
|
+
"Install the Outlit skill for coding agents.",
|
|
4135
5397
|
"",
|
|
4136
|
-
"Without a subcommand, auto-detects
|
|
4137
|
-
"Subcommands:
|
|
5398
|
+
"Without a subcommand, auto-detects supported coding agents and installs `outlit` for all of them.",
|
|
5399
|
+
"Subcommands: claude-code, codex, gemini, droid, opencode, pi, openclaw, skills"
|
|
4138
5400
|
].join(`
|
|
4139
5401
|
`)
|
|
4140
5402
|
},
|
|
4141
5403
|
args: {
|
|
4142
|
-
...authArgs,
|
|
4143
5404
|
...outputArgs,
|
|
4144
5405
|
yes: {
|
|
4145
5406
|
type: "boolean",
|
|
4146
|
-
description: "
|
|
5407
|
+
description: "Install for all detected coding agents without prompting."
|
|
4147
5408
|
}
|
|
4148
5409
|
},
|
|
4149
5410
|
subCommands: {
|
|
4150
|
-
cursor: () => Promise.resolve().then(() => (init_cursor(), exports_cursor)).then((m) => m.default),
|
|
4151
5411
|
"claude-code": () => Promise.resolve().then(() => (init_claude_code(), exports_claude_code)).then((m) => m.default),
|
|
4152
|
-
|
|
4153
|
-
vscode: () => Promise.resolve().then(() => (init_vscode(), exports_vscode)).then((m) => m.default),
|
|
5412
|
+
codex: () => Promise.resolve().then(() => (init_codex(), exports_codex)).then((m) => m.default),
|
|
4154
5413
|
gemini: () => Promise.resolve().then(() => (init_gemini(), exports_gemini)).then((m) => m.default),
|
|
5414
|
+
droid: () => Promise.resolve().then(() => (init_droid(), exports_droid)).then((m) => m.default),
|
|
5415
|
+
opencode: () => Promise.resolve().then(() => (init_opencode(), exports_opencode)).then((m) => m.default),
|
|
5416
|
+
pi: () => Promise.resolve().then(() => (init_pi(), exports_pi)).then((m) => m.default),
|
|
4155
5417
|
openclaw: () => Promise.resolve().then(() => (init_openclaw(), exports_openclaw)).then((m) => m.default),
|
|
4156
5418
|
skills: () => Promise.resolve().then(() => (init_skills(), exports_skills)).then((m) => m.default)
|
|
4157
5419
|
},
|
|
4158
|
-
async run({ args }) {
|
|
5420
|
+
async run({ args, rawArgs }) {
|
|
5421
|
+
const setupRawArgs = rawArgs ?? [];
|
|
5422
|
+
const subcommandName = setupRawArgs.find((arg) => !arg.startsWith("-"));
|
|
5423
|
+
if (subcommandName && setupSubcommandNames2.has(subcommandName)) {
|
|
5424
|
+
return;
|
|
5425
|
+
}
|
|
4159
5426
|
const json = !!args.json;
|
|
4160
|
-
const
|
|
4161
|
-
const detected = detectAgents3();
|
|
5427
|
+
const detected = detectAgents2();
|
|
4162
5428
|
if (detected.length === 0) {
|
|
4163
5429
|
if (isJsonMode(json)) {
|
|
4164
|
-
return outputResult({ detected: [], configured: [], failed: [],
|
|
5430
|
+
return outputResult({ detected: [], configured: [], failed: [], runner: null });
|
|
4165
5431
|
}
|
|
4166
|
-
console.log("No supported
|
|
5432
|
+
console.log("No supported coding agents detected.");
|
|
4167
5433
|
return;
|
|
4168
5434
|
}
|
|
4169
5435
|
if (!isJsonMode(json) && !args.yes) {
|
|
4170
|
-
console.log("Detected agents:");
|
|
5436
|
+
console.log("Detected coding agents:");
|
|
4171
5437
|
for (const agentId of detected) {
|
|
4172
5438
|
const { label, hint } = agentLabels2[agentId];
|
|
4173
5439
|
console.log(` ${TICK2} ${label.padEnd(14)} -- ${hint}`);
|
|
4174
5440
|
}
|
|
4175
5441
|
console.log(`
|
|
4176
|
-
|
|
4177
|
-
}
|
|
4178
|
-
const configured = [];
|
|
4179
|
-
const failed = [];
|
|
4180
|
-
for (const agentId of detected) {
|
|
4181
|
-
const ok = configurators2[agentId](credential.key, json);
|
|
4182
|
-
if (ok) {
|
|
4183
|
-
configured.push(agentId);
|
|
4184
|
-
} else {
|
|
4185
|
-
failed.push(agentId);
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
const skills = runSkillsInstall(json, false);
|
|
4189
|
-
if (!isJsonMode(json) && !skills.success) {
|
|
4190
|
-
console.log(`
|
|
4191
|
-
! Agent skills installation failed: ${skills.error ?? "unknown error"}`);
|
|
4192
|
-
console.log(" Run `outlit setup skills` to retry.");
|
|
5442
|
+
Installing Outlit skill...`);
|
|
4193
5443
|
}
|
|
5444
|
+
const install = runSkillsInstall({
|
|
5445
|
+
json,
|
|
5446
|
+
exitOnError: false,
|
|
5447
|
+
agents: detected.map(getSkillAgentId),
|
|
5448
|
+
skillNames: ["outlit"],
|
|
5449
|
+
autoConfirm: true
|
|
5450
|
+
});
|
|
5451
|
+
const configured = install.success ? detected : [];
|
|
5452
|
+
const failed = install.success ? [] : detected;
|
|
4194
5453
|
if (isJsonMode(json)) {
|
|
4195
|
-
return outputResult({
|
|
5454
|
+
return outputResult({
|
|
5455
|
+
detected,
|
|
5456
|
+
configured,
|
|
5457
|
+
failed,
|
|
5458
|
+
runner: install.runner ?? null
|
|
5459
|
+
});
|
|
4196
5460
|
}
|
|
4197
|
-
if (
|
|
5461
|
+
if (!install.success) {
|
|
4198
5462
|
console.log(`
|
|
4199
|
-
|
|
5463
|
+
! Outlit skill install failed: ${install.error ?? "unknown error"}`);
|
|
5464
|
+
console.log(" Run `outlit setup skills` to retry manually.");
|
|
5465
|
+
return;
|
|
4200
5466
|
}
|
|
4201
5467
|
console.log(`
|
|
4202
|
-
Done. ${configured.length}
|
|
5468
|
+
Done. Installed Outlit for ${configured.length} coding agent(s).`);
|
|
4203
5469
|
}
|
|
4204
5470
|
});
|
|
4205
5471
|
});
|
|
@@ -4556,11 +5822,226 @@ init_tty();
|
|
|
4556
5822
|
var CLI_VERSION = package_default.version;
|
|
4557
5823
|
var TICK = `\x1B[32m${isUnicodeSupported ? String.fromCodePoint(10003) : String.fromCodePoint(8730)}\x1B[0m`;
|
|
4558
5824
|
|
|
5825
|
+
// src/lib/update.ts
|
|
5826
|
+
init_config();
|
|
5827
|
+
init_tty();
|
|
5828
|
+
import { execFileSync as execFileSync2, spawn, spawnSync } from "node:child_process";
|
|
5829
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5830
|
+
import { homedir as homedir2 } from "node:os";
|
|
5831
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
5832
|
+
var UPDATE_CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
5833
|
+
var PACKAGE_NAME = "@outlit/cli";
|
|
5834
|
+
var LATEST_VERSION_URL = "https://registry.npmjs.org/@outlit%2Fcli/latest";
|
|
5835
|
+
var INTERNAL_UPDATE_FLAG = "--internal-update-check";
|
|
5836
|
+
function getUpdateCachePath() {
|
|
5837
|
+
return join2(getConfigDir(), "update-check.json");
|
|
5838
|
+
}
|
|
5839
|
+
function readCachedUpdateState() {
|
|
5840
|
+
const cachePath = getUpdateCachePath();
|
|
5841
|
+
if (!existsSync2(cachePath))
|
|
5842
|
+
return null;
|
|
5843
|
+
try {
|
|
5844
|
+
return JSON.parse(readFileSync2(cachePath, "utf8"));
|
|
5845
|
+
} catch {
|
|
5846
|
+
return null;
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
function writeCachedUpdateState(state) {
|
|
5850
|
+
const cachePath = getUpdateCachePath();
|
|
5851
|
+
mkdirSync2(dirname2(cachePath), { recursive: true });
|
|
5852
|
+
writeFileSync2(cachePath, `${JSON.stringify(state, null, 2)}
|
|
5853
|
+
`);
|
|
5854
|
+
}
|
|
5855
|
+
function isUpdateCheckDue(state) {
|
|
5856
|
+
if (!state?.lastCheckedAt)
|
|
5857
|
+
return true;
|
|
5858
|
+
return Date.now() - state.lastCheckedAt >= UPDATE_CHECK_INTERVAL_MS;
|
|
5859
|
+
}
|
|
5860
|
+
function compareVersions(a, b) {
|
|
5861
|
+
const aParts = a.split("-")[0]?.split(".").map((part) => Number.parseInt(part, 10) || 0) ?? [];
|
|
5862
|
+
const bParts = b.split("-")[0]?.split(".").map((part) => Number.parseInt(part, 10) || 0) ?? [];
|
|
5863
|
+
const maxLength = Math.max(aParts.length, bParts.length);
|
|
5864
|
+
for (let index = 0;index < maxLength; index++) {
|
|
5865
|
+
const left = aParts[index] ?? 0;
|
|
5866
|
+
const right = bParts[index] ?? 0;
|
|
5867
|
+
if (left > right)
|
|
5868
|
+
return 1;
|
|
5869
|
+
if (left < right)
|
|
5870
|
+
return -1;
|
|
5871
|
+
}
|
|
5872
|
+
return 0;
|
|
5873
|
+
}
|
|
5874
|
+
function inferInstallerFromUserAgent(agent) {
|
|
5875
|
+
if (agent.startsWith("bun/"))
|
|
5876
|
+
return "bun";
|
|
5877
|
+
if (agent.startsWith("npm/"))
|
|
5878
|
+
return "npm";
|
|
5879
|
+
if (agent.startsWith("pnpm/"))
|
|
5880
|
+
return "pnpm";
|
|
5881
|
+
if (agent.startsWith("yarn/"))
|
|
5882
|
+
return "yarn";
|
|
5883
|
+
return null;
|
|
5884
|
+
}
|
|
5885
|
+
function isUnderPath(path, parent) {
|
|
5886
|
+
const normalizedPath = normalizeInstallerPath(path);
|
|
5887
|
+
const normalizedParent = normalizeInstallerPath(parent);
|
|
5888
|
+
return normalizedPath === normalizedParent || normalizedPath.startsWith(`${normalizedParent}/`);
|
|
5889
|
+
}
|
|
5890
|
+
function normalizeInstallerPath(path) {
|
|
5891
|
+
return path.replace(/^\/private\/tmp\//, "/tmp/");
|
|
5892
|
+
}
|
|
5893
|
+
function inferInstallerFromInstallation(opts) {
|
|
5894
|
+
const candidatePaths = [opts.argv1, opts.realExecPath].filter((value) => !!value);
|
|
5895
|
+
if (opts.npmGlobalPrefix) {
|
|
5896
|
+
const npmPackageRoots = [
|
|
5897
|
+
join2(opts.npmGlobalPrefix, "node_modules", PACKAGE_NAME),
|
|
5898
|
+
join2(opts.npmGlobalPrefix, "lib", "node_modules", PACKAGE_NAME)
|
|
5899
|
+
];
|
|
5900
|
+
if (candidatePaths.some((path) => npmPackageRoots.some((root) => isUnderPath(path, root)))) {
|
|
5901
|
+
return "npm";
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
if (opts.bunGlobalBin) {
|
|
5905
|
+
const bunGlobalBin = opts.bunGlobalBin;
|
|
5906
|
+
if (candidatePaths.some((path) => isUnderPath(path, bunGlobalBin))) {
|
|
5907
|
+
return "bun";
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
return null;
|
|
5911
|
+
}
|
|
5912
|
+
function readCommandOutput(command, args) {
|
|
5913
|
+
try {
|
|
5914
|
+
return execFileSync2(command, args, {
|
|
5915
|
+
encoding: "utf8",
|
|
5916
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5917
|
+
}).trim();
|
|
5918
|
+
} catch {
|
|
5919
|
+
return null;
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5922
|
+
function inferInstaller() {
|
|
5923
|
+
const fromAgent = inferInstallerFromUserAgent(process.env.npm_config_user_agent ?? "");
|
|
5924
|
+
if (fromAgent)
|
|
5925
|
+
return fromAgent;
|
|
5926
|
+
const argv1 = process.argv[1];
|
|
5927
|
+
const realExecPath = argv1 ? readCommandOutput("realpath", [argv1]) ?? safeRealPath(argv1) : null;
|
|
5928
|
+
const npmGlobalPrefix = process.env.npm_config_prefix ?? readCommandOutput("npm", ["prefix", "-g"]);
|
|
5929
|
+
const bunGlobalBin = process.env.BUN_INSTALL ? join2(process.env.BUN_INSTALL, "bin") : readCommandOutput("bun", ["pm", "bin", "-g"]) ?? join2(homedir2(), ".bun", "bin");
|
|
5930
|
+
return inferInstallerFromInstallation({
|
|
5931
|
+
argv1,
|
|
5932
|
+
realExecPath,
|
|
5933
|
+
npmGlobalPrefix,
|
|
5934
|
+
bunGlobalBin
|
|
5935
|
+
});
|
|
5936
|
+
}
|
|
5937
|
+
function safeRealPath(filePath) {
|
|
5938
|
+
try {
|
|
5939
|
+
return realpathSync(filePath);
|
|
5940
|
+
} catch {
|
|
5941
|
+
return null;
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
function formatUpdateCommand(installer = inferInstaller()) {
|
|
5945
|
+
switch (installer) {
|
|
5946
|
+
case "bun":
|
|
5947
|
+
return "bun add -g @outlit/cli";
|
|
5948
|
+
case "npm":
|
|
5949
|
+
return "npm install -g @outlit/cli";
|
|
5950
|
+
case "pnpm":
|
|
5951
|
+
return "pnpm add -g @outlit/cli";
|
|
5952
|
+
case "yarn":
|
|
5953
|
+
return "yarn global add @outlit/cli";
|
|
5954
|
+
default:
|
|
5955
|
+
return `update ${PACKAGE_NAME} with your package manager`;
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
function getCachedUpdateNotice(state = readCachedUpdateState()) {
|
|
5959
|
+
if (!state?.latestVersion)
|
|
5960
|
+
return null;
|
|
5961
|
+
if (compareVersions(CLI_VERSION2, state.latestVersion) >= 0)
|
|
5962
|
+
return null;
|
|
5963
|
+
return {
|
|
5964
|
+
currentVersion: CLI_VERSION2,
|
|
5965
|
+
latestVersion: state.latestVersion,
|
|
5966
|
+
command: formatUpdateCommand(state.installer)
|
|
5967
|
+
};
|
|
5968
|
+
}
|
|
5969
|
+
function shouldCheckForUpdates() {
|
|
5970
|
+
if (process.env.OUTLIT_NO_UPDATE_NOTIFIER)
|
|
5971
|
+
return false;
|
|
5972
|
+
return isInteractive();
|
|
5973
|
+
}
|
|
5974
|
+
function shouldShowUpdateNotice(argv = process.argv) {
|
|
5975
|
+
return shouldCheckForUpdates() && !argv.includes("--json") && !argv.includes(INTERNAL_UPDATE_FLAG);
|
|
5976
|
+
}
|
|
5977
|
+
function printCachedUpdateNotice(argv = process.argv, notify = console.error) {
|
|
5978
|
+
if (!shouldShowUpdateNotice(argv))
|
|
5979
|
+
return false;
|
|
5980
|
+
const notice = getCachedUpdateNotice();
|
|
5981
|
+
if (!notice)
|
|
5982
|
+
return false;
|
|
5983
|
+
notify(`Outlit CLI update available: ${notice.currentVersion} -> ${notice.latestVersion}
|
|
5984
|
+
Update with: ${notice.command}`);
|
|
5985
|
+
return true;
|
|
5986
|
+
}
|
|
5987
|
+
function scheduleBackgroundUpdateCheck(argv = process.argv, spawnProcess = spawn) {
|
|
5988
|
+
if (!shouldShowUpdateNotice(argv))
|
|
5989
|
+
return false;
|
|
5990
|
+
if (!isUpdateCheckDue(readCachedUpdateState()))
|
|
5991
|
+
return false;
|
|
5992
|
+
const runtimePath = argv[0];
|
|
5993
|
+
const scriptPath = argv[1];
|
|
5994
|
+
if (!runtimePath || !scriptPath)
|
|
5995
|
+
return false;
|
|
5996
|
+
const child = spawnProcess(runtimePath, [scriptPath, INTERNAL_UPDATE_FLAG], {
|
|
5997
|
+
detached: true,
|
|
5998
|
+
stdio: "ignore"
|
|
5999
|
+
});
|
|
6000
|
+
child.unref?.();
|
|
6001
|
+
return true;
|
|
6002
|
+
}
|
|
6003
|
+
function initializeUpdateNotifier(opts) {
|
|
6004
|
+
const argv = opts?.argv ?? process.argv;
|
|
6005
|
+
printCachedUpdateNotice(argv, opts?.notify);
|
|
6006
|
+
scheduleBackgroundUpdateCheck(argv, opts?.spawn);
|
|
6007
|
+
}
|
|
6008
|
+
async function fetchLatestCliVersion() {
|
|
6009
|
+
const response = await fetch(LATEST_VERSION_URL, { signal: AbortSignal.timeout(5000) });
|
|
6010
|
+
if (!response.ok)
|
|
6011
|
+
throw new Error("registry unavailable");
|
|
6012
|
+
const data = await response.json();
|
|
6013
|
+
if (!data.version)
|
|
6014
|
+
throw new Error("registry returned no version");
|
|
6015
|
+
return data.version;
|
|
6016
|
+
}
|
|
6017
|
+
async function runInternalUpdateCheck(opts) {
|
|
6018
|
+
const fetchLatestVersion = opts?.fetchLatestVersion ?? fetchLatestCliVersion;
|
|
6019
|
+
const installer = opts?.installer ?? inferInstaller();
|
|
6020
|
+
try {
|
|
6021
|
+
const latestVersion = await fetchLatestVersion();
|
|
6022
|
+
writeCachedUpdateState({
|
|
6023
|
+
lastCheckedAt: Date.now(),
|
|
6024
|
+
latestVersion,
|
|
6025
|
+
...installer ? { installer } : {}
|
|
6026
|
+
});
|
|
6027
|
+
} catch {
|
|
6028
|
+
writeCachedUpdateState({
|
|
6029
|
+
lastCheckedAt: Date.now(),
|
|
6030
|
+
...installer ? { installer } : {}
|
|
6031
|
+
});
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
|
|
4559
6035
|
// src/cli.ts
|
|
4560
6036
|
if (process.argv.includes("-v")) {
|
|
4561
6037
|
console.log(CLI_VERSION);
|
|
4562
6038
|
process.exit(0);
|
|
4563
6039
|
}
|
|
6040
|
+
if (process.argv.includes(INTERNAL_UPDATE_FLAG)) {
|
|
6041
|
+
await runInternalUpdateCheck();
|
|
6042
|
+
process.exit(0);
|
|
6043
|
+
}
|
|
6044
|
+
initializeUpdateNotifier();
|
|
4564
6045
|
var main = defineCommand({
|
|
4565
6046
|
meta: {
|
|
4566
6047
|
name: "outlit",
|
|
@@ -4572,8 +6053,10 @@ Usage examples:
|
|
|
4572
6053
|
outlit customers get acme.com --include users,revenue
|
|
4573
6054
|
outlit customers timeline acme.com --timeframe 90d
|
|
4574
6055
|
outlit users list --journey-stage CHAMPION
|
|
4575
|
-
outlit facts acme.com --
|
|
4576
|
-
outlit
|
|
6056
|
+
outlit facts list acme.com --source-types CALL --after 2025-01-01T00:00:00Z
|
|
6057
|
+
outlit facts get --fact-id fact_123 --include evidence
|
|
6058
|
+
outlit sources get --source-type CALL --source-id call_123
|
|
6059
|
+
outlit search 'pricing objections last quarter' --source-types CALL,EMAIL
|
|
4577
6060
|
outlit sql 'SELECT * FROM events LIMIT 10'
|
|
4578
6061
|
outlit schema events
|
|
4579
6062
|
outlit doctor --json
|
|
@@ -4585,12 +6068,15 @@ For AI agents: commands auto-output JSON when stdout is piped. No --json flag ne
|
|
|
4585
6068
|
customers: () => Promise.resolve().then(() => (init_customers(), exports_customers)).then((m) => m.default),
|
|
4586
6069
|
users: () => Promise.resolve().then(() => (init_users(), exports_users)).then((m) => m.default),
|
|
4587
6070
|
doctor: () => Promise.resolve().then(() => (init_doctor(), exports_doctor)).then((m) => m.default),
|
|
6071
|
+
upgrade: () => Promise.resolve().then(() => (init_upgrade(), exports_upgrade)).then((m) => m.default),
|
|
4588
6072
|
facts: () => Promise.resolve().then(() => (init_facts(), exports_facts)).then((m) => m.default),
|
|
6073
|
+
sources: () => Promise.resolve().then(() => (init_sources(), exports_sources)).then((m) => m.default),
|
|
4589
6074
|
search: () => Promise.resolve().then(() => (init_search(), exports_search)).then((m) => m.default),
|
|
4590
6075
|
sql: () => Promise.resolve().then(() => (init_sql(), exports_sql)).then((m) => m.default),
|
|
4591
6076
|
schema: () => Promise.resolve().then(() => (init_schema(), exports_schema)).then((m) => m.default),
|
|
6077
|
+
integrations: () => Promise.resolve().then(() => (init_integrations(), exports_integrations)).then((m) => m.default),
|
|
4592
6078
|
completions: () => Promise.resolve().then(() => (init_completions(), exports_completions)).then((m) => m.default),
|
|
4593
|
-
setup: () => Promise.resolve().then(() => (
|
|
6079
|
+
setup: () => Promise.resolve().then(() => (init_setup2(), exports_setup)).then((m) => m.default)
|
|
4594
6080
|
}
|
|
4595
6081
|
});
|
|
4596
6082
|
runMain(main);
|