@oodarun/cli 0.1.14 → 0.1.16
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 +235 -42
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -846,6 +846,31 @@ async function loginWithPassword(email, password) {
|
|
|
846
846
|
return null;
|
|
847
847
|
}
|
|
848
848
|
}
|
|
849
|
+
async function requestLoginCode(email) {
|
|
850
|
+
try {
|
|
851
|
+
const res = await fetch(`${ORG_API_BASE}/auth/request-code`, {
|
|
852
|
+
method: "POST",
|
|
853
|
+
headers: { "Content-Type": "application/json" },
|
|
854
|
+
body: JSON.stringify({ email })
|
|
855
|
+
});
|
|
856
|
+
return res.ok;
|
|
857
|
+
} catch {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async function verifyLoginCode(email, code) {
|
|
862
|
+
try {
|
|
863
|
+
const res = await fetch(`${ORG_API_BASE}/auth/verify-code`, {
|
|
864
|
+
method: "POST",
|
|
865
|
+
headers: { "Content-Type": "application/json" },
|
|
866
|
+
body: JSON.stringify({ email, code })
|
|
867
|
+
});
|
|
868
|
+
if (!res.ok) return null;
|
|
869
|
+
return await res.json();
|
|
870
|
+
} catch {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
849
874
|
async function signupWithPassword(email, password, name) {
|
|
850
875
|
try {
|
|
851
876
|
const res = await fetch(`${ORG_API_BASE}/auth/signup`, {
|
|
@@ -1129,58 +1154,49 @@ function patchFetchForOrg(orgId) {
|
|
|
1129
1154
|
});
|
|
1130
1155
|
}
|
|
1131
1156
|
async function handleEmailPasswordAuth() {
|
|
1132
|
-
const
|
|
1133
|
-
message: "
|
|
1157
|
+
const method = await select2({
|
|
1158
|
+
message: "How would you like to sign in?",
|
|
1134
1159
|
choices: [
|
|
1135
|
-
{ name: "
|
|
1136
|
-
{ name: "
|
|
1160
|
+
{ name: "Email me a login code (recommended)", value: "code" },
|
|
1161
|
+
{ name: "Use my password", value: "password" },
|
|
1162
|
+
{ name: "Sign up (requires invite)", value: "signup" }
|
|
1137
1163
|
]
|
|
1138
1164
|
});
|
|
1139
1165
|
console.log("");
|
|
1140
|
-
if (
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
if (!result) {
|
|
1150
|
-
console.log(` ${c.red}Could not reach the server. Check your connection.${c.reset}`);
|
|
1151
|
-
process.exit(1);
|
|
1152
|
-
}
|
|
1153
|
-
if (result.ok) {
|
|
1154
|
-
const loginResult = await loginWithPassword(email2, password);
|
|
1155
|
-
if (!loginResult) {
|
|
1156
|
-
console.log(` ${c.red}Account created but login failed. Try again.${c.reset}`);
|
|
1157
|
-
process.exit(1);
|
|
1158
|
-
}
|
|
1159
|
-
return completeJwtLogin(loginResult);
|
|
1160
|
-
}
|
|
1161
|
-
const remaining = 2 - attempt;
|
|
1162
|
-
console.log(` ${c.red}${result.error || "Signup failed"}${c.reset}`);
|
|
1163
|
-
if (result.errors?.length) {
|
|
1164
|
-
for (const err of result.errors) {
|
|
1165
|
-
console.log(` ${c.gray}\u2022 ${err}${c.reset}`);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
if (remaining > 0) {
|
|
1169
|
-
console.log(` ${c.gray}${remaining} attempt${remaining > 1 ? "s" : ""} remaining${c.reset}`);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
console.log(` ${c.red}Too many failed attempts.${c.reset}`);
|
|
1166
|
+
if (method === "code") return emailCodeLoginInteractive();
|
|
1167
|
+
if (method === "signup") return signupInteractive();
|
|
1168
|
+
return passwordLoginInteractive();
|
|
1169
|
+
}
|
|
1170
|
+
async function emailCodeLoginInteractive(prefillEmail) {
|
|
1171
|
+
const email = prefillEmail || await prompt(` ${c.blue}${c.bold}Email:${c.reset} `);
|
|
1172
|
+
if (!email) process.exit(1);
|
|
1173
|
+
if (!await requestLoginCode(email)) {
|
|
1174
|
+
console.log(` ${c.red}Couldn't send a login code. Check the email address and try again.${c.reset}`);
|
|
1173
1175
|
process.exit(1);
|
|
1174
1176
|
}
|
|
1177
|
+
console.log(` ${c.gray}We emailed a 6-digit code to ${email} (expires in 10 minutes).${c.reset}`);
|
|
1178
|
+
console.log("");
|
|
1179
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1180
|
+
const code = await promptToken(` ${c.blue}${c.bold}Code:${c.reset} `);
|
|
1181
|
+
if (!code) process.exit(1);
|
|
1182
|
+
const result = await verifyLoginCode(email, code);
|
|
1183
|
+
if (result) return completeJwtLogin(result);
|
|
1184
|
+
const remaining = 2 - attempt;
|
|
1185
|
+
if (remaining > 0) {
|
|
1186
|
+
console.log(` ${c.red}Invalid or expired code.${c.reset} ${c.gray}${remaining} attempt${remaining > 1 ? "s" : ""} remaining${c.reset}`);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
console.log(` ${c.red}Too many failed attempts.${c.reset}`);
|
|
1190
|
+
process.exit(1);
|
|
1191
|
+
}
|
|
1192
|
+
async function passwordLoginInteractive() {
|
|
1175
1193
|
const email = await prompt(` ${c.blue}${c.bold}Email:${c.reset} `);
|
|
1176
1194
|
if (!email) process.exit(1);
|
|
1177
1195
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1178
1196
|
const password = await promptPassword(` ${c.blue}${c.bold}Password:${c.reset} `);
|
|
1179
1197
|
if (!password) process.exit(1);
|
|
1180
1198
|
const result = await loginWithPassword(email, password);
|
|
1181
|
-
if (result)
|
|
1182
|
-
return completeJwtLogin(result);
|
|
1183
|
-
}
|
|
1199
|
+
if (result) return completeJwtLogin(result);
|
|
1184
1200
|
const remaining = 2 - attempt;
|
|
1185
1201
|
if (remaining > 0) {
|
|
1186
1202
|
console.log(` ${c.red}Invalid password.${c.reset} ${c.gray}${remaining} attempt${remaining > 1 ? "s" : ""} remaining${c.reset}`);
|
|
@@ -1189,6 +1205,141 @@ async function handleEmailPasswordAuth() {
|
|
|
1189
1205
|
console.log(` ${c.red}Too many failed attempts.${c.reset}`);
|
|
1190
1206
|
process.exit(1);
|
|
1191
1207
|
}
|
|
1208
|
+
async function signupInteractive() {
|
|
1209
|
+
const email = await prompt(` ${c.blue}${c.bold}Email:${c.reset} `);
|
|
1210
|
+
if (!email) process.exit(1);
|
|
1211
|
+
const name = await prompt(` ${c.blue}${c.bold}Your name:${c.reset} `);
|
|
1212
|
+
if (!name) process.exit(1);
|
|
1213
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1214
|
+
const password = await promptPassword(` ${c.blue}${c.bold}Password:${c.reset} `);
|
|
1215
|
+
if (!password) process.exit(1);
|
|
1216
|
+
const result = await signupWithPassword(email, password, name);
|
|
1217
|
+
if (!result) {
|
|
1218
|
+
console.log(` ${c.red}Could not reach the server. Check your connection.${c.reset}`);
|
|
1219
|
+
process.exit(1);
|
|
1220
|
+
}
|
|
1221
|
+
if (result.ok) {
|
|
1222
|
+
const loginResult = await loginWithPassword(email, password);
|
|
1223
|
+
if (!loginResult) {
|
|
1224
|
+
console.log(` ${c.red}Account created but login failed. Try again.${c.reset}`);
|
|
1225
|
+
process.exit(1);
|
|
1226
|
+
}
|
|
1227
|
+
return completeJwtLogin(loginResult);
|
|
1228
|
+
}
|
|
1229
|
+
const remaining = 2 - attempt;
|
|
1230
|
+
console.log(` ${c.red}${result.error || "Signup failed"}${c.reset}`);
|
|
1231
|
+
if (result.errors?.length) {
|
|
1232
|
+
for (const err of result.errors) {
|
|
1233
|
+
console.log(` ${c.gray}\u2022 ${err}${c.reset}`);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (remaining > 0) {
|
|
1237
|
+
console.log(` ${c.gray}${remaining} attempt${remaining > 1 ? "s" : ""} remaining${c.reset}`);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
console.log(` ${c.red}Too many failed attempts.${c.reset}`);
|
|
1241
|
+
process.exit(1);
|
|
1242
|
+
}
|
|
1243
|
+
async function loginCmd(opts) {
|
|
1244
|
+
if (opts.email && opts.code) {
|
|
1245
|
+
const result = await verifyLoginCode(opts.email, opts.code);
|
|
1246
|
+
if (!result) return failLogin(opts.json, "Invalid or expired code.");
|
|
1247
|
+
finalizeLogin(result, opts.org, opts.json);
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
if (opts.email) {
|
|
1251
|
+
if (!await requestLoginCode(opts.email)) return failLogin(opts.json, "Couldn't send a login code.");
|
|
1252
|
+
if (opts.json) {
|
|
1253
|
+
console.log(JSON.stringify({ ok: true, sent: true, email: opts.email }));
|
|
1254
|
+
} else {
|
|
1255
|
+
console.log(`
|
|
1256
|
+
${c.green}${c.bold}\u2713${c.reset} Code sent to ${c.bold}${opts.email}${c.reset}`);
|
|
1257
|
+
console.log(` ${c.gray}Check your email, then run:${c.reset}`);
|
|
1258
|
+
console.log(` ${c.cyan}ooda login --email ${opts.email} --code <code>${c.reset}
|
|
1259
|
+
`);
|
|
1260
|
+
}
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
await emailCodeLoginInteractive();
|
|
1264
|
+
}
|
|
1265
|
+
function finalizeLogin(result, orgFlag, json) {
|
|
1266
|
+
if (result.orgs.length === 0) return failLogin(json, "No organizations for this account.");
|
|
1267
|
+
let orgId;
|
|
1268
|
+
let orgName;
|
|
1269
|
+
if (orgFlag) {
|
|
1270
|
+
const match = result.orgs.find((o) => o.orgId === orgFlag);
|
|
1271
|
+
if (!match) return failLogin(json, `You're not a member of org "${orgFlag}".`);
|
|
1272
|
+
orgId = match.orgId;
|
|
1273
|
+
orgName = match.orgName;
|
|
1274
|
+
} else if (result.orgs.length === 1) {
|
|
1275
|
+
orgId = result.orgs[0].orgId;
|
|
1276
|
+
orgName = result.orgs[0].orgName;
|
|
1277
|
+
} else {
|
|
1278
|
+
return failLogin(json, `Multiple organizations \u2014 pass --org <id>. Options: ${result.orgs.map((o) => o.orgId).join(", ")}`);
|
|
1279
|
+
}
|
|
1280
|
+
saveJwtTokens(result.accessToken, result.refreshToken, result.user.id, orgId);
|
|
1281
|
+
const selected = result.orgs.find((o) => o.orgId === orgId);
|
|
1282
|
+
setOrgMode(orgId, result.user.email, result.user.name, orgName, selected?.claudeConfig ?? void 0);
|
|
1283
|
+
patchFetchForOrg(orgId);
|
|
1284
|
+
if (json) {
|
|
1285
|
+
console.log(JSON.stringify({ ok: true, email: result.user.email, orgId, orgName }));
|
|
1286
|
+
} else {
|
|
1287
|
+
console.log(`
|
|
1288
|
+
${c.green}${c.bold}\u2713${c.reset} Logged in as ${c.bold}${result.user.name}${c.reset} (${orgName})
|
|
1289
|
+
`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
function failLogin(json, message) {
|
|
1293
|
+
if (json) {
|
|
1294
|
+
console.log(JSON.stringify({ ok: false, error: message }));
|
|
1295
|
+
} else {
|
|
1296
|
+
console.log(`
|
|
1297
|
+
${c.red}${message}${c.reset}
|
|
1298
|
+
`);
|
|
1299
|
+
}
|
|
1300
|
+
process.exitCode = 1;
|
|
1301
|
+
}
|
|
1302
|
+
function whoamiCmd(opts) {
|
|
1303
|
+
const orgId = getOrgId();
|
|
1304
|
+
const token = getAccessToken();
|
|
1305
|
+
if (!orgId || !token) {
|
|
1306
|
+
if (opts.json) {
|
|
1307
|
+
console.log(JSON.stringify({ ok: false, authenticated: false }));
|
|
1308
|
+
} else {
|
|
1309
|
+
console.log(`
|
|
1310
|
+
${c.yellow}Not signed in.${c.reset} ${c.gray}Run ${c.bold}ooda login${c.reset}${c.gray} to authenticate.${c.reset}
|
|
1311
|
+
`);
|
|
1312
|
+
}
|
|
1313
|
+
process.exitCode = 1;
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const orgName = getOrgName() || orgId;
|
|
1317
|
+
const email = getOrgEmail();
|
|
1318
|
+
const name = getOrgDisplayName();
|
|
1319
|
+
if (opts.json) {
|
|
1320
|
+
console.log(JSON.stringify({ ok: true, authenticated: true, orgId, orgName, email: email ?? null, name: name ?? null }));
|
|
1321
|
+
} else {
|
|
1322
|
+
const who = name || email;
|
|
1323
|
+
console.log(`
|
|
1324
|
+
${c.green}${c.bold}\u2713${c.reset} Signed in${who ? ` as ${c.bold}${who}${c.reset}` : ""} ${c.gray}(${orgName})${c.reset}
|
|
1325
|
+
`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function logoutCmd(opts) {
|
|
1329
|
+
const envSession = Boolean(process.env.OODA_ACCESS_TOKEN || process.env.OODA_ORG_ID);
|
|
1330
|
+
clearJwtTokens();
|
|
1331
|
+
clearOrgMode();
|
|
1332
|
+
if (opts.json) {
|
|
1333
|
+
console.log(JSON.stringify({ ok: true, loggedOut: true, envSession }));
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
console.log(`
|
|
1337
|
+
${c.green}${c.bold}\u2713${c.reset} Logged out${c.gray} (cleared ~/.ooda/auth.json).${c.reset}`);
|
|
1338
|
+
if (envSession) {
|
|
1339
|
+
console.log(` ${c.yellow}!${c.reset} ${c.gray}A session is still set via ${c.bold}OODA_ACCESS_TOKEN${c.reset}${c.gray}/${c.bold}OODA_ORG_ID${c.reset}${c.gray} \u2014 unset those to fully log out.${c.reset}`);
|
|
1340
|
+
}
|
|
1341
|
+
console.log("");
|
|
1342
|
+
}
|
|
1192
1343
|
async function completeJwtLogin(result) {
|
|
1193
1344
|
let orgId;
|
|
1194
1345
|
let orgName;
|
|
@@ -5767,6 +5918,9 @@ Usage:
|
|
|
5767
5918
|
|
|
5768
5919
|
Commands:
|
|
5769
5920
|
(no command) Open the interactive project menu
|
|
5921
|
+
login Sign in with an email code (password fallback)
|
|
5922
|
+
logout Sign out (clears the saved session)
|
|
5923
|
+
whoami Show the current session (exits non-zero if none)
|
|
5770
5924
|
list, ls List your projects
|
|
5771
5925
|
connect <project> Connect to a project and run Claude (interactive)
|
|
5772
5926
|
deploy [path|github-url] Deploy a local folder or GitHub repo as a project
|
|
@@ -5777,6 +5931,14 @@ Commands:
|
|
|
5777
5931
|
sites delete <slug> Unpublish a site
|
|
5778
5932
|
help, --help, -h Show this help
|
|
5779
5933
|
|
|
5934
|
+
login
|
|
5935
|
+
Passwordless sign-in via a 6-digit email code. No flags \u2192 interactive (with a
|
|
5936
|
+
password fallback). Flag mode (for agents/CI):
|
|
5937
|
+
--email <e> Send a login code to this email
|
|
5938
|
+
--email <e> --code <c> Verify the code and save the session
|
|
5939
|
+
--org <id> Choose the org when the account has several
|
|
5940
|
+
--json Machine-readable JSON output
|
|
5941
|
+
|
|
5780
5942
|
publish [path]
|
|
5781
5943
|
Publishes an already-built static site (no build is run). Looks for a build
|
|
5782
5944
|
output dir (dist, build, out, .output/public, .next/static) in [path] (default:
|
|
@@ -5813,7 +5975,7 @@ Examples:
|
|
|
5813
5975
|
}
|
|
5814
5976
|
|
|
5815
5977
|
// src/cli/index.ts
|
|
5816
|
-
var CLI_VERSION = "0.1.
|
|
5978
|
+
var CLI_VERSION = "0.1.16";
|
|
5817
5979
|
function formatMutationError(result) {
|
|
5818
5980
|
const parts = [];
|
|
5819
5981
|
if (result.status !== void 0) parts.push(String(result.status));
|
|
@@ -5859,6 +6021,9 @@ function parseArgs(argv) {
|
|
|
5859
6021
|
else if (first === "deploy") command = "deploy";
|
|
5860
6022
|
else if (first === "publish") command = "publish";
|
|
5861
6023
|
else if (first === "sites") command = "sites";
|
|
6024
|
+
else if (first === "login") command = "login";
|
|
6025
|
+
else if (first === "logout") command = "logout";
|
|
6026
|
+
else if (first === "whoami") command = "whoami";
|
|
5862
6027
|
else if (first === "help") command = "help";
|
|
5863
6028
|
if (rest.includes("--help") || rest.includes("-h")) command = "help";
|
|
5864
6029
|
const positionals = [];
|
|
@@ -5873,6 +6038,15 @@ function parseArgs(argv) {
|
|
|
5873
6038
|
} else if (arg === "--slug" && rest[i + 1]) {
|
|
5874
6039
|
flags.slug = rest[i + 1];
|
|
5875
6040
|
i++;
|
|
6041
|
+
} else if (arg === "--email" && rest[i + 1]) {
|
|
6042
|
+
flags.email = rest[i + 1];
|
|
6043
|
+
i++;
|
|
6044
|
+
} else if (arg === "--code" && rest[i + 1]) {
|
|
6045
|
+
flags.code = rest[i + 1];
|
|
6046
|
+
i++;
|
|
6047
|
+
} else if (arg === "--org" && rest[i + 1]) {
|
|
6048
|
+
flags.org = rest[i + 1];
|
|
6049
|
+
i++;
|
|
5876
6050
|
} else if (arg === "--mode" && rest[i + 1]) {
|
|
5877
6051
|
flags.mode = rest[i + 1];
|
|
5878
6052
|
i++;
|
|
@@ -5902,6 +6076,13 @@ function parseArgs(argv) {
|
|
|
5902
6076
|
clearMode: Boolean(flags.clearMode),
|
|
5903
6077
|
json: Boolean(flags.json)
|
|
5904
6078
|
};
|
|
6079
|
+
} else if (command === "login") {
|
|
6080
|
+
args.login = {
|
|
6081
|
+
email: flags.email,
|
|
6082
|
+
code: flags.code,
|
|
6083
|
+
org: flags.org,
|
|
6084
|
+
json: Boolean(flags.json)
|
|
6085
|
+
};
|
|
5905
6086
|
}
|
|
5906
6087
|
return args;
|
|
5907
6088
|
}
|
|
@@ -6322,7 +6503,6 @@ async function directConnect(projectName) {
|
|
|
6322
6503
|
async function main() {
|
|
6323
6504
|
const args = parseArgs(process.argv);
|
|
6324
6505
|
const cwd = process.cwd();
|
|
6325
|
-
printLogo(CLI_VERSION);
|
|
6326
6506
|
if (args.command === "help") {
|
|
6327
6507
|
console.log(buildHelpText(CLI_VERSION));
|
|
6328
6508
|
process.exit(0);
|
|
@@ -6353,6 +6533,18 @@ async function main() {
|
|
|
6353
6533
|
await runSitesCommand(args.sites ?? {});
|
|
6354
6534
|
process.exit(process.exitCode ?? 0);
|
|
6355
6535
|
}
|
|
6536
|
+
if (args.command === "login") {
|
|
6537
|
+
await loginCmd(args.login ?? {});
|
|
6538
|
+
process.exit(process.exitCode ?? 0);
|
|
6539
|
+
}
|
|
6540
|
+
if (args.command === "whoami") {
|
|
6541
|
+
whoamiCmd({ json: args.json });
|
|
6542
|
+
process.exit(process.exitCode ?? 0);
|
|
6543
|
+
}
|
|
6544
|
+
if (args.command === "logout") {
|
|
6545
|
+
logoutCmd({ json: args.json });
|
|
6546
|
+
process.exit(process.exitCode ?? 0);
|
|
6547
|
+
}
|
|
6356
6548
|
if (args.command === "connect") {
|
|
6357
6549
|
if (!args.connectTarget) {
|
|
6358
6550
|
console.log(` ${c.red}Usage: ooda connect <project-name>${c.reset}`);
|
|
@@ -6364,6 +6556,7 @@ async function main() {
|
|
|
6364
6556
|
await directConnect(args.connectTarget);
|
|
6365
6557
|
process.exit(0);
|
|
6366
6558
|
}
|
|
6559
|
+
printLogo(CLI_VERSION);
|
|
6367
6560
|
const { port } = await startServer({ port: args.port, cwd });
|
|
6368
6561
|
const dashboardUrl = process.env.OODA_DASHBOARD_BASE || `http://localhost:${port}`;
|
|
6369
6562
|
while (true) {
|