@corsair-dev/cli 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +242 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @corsair-
|
|
1
|
+
# @corsair-dev/cli
|
|
2
2
|
|
|
3
3
|
The Corsair CLI handles everything needed to get a Corsair instance running: database setup, credential storage, and OAuth token acquisition. The intention is that a developer (or an agent) can go from a blank project to a fully authorized integration with a minimal number of commands, without ever needing to manually touch a database or manage encryption keys.
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ All output is JSON, so both humans and agents can consume it the same way.
|
|
|
11
11
|
The CLI is included when you install `corsair`:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pnpm add corsair @corsair-
|
|
14
|
+
pnpm add corsair @corsair-dev/cli
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
The `corsair` binary is available immediately after install via `pnpm exec corsair <command>` or `npx corsair <command>`. No additional configuration needed.
|
|
@@ -106,7 +106,7 @@ npx corsair auth --plugin=gmail --credentials
|
|
|
106
106
|
"client_secret": "***",
|
|
107
107
|
"access_token": "ya29.A...abc",
|
|
108
108
|
"refresh_token": "1//0g...xyz",
|
|
109
|
-
"expires_at": "
|
|
109
|
+
"expires_at": "1234567890"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
```
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAsLA;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,kBAAkB,CAAC,EACxC,GAAG,EACH,UAAU,EACV,kBAA0B,GAC1B,EAAE;IACF,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAsLA;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,kBAAkB,CAAC,EACxC,GAAG,EACH,UAAU,EACV,kBAA0B,GAC1B,EAAE;IACF,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,eA8KA"}
|
package/dist/index.js
CHANGED
|
@@ -268,7 +268,7 @@ async function oauthExchangeCode(database, plugin, kek, tenantId, code, redirect
|
|
|
268
268
|
});
|
|
269
269
|
await accountKm.set_access_token(tokens.access_token);
|
|
270
270
|
if (tokens.refresh_token) await accountKm.set_refresh_token(tokens.refresh_token);
|
|
271
|
-
if (tokens.expires_in) await accountKm.set_expires_at((Date.now() + tokens.expires_in
|
|
271
|
+
if (tokens.expires_in) await accountKm.set_expires_at((Math.floor(Date.now() / 1e3) + tokens.expires_in).toString());
|
|
272
272
|
out({ status: "success", plugin: plugin.id, tenant: tenantId });
|
|
273
273
|
}
|
|
274
274
|
async function oauthWithCode(database, plugin, kek, tenantId, code) {
|
|
@@ -1060,10 +1060,30 @@ async function getCorsairInstance({
|
|
|
1060
1060
|
);
|
|
1061
1061
|
process.exit(1);
|
|
1062
1062
|
}
|
|
1063
|
+
const msg = typeof e === "object" && e && "message" in e && typeof e.message === "string" ? e.message : String(e);
|
|
1064
|
+
if (msg.includes("Could not locate the bindings file") || msg.includes("NODE_MODULE_VERSION") || msg.includes(".node")) {
|
|
1065
|
+
if (shouldThrowOnError) {
|
|
1066
|
+
throw new Error(
|
|
1067
|
+
`Native module error in ${possiblePath}: ${msg}
|
|
1068
|
+
|
|
1069
|
+
This is likely because a native Node.js addon (e.g. better-sqlite3) needs to be rebuilt for your current Node.js version. Try running:
|
|
1070
|
+
npm rebuild
|
|
1071
|
+
or reinstall your dependencies:
|
|
1072
|
+
rm -rf node_modules && npm install`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
console.error(`[#corsair]: Error loading ${possiblePath}: Native module binding not found.`);
|
|
1076
|
+
console.log("");
|
|
1077
|
+
console.log("[#corsair]: A native Node.js addon (e.g. better-sqlite3) needs to be rebuilt for your current Node.js version.");
|
|
1078
|
+
console.log("[#corsair]: Try running:");
|
|
1079
|
+
console.log(" npm rebuild");
|
|
1080
|
+
console.log("[#corsair]: Or reinstall your dependencies:");
|
|
1081
|
+
console.log(" rm -rf node_modules && npm install");
|
|
1082
|
+
process.exit(1);
|
|
1083
|
+
}
|
|
1063
1084
|
if (shouldThrowOnError) {
|
|
1064
1085
|
throw e;
|
|
1065
1086
|
}
|
|
1066
|
-
const msg = typeof e === "object" && e && "message" in e && typeof e.message === "string" ? e.message : String(e);
|
|
1067
1087
|
console.error(`[#corsair]: Error loading ${possiblePath}:`, msg);
|
|
1068
1088
|
process.exit(1);
|
|
1069
1089
|
}
|
|
@@ -1104,6 +1124,46 @@ async function getCorsairInstance({
|
|
|
1104
1124
|
process.exit(1);
|
|
1105
1125
|
}
|
|
1106
1126
|
}
|
|
1127
|
+
function resolveClient(instance, tenant) {
|
|
1128
|
+
const obj = instance;
|
|
1129
|
+
if ("withTenant" in obj && typeof obj.withTenant === "function") {
|
|
1130
|
+
if (!tenant) {
|
|
1131
|
+
console.error("[#corsair]: This is a multi-tenant instance. Pass --tenant=<id>.");
|
|
1132
|
+
process.exit(1);
|
|
1133
|
+
}
|
|
1134
|
+
return obj.withTenant(tenant);
|
|
1135
|
+
}
|
|
1136
|
+
return obj;
|
|
1137
|
+
}
|
|
1138
|
+
function navigateToEndpoint(client, path3) {
|
|
1139
|
+
const parts = path3.split(".");
|
|
1140
|
+
let current = client;
|
|
1141
|
+
for (const part of parts) {
|
|
1142
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
1143
|
+
current = current[part];
|
|
1144
|
+
}
|
|
1145
|
+
return typeof current === "function" ? current : void 0;
|
|
1146
|
+
}
|
|
1147
|
+
function parseListArgs(args) {
|
|
1148
|
+
let plugin;
|
|
1149
|
+
let type;
|
|
1150
|
+
for (const arg of args) {
|
|
1151
|
+
const eqIdx = arg.indexOf("=");
|
|
1152
|
+
if (arg.startsWith("--") && eqIdx !== -1) {
|
|
1153
|
+
const key = arg.slice(2, eqIdx);
|
|
1154
|
+
const value = arg.slice(eqIdx + 1);
|
|
1155
|
+
if (key === "plugin") {
|
|
1156
|
+
plugin = value;
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
if (key === "type" && (value === "api" || value === "webhooks" || value === "db")) {
|
|
1160
|
+
type = value;
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return { plugin, type };
|
|
1166
|
+
}
|
|
1107
1167
|
function parseAuthArgs(args) {
|
|
1108
1168
|
let pluginId;
|
|
1109
1169
|
let tenantId;
|
|
@@ -1160,24 +1220,84 @@ function parseSetupArgs(args) {
|
|
|
1160
1220
|
}
|
|
1161
1221
|
return { backfill, credentials };
|
|
1162
1222
|
}
|
|
1223
|
+
function parseRunArgs(args) {
|
|
1224
|
+
let endpointPath;
|
|
1225
|
+
let input;
|
|
1226
|
+
let tenant;
|
|
1227
|
+
for (let i = 0; i < args.length; i++) {
|
|
1228
|
+
const arg = args[i];
|
|
1229
|
+
if (arg === "--tenant" && args[i + 1]) {
|
|
1230
|
+
tenant = args[++i];
|
|
1231
|
+
} else if (arg.startsWith("--tenant=")) {
|
|
1232
|
+
tenant = arg.slice("--tenant=".length);
|
|
1233
|
+
} else if (arg === "--input" && args[i + 1]) {
|
|
1234
|
+
input = args[++i];
|
|
1235
|
+
} else if (arg.startsWith("--input=")) {
|
|
1236
|
+
input = arg.slice("--input=".length);
|
|
1237
|
+
} else if (!arg.startsWith("-")) {
|
|
1238
|
+
if (!endpointPath) endpointPath = arg;
|
|
1239
|
+
else if (!input) input = arg;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return { path: endpointPath, input, tenant };
|
|
1243
|
+
}
|
|
1244
|
+
function parseScriptArgs(args) {
|
|
1245
|
+
let code;
|
|
1246
|
+
let tenant;
|
|
1247
|
+
for (let i = 0; i < args.length; i++) {
|
|
1248
|
+
const arg = args[i];
|
|
1249
|
+
if (arg === "--code" && args[i + 1]) {
|
|
1250
|
+
code = args[++i];
|
|
1251
|
+
} else if (arg.startsWith("--code=")) {
|
|
1252
|
+
code = arg.slice("--code=".length);
|
|
1253
|
+
} else if (arg === "--tenant" && args[i + 1]) {
|
|
1254
|
+
tenant = args[++i];
|
|
1255
|
+
} else if (arg.startsWith("--tenant=")) {
|
|
1256
|
+
tenant = arg.slice("--tenant=".length);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return { code, tenant };
|
|
1260
|
+
}
|
|
1261
|
+
function printHelp() {
|
|
1262
|
+
console.log("[#corsair]: Corsair CLI\n");
|
|
1263
|
+
console.log("Commands:");
|
|
1264
|
+
console.log(" corsair setup Initialize your Corsair instance");
|
|
1265
|
+
console.log(" corsair setup -backfill Initialize and backfill initial data");
|
|
1266
|
+
console.log(" corsair setup --<plugin> <field>=VALUE ... Set credentials for a plugin");
|
|
1267
|
+
console.log("");
|
|
1268
|
+
console.log(" corsair auth --plugin=<id> Start OAuth flow (outputs auth URL as JSON)");
|
|
1269
|
+
console.log(" corsair auth --plugin=<id> --code=<code> Exchange OAuth code for tokens");
|
|
1270
|
+
console.log(" corsair auth --plugin=<id> --credentials Show current credential status");
|
|
1271
|
+
console.log("");
|
|
1272
|
+
console.log(" corsair list List all API endpoint paths across all plugins");
|
|
1273
|
+
console.log(" corsair list --plugin=<id> List paths for a specific plugin");
|
|
1274
|
+
console.log(" corsair list --type=api|webhooks|db Filter by operation type (default: api)");
|
|
1275
|
+
console.log(" corsair list --plugin=<id> --type=<type> Combine plugin + type filters");
|
|
1276
|
+
console.log("");
|
|
1277
|
+
console.log(" corsair schema <path> Show schema for an endpoint, webhook, or DB entity");
|
|
1278
|
+
console.log(" corsair schema slack.api.messages.post Example: API endpoint schema");
|
|
1279
|
+
console.log(" corsair schema slack.webhooks.messages.message Example: webhook schema");
|
|
1280
|
+
console.log(" corsair schema slack.db.messages.search Example: DB search schema");
|
|
1281
|
+
console.log("");
|
|
1282
|
+
console.log(" corsair run <path> [input-json] Call an API endpoint and print the result");
|
|
1283
|
+
console.log(" corsair run slack.api.channels.list Example: no input needed");
|
|
1284
|
+
console.log(` corsair run slack.api.messages.post '{"channel":"C123","text":"hi"}'`);
|
|
1285
|
+
console.log(" corsair run <path> [input-json] --tenant=<id> Multi-tenant variant");
|
|
1286
|
+
console.log("");
|
|
1287
|
+
console.log(' corsair script --code "<js>" Run a sandboxed script with corsair injected');
|
|
1288
|
+
console.log(' corsair script --code "<js>" --tenant=<id> Multi-tenant variant');
|
|
1289
|
+
console.log(" # corsair is pre-injected; use return to output a value:");
|
|
1290
|
+
console.log(` # --code "const r = await corsair.slack.api.channels.list(); return r.channels.find(c => c.name === 'general')?.id"`);
|
|
1291
|
+
console.log("");
|
|
1292
|
+
console.log(" corsair watch-renew Renew Google webhook watch (Gmail/Drive/Calendar)");
|
|
1293
|
+
console.log(" corsair help Show this help message\n");
|
|
1294
|
+
}
|
|
1163
1295
|
async function main() {
|
|
1164
1296
|
const cwd = process.cwd();
|
|
1165
1297
|
const args = process.argv.slice(2);
|
|
1166
1298
|
const command = args[0];
|
|
1167
1299
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
1168
|
-
|
|
1169
|
-
console.log("Commands:");
|
|
1170
|
-
console.log(" corsair setup Initialize your Corsair instance");
|
|
1171
|
-
console.log(" corsair setup -backfill Initialize and backfill initial data");
|
|
1172
|
-
console.log(" corsair setup --<plugin> <field>=VALUE ... Set credentials for a plugin");
|
|
1173
|
-
console.log(" corsair auth --plugin=<id> Start OAuth flow (outputs auth URL as JSON)");
|
|
1174
|
-
console.log(" corsair auth --plugin=<id> --code=<code> Exchange OAuth code for tokens");
|
|
1175
|
-
console.log(" corsair auth --plugin=<id> --credentials Show current credential status");
|
|
1176
|
-
console.log(
|
|
1177
|
-
" corsair watch-renew Renew Google webhook watch (Gmail/Drive/Calendar)"
|
|
1178
|
-
);
|
|
1179
|
-
console.log(" corsair migrate Create a data migration script");
|
|
1180
|
-
console.log(" corsair migrate help Show migration help\n");
|
|
1300
|
+
printHelp();
|
|
1181
1301
|
return;
|
|
1182
1302
|
}
|
|
1183
1303
|
if (command === "setup") {
|
|
@@ -1202,6 +1322,113 @@ async function main() {
|
|
|
1202
1322
|
await runWatchRenew2({ cwd });
|
|
1203
1323
|
return;
|
|
1204
1324
|
}
|
|
1325
|
+
if (command === "list") {
|
|
1326
|
+
const { plugin, type } = parseListArgs(args.slice(1));
|
|
1327
|
+
const instance = await getCorsairInstance({ cwd });
|
|
1328
|
+
const corsair = instance;
|
|
1329
|
+
if (typeof corsair.list_operations !== "function") {
|
|
1330
|
+
console.error("[#corsair]: list_operations not available on this Corsair instance.");
|
|
1331
|
+
process.exit(1);
|
|
1332
|
+
}
|
|
1333
|
+
const result = corsair.list_operations({ plugin, type });
|
|
1334
|
+
if (typeof result === "string") {
|
|
1335
|
+
console.log(result);
|
|
1336
|
+
} else if (Array.isArray(result)) {
|
|
1337
|
+
for (const path3 of result) {
|
|
1338
|
+
console.log(path3);
|
|
1339
|
+
}
|
|
1340
|
+
console.log("");
|
|
1341
|
+
console.log("Run `pnpm corsair schema <path>` to get the schema for any of the above.");
|
|
1342
|
+
} else if (result && typeof result === "object") {
|
|
1343
|
+
const grouped = result;
|
|
1344
|
+
for (const [pluginId, paths] of Object.entries(grouped)) {
|
|
1345
|
+
console.log(`${pluginId}:`);
|
|
1346
|
+
for (const path3 of paths) {
|
|
1347
|
+
console.log(` ${path3}`);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
console.log("");
|
|
1351
|
+
console.log("Run `pnpm corsair schema <path>` to get the schema for any of the above.");
|
|
1352
|
+
}
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
if (command === "run") {
|
|
1356
|
+
const { path: endpointPath, input, tenant } = parseRunArgs(args.slice(1));
|
|
1357
|
+
if (!endpointPath) {
|
|
1358
|
+
console.error("[#corsair]: Usage: corsair run <path> [input-json]");
|
|
1359
|
+
console.error(`[#corsair]: Example: corsair run slack.api.messages.post '{"channel":"C123","text":"hi"}'`);
|
|
1360
|
+
process.exit(1);
|
|
1361
|
+
}
|
|
1362
|
+
const instance = await getCorsairInstance({ cwd });
|
|
1363
|
+
const client = resolveClient(instance, tenant);
|
|
1364
|
+
const fn = navigateToEndpoint(client, endpointPath);
|
|
1365
|
+
if (!fn) {
|
|
1366
|
+
console.error(`[#corsair]: Could not find endpoint "${endpointPath}".`);
|
|
1367
|
+
console.error("[#corsair]: Run `pnpm corsair list` to see available paths.");
|
|
1368
|
+
process.exit(1);
|
|
1369
|
+
}
|
|
1370
|
+
let parsedInput = {};
|
|
1371
|
+
if (input) {
|
|
1372
|
+
try {
|
|
1373
|
+
parsedInput = JSON.parse(input);
|
|
1374
|
+
} catch {
|
|
1375
|
+
console.error("[#corsair]: Invalid JSON input. Make sure to quote the JSON string.");
|
|
1376
|
+
process.exit(1);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
try {
|
|
1380
|
+
const result = await fn(parsedInput);
|
|
1381
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1382
|
+
} catch (e) {
|
|
1383
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1384
|
+
console.error(`[#corsair]: ${msg.slice(0, 500)}`);
|
|
1385
|
+
process.exit(1);
|
|
1386
|
+
}
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
if (command === "script") {
|
|
1390
|
+
const { code, tenant } = parseScriptArgs(args.slice(1));
|
|
1391
|
+
if (!code) {
|
|
1392
|
+
console.error('[#corsair]: Usage: corsair script --code "<js>"');
|
|
1393
|
+
console.error(`[#corsair]: Example: corsair script --code "const r = await corsair.slack.channels.list(); return r.channels.find(c => c.name === 'general')?.id"`);
|
|
1394
|
+
process.exit(1);
|
|
1395
|
+
}
|
|
1396
|
+
const instance = await getCorsairInstance({ cwd });
|
|
1397
|
+
const client = resolveClient(instance, tenant);
|
|
1398
|
+
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
1399
|
+
}).constructor;
|
|
1400
|
+
const fn = new AsyncFunction("corsair", code);
|
|
1401
|
+
try {
|
|
1402
|
+
const result = await fn(client);
|
|
1403
|
+
if (result !== void 0) {
|
|
1404
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1405
|
+
}
|
|
1406
|
+
} catch (e) {
|
|
1407
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1408
|
+
console.error(`[#corsair]: ${msg.slice(0, 500)}`);
|
|
1409
|
+
process.exit(1);
|
|
1410
|
+
}
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
if (command === "schema") {
|
|
1414
|
+
const schemaPath = args[1];
|
|
1415
|
+
if (!schemaPath) {
|
|
1416
|
+
console.error("[#corsair]: Usage: corsair schema <path>");
|
|
1417
|
+
console.error("[#corsair]: Example: corsair schema slack.api.messages.post");
|
|
1418
|
+
process.exit(1);
|
|
1419
|
+
}
|
|
1420
|
+
const instance = await getCorsairInstance({ cwd });
|
|
1421
|
+
const corsair = instance;
|
|
1422
|
+
if (typeof corsair.get_schema !== "function") {
|
|
1423
|
+
console.error("[#corsair]: get_schema not available on this Corsair instance.");
|
|
1424
|
+
process.exit(1);
|
|
1425
|
+
}
|
|
1426
|
+
const result = corsair.get_schema(schemaPath);
|
|
1427
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
printHelp();
|
|
1431
|
+
console.log("");
|
|
1205
1432
|
console.log(`[#corsair]: Looking for Corsair instance in ${cwd}...
|
|
1206
1433
|
`);
|
|
1207
1434
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corsair-dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@clack/prompts": "^1.0.0",
|
|
24
24
|
"c12": "^3.3.3",
|
|
25
25
|
"zod": "^3.25.76",
|
|
26
|
-
"corsair": "0.1.
|
|
26
|
+
"corsair": "0.1.46"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/jest": "^29.5.14",
|