@clipdone/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/bin/clipdone.js +42 -6
- package/package.json +1 -1
package/bin/clipdone.js
CHANGED
|
@@ -148,8 +148,9 @@ var OAUTH_AUTHORIZE_PATH = "/api/auth/oauth2/authorize";
|
|
|
148
148
|
var OAUTH_REGISTER_PATH = "/api/auth/oauth2/register";
|
|
149
149
|
var OAUTH_TOKEN_PATH = "/api/auth/oauth2/token";
|
|
150
150
|
var OAUTH_REVOKE_PATH = "/api/auth/oauth2/revoke";
|
|
151
|
-
var OAUTH_CLIENT_REGISTRATION_VERSION =
|
|
151
|
+
var OAUTH_CLIENT_REGISTRATION_VERSION = 3;
|
|
152
152
|
var OAUTH_LOOPBACK_PORTS = [59852, 59853, 59854, 59855, 59856, 59857, 59858, 59859];
|
|
153
|
+
var OAUTH_CALLBACK_PATH = "/agents/callback";
|
|
153
154
|
var PRODUCTION_APP_URL = "https://app.clipdone.app";
|
|
154
155
|
var LOCAL_APP_URL = "http://localhost:3000";
|
|
155
156
|
var REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -241,6 +242,13 @@ function clearStoredAuth() {
|
|
|
241
242
|
delete config.token;
|
|
242
243
|
delete config.tokenExpiresAt;
|
|
243
244
|
delete config.oauth;
|
|
245
|
+
delete config.oauthClient;
|
|
246
|
+
writeConfig(config);
|
|
247
|
+
}
|
|
248
|
+
function clearStoredOAuthClientRegistration() {
|
|
249
|
+
const config = readConfig();
|
|
250
|
+
if (!config.oauthClient) return;
|
|
251
|
+
delete config.oauthClient;
|
|
244
252
|
writeConfig(config);
|
|
245
253
|
}
|
|
246
254
|
function readStoredOAuthClientRegistration(scope, resource) {
|
|
@@ -530,7 +538,7 @@ async function oauthRegisterClient({ scope }) {
|
|
|
530
538
|
},
|
|
531
539
|
body: JSON.stringify({
|
|
532
540
|
client_name: "ClipDone CLI",
|
|
533
|
-
redirect_uris:
|
|
541
|
+
redirect_uris: [new URL(OAUTH_CALLBACK_PATH, appBase()).toString()],
|
|
534
542
|
grant_types: ["authorization_code", "refresh_token"],
|
|
535
543
|
response_types: ["code"],
|
|
536
544
|
token_endpoint_auth_method: "none",
|
|
@@ -558,10 +566,14 @@ async function oauthRegisterClient({ scope }) {
|
|
|
558
566
|
return { clientId };
|
|
559
567
|
}
|
|
560
568
|
async function resolveOAuthClient(authConfig) {
|
|
569
|
+
const activeSession = readStoredOAuthSession();
|
|
561
570
|
const cached = readStoredOAuthClientRegistration(authConfig?.scope, authConfig?.resource);
|
|
562
|
-
if (cached) {
|
|
571
|
+
if (cached && activeSession?.clientId === cached.clientId) {
|
|
563
572
|
return cached;
|
|
564
573
|
}
|
|
574
|
+
if (cached) {
|
|
575
|
+
clearStoredOAuthClientRegistration();
|
|
576
|
+
}
|
|
565
577
|
const registered = await oauthRegisterClient({ scope: authConfig.scope });
|
|
566
578
|
writeStoredOAuthClientRegistration(registered.clientId, authConfig.scope, authConfig.resource);
|
|
567
579
|
return registered;
|
|
@@ -944,6 +956,9 @@ function parseLegacyCommand(args, parts) {
|
|
|
944
956
|
}
|
|
945
957
|
function parseTopLevelCommand(args) {
|
|
946
958
|
const parts = Array.isArray(args._) ? args._ : [];
|
|
959
|
+
if (args.version === true || parts[0] === "version") {
|
|
960
|
+
return { key: "version" };
|
|
961
|
+
}
|
|
947
962
|
if (parts.length === 0 || parts[0] === "help") {
|
|
948
963
|
return { key: "help" };
|
|
949
964
|
}
|
|
@@ -1033,6 +1048,7 @@ function printHelp() {
|
|
|
1033
1048
|
writeLine();
|
|
1034
1049
|
writeLine("Useful options:");
|
|
1035
1050
|
writeLine(" --help Show command help");
|
|
1051
|
+
writeLine(" --version Show the installed CLI version");
|
|
1036
1052
|
if (devHelp) {
|
|
1037
1053
|
writeLine();
|
|
1038
1054
|
writeLine("Development target options:");
|
|
@@ -1049,6 +1065,10 @@ function readJsonFile(filePath) {
|
|
|
1049
1065
|
throw new Error(`Could not read JSON from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1050
1066
|
}
|
|
1051
1067
|
}
|
|
1068
|
+
function cliVersion() {
|
|
1069
|
+
const packageJson = readJsonFile(join(BIN_DIR, "..", "package.json"));
|
|
1070
|
+
return normalizeString(packageJson?.version) || "0.0.0";
|
|
1071
|
+
}
|
|
1052
1072
|
function normalizeFeedbackNotes(value) {
|
|
1053
1073
|
if (!value) return [];
|
|
1054
1074
|
if (Array.isArray(value)) return value;
|
|
@@ -1095,6 +1115,16 @@ function base64UrlEncode(bytes) {
|
|
|
1095
1115
|
function randomState() {
|
|
1096
1116
|
return base64UrlEncode(webcrypto.getRandomValues(new Uint8Array(24)));
|
|
1097
1117
|
}
|
|
1118
|
+
function buildOAuthState(csrfState, localRedirectUri) {
|
|
1119
|
+
return base64UrlEncode(
|
|
1120
|
+
new TextEncoder().encode(
|
|
1121
|
+
JSON.stringify({
|
|
1122
|
+
csrfState,
|
|
1123
|
+
localRedirectUri
|
|
1124
|
+
})
|
|
1125
|
+
)
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1098
1128
|
function generateCodeVerifier() {
|
|
1099
1129
|
return base64UrlEncode(webcrypto.getRandomValues(new Uint8Array(48)));
|
|
1100
1130
|
}
|
|
@@ -1128,11 +1158,13 @@ async function listenOnLoopbackPort() {
|
|
|
1128
1158
|
async function login(args) {
|
|
1129
1159
|
const authConfig = { resource: API_OAUTH_RESOURCE, scope: API_OAUTH_SCOPES.join(" ") };
|
|
1130
1160
|
const oauthClient = await resolveOAuthClient(authConfig);
|
|
1131
|
-
const
|
|
1161
|
+
const csrfState = randomState();
|
|
1132
1162
|
const codeVerifier = generateCodeVerifier();
|
|
1133
1163
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
1134
1164
|
const { server, port } = await listenOnLoopbackPort();
|
|
1135
|
-
const
|
|
1165
|
+
const localRedirectUri = `http://127.0.0.1:${port}/callback`;
|
|
1166
|
+
const state = buildOAuthState(csrfState, localRedirectUri);
|
|
1167
|
+
const redirectUri = new URL(OAUTH_CALLBACK_PATH, appBase()).toString();
|
|
1136
1168
|
const authUrl = new URL(OAUTH_AUTHORIZE_PATH, appBase());
|
|
1137
1169
|
authUrl.searchParams.set("client_id", oauthClient.clientId);
|
|
1138
1170
|
authUrl.searchParams.set("response_type", "code");
|
|
@@ -1155,7 +1187,7 @@ async function login(args) {
|
|
|
1155
1187
|
}, 5 * 60 * 1e3);
|
|
1156
1188
|
server.on("request", async (req, res) => {
|
|
1157
1189
|
try {
|
|
1158
|
-
const url = new URL(req.url || "/",
|
|
1190
|
+
const url = new URL(req.url || "/", localRedirectUri);
|
|
1159
1191
|
if (url.pathname !== "/callback") {
|
|
1160
1192
|
res.writeHead(404).end("Not found");
|
|
1161
1193
|
return;
|
|
@@ -1321,6 +1353,10 @@ async function main() {
|
|
|
1321
1353
|
printHelp();
|
|
1322
1354
|
return;
|
|
1323
1355
|
}
|
|
1356
|
+
if (command === "version") {
|
|
1357
|
+
writeLine(cliVersion());
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1324
1360
|
if (command === "login") return await login(args);
|
|
1325
1361
|
if (command === "logout") {
|
|
1326
1362
|
if (!hasStoredAuth()) {
|