@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.
Files changed (2) hide show
  1. package/bin/clipdone.js +42 -6
  2. 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 = 2;
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: OAUTH_LOOPBACK_PORTS.map((port) => `http://127.0.0.1:${port}/callback`),
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 state = randomState();
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 redirectUri = `http://127.0.0.1:${port}/callback`;
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 || "/", redirectUri);
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()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipdone/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Command line access to ClipDone projects and exports",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",