@oodarun/cli 0.1.14 → 0.1.15
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 +213 -41
- 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,126 @@ 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
|
+
}
|
|
1192
1328
|
async function completeJwtLogin(result) {
|
|
1193
1329
|
let orgId;
|
|
1194
1330
|
let orgName;
|
|
@@ -5767,6 +5903,8 @@ Usage:
|
|
|
5767
5903
|
|
|
5768
5904
|
Commands:
|
|
5769
5905
|
(no command) Open the interactive project menu
|
|
5906
|
+
login Sign in with an email code (password fallback)
|
|
5907
|
+
whoami Show the current session (exits non-zero if none)
|
|
5770
5908
|
list, ls List your projects
|
|
5771
5909
|
connect <project> Connect to a project and run Claude (interactive)
|
|
5772
5910
|
deploy [path|github-url] Deploy a local folder or GitHub repo as a project
|
|
@@ -5777,6 +5915,14 @@ Commands:
|
|
|
5777
5915
|
sites delete <slug> Unpublish a site
|
|
5778
5916
|
help, --help, -h Show this help
|
|
5779
5917
|
|
|
5918
|
+
login
|
|
5919
|
+
Passwordless sign-in via a 6-digit email code. No flags \u2192 interactive (with a
|
|
5920
|
+
password fallback). Flag mode (for agents/CI):
|
|
5921
|
+
--email <e> Send a login code to this email
|
|
5922
|
+
--email <e> --code <c> Verify the code and save the session
|
|
5923
|
+
--org <id> Choose the org when the account has several
|
|
5924
|
+
--json Machine-readable JSON output
|
|
5925
|
+
|
|
5780
5926
|
publish [path]
|
|
5781
5927
|
Publishes an already-built static site (no build is run). Looks for a build
|
|
5782
5928
|
output dir (dist, build, out, .output/public, .next/static) in [path] (default:
|
|
@@ -5813,7 +5959,7 @@ Examples:
|
|
|
5813
5959
|
}
|
|
5814
5960
|
|
|
5815
5961
|
// src/cli/index.ts
|
|
5816
|
-
var CLI_VERSION = "0.1.
|
|
5962
|
+
var CLI_VERSION = "0.1.15";
|
|
5817
5963
|
function formatMutationError(result) {
|
|
5818
5964
|
const parts = [];
|
|
5819
5965
|
if (result.status !== void 0) parts.push(String(result.status));
|
|
@@ -5859,6 +6005,8 @@ function parseArgs(argv) {
|
|
|
5859
6005
|
else if (first === "deploy") command = "deploy";
|
|
5860
6006
|
else if (first === "publish") command = "publish";
|
|
5861
6007
|
else if (first === "sites") command = "sites";
|
|
6008
|
+
else if (first === "login") command = "login";
|
|
6009
|
+
else if (first === "whoami") command = "whoami";
|
|
5862
6010
|
else if (first === "help") command = "help";
|
|
5863
6011
|
if (rest.includes("--help") || rest.includes("-h")) command = "help";
|
|
5864
6012
|
const positionals = [];
|
|
@@ -5873,6 +6021,15 @@ function parseArgs(argv) {
|
|
|
5873
6021
|
} else if (arg === "--slug" && rest[i + 1]) {
|
|
5874
6022
|
flags.slug = rest[i + 1];
|
|
5875
6023
|
i++;
|
|
6024
|
+
} else if (arg === "--email" && rest[i + 1]) {
|
|
6025
|
+
flags.email = rest[i + 1];
|
|
6026
|
+
i++;
|
|
6027
|
+
} else if (arg === "--code" && rest[i + 1]) {
|
|
6028
|
+
flags.code = rest[i + 1];
|
|
6029
|
+
i++;
|
|
6030
|
+
} else if (arg === "--org" && rest[i + 1]) {
|
|
6031
|
+
flags.org = rest[i + 1];
|
|
6032
|
+
i++;
|
|
5876
6033
|
} else if (arg === "--mode" && rest[i + 1]) {
|
|
5877
6034
|
flags.mode = rest[i + 1];
|
|
5878
6035
|
i++;
|
|
@@ -5902,6 +6059,13 @@ function parseArgs(argv) {
|
|
|
5902
6059
|
clearMode: Boolean(flags.clearMode),
|
|
5903
6060
|
json: Boolean(flags.json)
|
|
5904
6061
|
};
|
|
6062
|
+
} else if (command === "login") {
|
|
6063
|
+
args.login = {
|
|
6064
|
+
email: flags.email,
|
|
6065
|
+
code: flags.code,
|
|
6066
|
+
org: flags.org,
|
|
6067
|
+
json: Boolean(flags.json)
|
|
6068
|
+
};
|
|
5905
6069
|
}
|
|
5906
6070
|
return args;
|
|
5907
6071
|
}
|
|
@@ -6353,6 +6517,14 @@ async function main() {
|
|
|
6353
6517
|
await runSitesCommand(args.sites ?? {});
|
|
6354
6518
|
process.exit(process.exitCode ?? 0);
|
|
6355
6519
|
}
|
|
6520
|
+
if (args.command === "login") {
|
|
6521
|
+
await loginCmd(args.login ?? {});
|
|
6522
|
+
process.exit(process.exitCode ?? 0);
|
|
6523
|
+
}
|
|
6524
|
+
if (args.command === "whoami") {
|
|
6525
|
+
whoamiCmd({ json: args.json });
|
|
6526
|
+
process.exit(process.exitCode ?? 0);
|
|
6527
|
+
}
|
|
6356
6528
|
if (args.command === "connect") {
|
|
6357
6529
|
if (!args.connectTarget) {
|
|
6358
6530
|
console.log(` ${c.red}Usage: ooda connect <project-name>${c.reset}`);
|