sequoia-cli 0.3.0 → 0.3.2

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/dist/index.js +178 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -99159,6 +99159,25 @@ async function deleteOAuthSession(did) {
99159
99159
  function getOAuthStorePath() {
99160
99160
  return OAUTH_FILE;
99161
99161
  }
99162
+ async function setOAuthHandle(did, handle) {
99163
+ const store = await loadOAuthStore();
99164
+ if (!store.handles) {
99165
+ store.handles = {};
99166
+ }
99167
+ store.handles[did] = handle;
99168
+ await saveOAuthStore(store);
99169
+ }
99170
+ async function getOAuthHandle(did) {
99171
+ const store = await loadOAuthStore();
99172
+ return store.handles?.[did];
99173
+ }
99174
+ async function listOAuthSessionsWithHandles() {
99175
+ const store = await loadOAuthStore();
99176
+ return Object.keys(store.sessions).map((did) => ({
99177
+ did,
99178
+ handle: store.handles?.[did]
99179
+ }));
99180
+ }
99162
99181
 
99163
99182
  // src/lib/oauth-client.ts
99164
99183
  var CALLBACK_PORT = 4000;
@@ -99330,9 +99349,17 @@ async function uploadImage(agent, imagePath) {
99330
99349
  }
99331
99350
  }
99332
99351
  async function resolveImagePath(ogImage, imagesDir, contentDir) {
99333
- const filename = path4.basename(ogImage);
99334
99352
  if (imagesDir) {
99335
- const imagePath = path4.join(imagesDir, filename);
99353
+ const imagesDirBaseName = path4.basename(imagesDir);
99354
+ const imagesDirIndex = ogImage.indexOf(imagesDirBaseName);
99355
+ let relativePath;
99356
+ if (imagesDirIndex !== -1) {
99357
+ const afterImagesDir = ogImage.substring(imagesDirIndex + imagesDirBaseName.length);
99358
+ relativePath = afterImagesDir.replace(/^[/\\]/, "");
99359
+ } else {
99360
+ relativePath = path4.basename(ogImage);
99361
+ }
99362
+ const imagePath = path4.join(imagesDir, relativePath);
99336
99363
  if (await fileExists2(imagePath)) {
99337
99364
  const stat2 = await fs3.stat(imagePath);
99338
99365
  if (stat2.size > 0) {
@@ -99727,14 +99754,25 @@ async function tryLoadOAuthCredentials(profile) {
99727
99754
  if (profile.startsWith("did:")) {
99728
99755
  const session = await getOAuthSession(profile);
99729
99756
  if (session) {
99757
+ const handle = await getOAuthHandle(profile);
99730
99758
  return {
99731
99759
  type: "oauth",
99732
99760
  did: profile,
99733
- handle: profile,
99761
+ handle: handle || profile,
99734
99762
  pdsUrl: "https://bsky.social"
99735
99763
  };
99736
99764
  }
99737
99765
  }
99766
+ const sessions = await listOAuthSessionsWithHandles();
99767
+ const match2 = sessions.find((s) => s.handle === profile);
99768
+ if (match2) {
99769
+ return {
99770
+ type: "oauth",
99771
+ did: match2.did,
99772
+ handle: match2.handle || match2.did,
99773
+ pdsUrl: "https://bsky.social"
99774
+ };
99775
+ }
99738
99776
  return null;
99739
99777
  }
99740
99778
  async function loadCredentials(projectIdentity) {
@@ -99779,10 +99817,11 @@ async function loadCredentials(projectIdentity) {
99779
99817
  if (oauthDids.length === 1 && oauthDids[0]) {
99780
99818
  const session = await getOAuthSession(oauthDids[0]);
99781
99819
  if (session) {
99820
+ const handle = await getOAuthHandle(oauthDids[0]);
99782
99821
  return {
99783
99822
  type: "oauth",
99784
99823
  did: oauthDids[0],
99785
- handle: oauthDids[0],
99824
+ handle: handle || oauthDids[0],
99786
99825
  pdsUrl: "https://bsky.social"
99787
99826
  };
99788
99827
  }
@@ -99801,6 +99840,18 @@ async function listCredentials() {
99801
99840
  const store = await loadCredentialsStore();
99802
99841
  return Object.keys(store);
99803
99842
  }
99843
+ async function listAllCredentials() {
99844
+ const store = await loadCredentialsStore();
99845
+ const oauthDids = await listOAuthSessions();
99846
+ const result = [];
99847
+ for (const id of Object.keys(store)) {
99848
+ result.push({ id, type: "app-password" });
99849
+ }
99850
+ for (const did of oauthDids) {
99851
+ result.push({ id: did, type: "oauth" });
99852
+ }
99853
+ return result;
99854
+ }
99804
99855
  async function saveCredentials(credentials) {
99805
99856
  const store = await loadCredentialsStore();
99806
99857
  store[credentials.identifier] = credentials;
@@ -100074,6 +100125,43 @@ async function saveState(configDir, state) {
100074
100125
  await fs5.writeFile(statePath, JSON.stringify(state, null, 2));
100075
100126
  }
100076
100127
 
100128
+ // src/lib/credential-select.ts
100129
+ async function selectCredential(allCredentials) {
100130
+ const options = await Promise.all(allCredentials.map(async ({ id, type }) => {
100131
+ let label = id;
100132
+ if (type === "oauth") {
100133
+ const handle = await getOAuthHandle(id);
100134
+ label = handle ? `${handle} (${id})` : id;
100135
+ }
100136
+ return {
100137
+ value: { id, type },
100138
+ label: `${label} [${type}]`
100139
+ };
100140
+ }));
100141
+ const selected = exitOnCancel(await qt({
100142
+ message: "Multiple identities found. Select one:",
100143
+ options
100144
+ }));
100145
+ if (selected.type === "oauth") {
100146
+ const session = await getOAuthSession(selected.id);
100147
+ if (session) {
100148
+ const handle = await getOAuthHandle(selected.id);
100149
+ return {
100150
+ type: "oauth",
100151
+ did: selected.id,
100152
+ handle: handle || selected.id,
100153
+ pdsUrl: "https://bsky.social"
100154
+ };
100155
+ }
100156
+ } else {
100157
+ const creds = await getCredentials(selected.id);
100158
+ if (creds) {
100159
+ return creds;
100160
+ }
100161
+ }
100162
+ return null;
100163
+ }
100164
+
100077
100165
  // src/commands/init.ts
100078
100166
  async function fileExists5(filePath) {
100079
100167
  try {
@@ -100202,10 +100290,21 @@ var initCommand = import_cmd_ts2.command({
100202
100290
  onCancel();
100203
100291
  }
100204
100292
  let publicationUri;
100205
- const credentials = await loadCredentials();
100293
+ let credentials = await loadCredentials();
100206
100294
  if (publicationChoice === "create") {
100207
100295
  if (!credentials) {
100208
- R2.error("You must authenticate first. Run 'sequoia auth' before creating a publication.");
100296
+ const allCredentials = await listAllCredentials();
100297
+ if (allCredentials.length > 1) {
100298
+ credentials = await selectCredential(allCredentials);
100299
+ } else if (allCredentials.length === 1) {
100300
+ credentials = await selectCredential(allCredentials);
100301
+ } else {
100302
+ R2.error("You must authenticate first. Run 'sequoia login' (recommended) or 'sequoia auth' before creating a publication.");
100303
+ process.exit(1);
100304
+ }
100305
+ }
100306
+ if (!credentials) {
100307
+ R2.error("Could not load credentials. Try running 'sequoia login' again to re-authenticate.");
100209
100308
  process.exit(1);
100210
100309
  }
100211
100310
  const s = Ie();
@@ -100216,7 +100315,7 @@ var initCommand = import_cmd_ts2.command({
100216
100315
  s.stop("Connected!");
100217
100316
  } catch (_error) {
100218
100317
  s.stop("Failed to connect");
100219
- R2.error("Failed to connect. Check your credentials with 'sequoia auth'.");
100318
+ R2.error("Failed to connect. Try re-authenticating with 'sequoia login' or 'sequoia auth'.");
100220
100319
  process.exit(1);
100221
100320
  }
100222
100321
  const publicationConfig = await Rt({
@@ -100491,13 +100590,13 @@ var loginCommand = import_cmd_ts4.command({
100491
100590
  },
100492
100591
  handler: async ({ logout, list }) => {
100493
100592
  if (list) {
100494
- const sessions = await listOAuthSessions();
100593
+ const sessions = await listOAuthSessionsWithHandles();
100495
100594
  if (sessions.length === 0) {
100496
100595
  R2.info("No OAuth sessions stored");
100497
100596
  } else {
100498
100597
  R2.info("OAuth sessions:");
100499
- for (const did2 of sessions) {
100500
- console.log(` - ${did2}`);
100598
+ for (const { did: did2, handle: handle2 } of sessions) {
100599
+ console.log(` - ${handle2 || did2} (${did2})`);
100501
100600
  }
100502
100601
  }
100503
100602
  return;
@@ -100591,12 +100690,11 @@ var loginCommand = import_cmd_ts4.command({
100591
100690
  }
100592
100691
  s.message("Completing authentication...");
100593
100692
  const { session } = await client.callback(new URLSearchParams(result.params));
100594
- let displayName = handle;
100595
- try {
100596
- displayName = handle.startsWith("did:") ? session.did : handle;
100597
- } catch {
100598
- displayName = session.did;
100693
+ const handleToStore = handle.startsWith("did:") ? undefined : handle;
100694
+ if (handleToStore) {
100695
+ await setOAuthHandle(session.did, handleToStore);
100599
100696
  }
100697
+ const displayName = handleToStore || session.did;
100600
100698
  s.stop(`Logged in as ${displayName}`);
100601
100699
  R2.success(`OAuth session saved to ${getOAuthStorePath()}`);
100602
100700
  R2.info("Your session will refresh automatically when needed.");
@@ -100727,23 +100825,51 @@ var publishCommand = import_cmd_ts5.command({
100727
100825
  R2.info(`Content directory: ${config.contentDir}`);
100728
100826
  let credentials = await loadCredentials(config.identity);
100729
100827
  if (!credentials) {
100730
- const identities = await listCredentials();
100828
+ const identities = await listAllCredentials();
100731
100829
  if (identities.length === 0) {
100732
- R2.error("No credentials found. Run 'sequoia auth' first.");
100830
+ R2.error("No credentials found. Run 'sequoia login' or 'sequoia auth' first.");
100733
100831
  R2.info("Or set ATP_IDENTIFIER and ATP_APP_PASSWORD environment variables.");
100734
100832
  process.exit(1);
100735
100833
  }
100834
+ const options = await Promise.all(identities.map(async (cred) => {
100835
+ if (cred.type === "oauth") {
100836
+ const handle = await getOAuthHandle(cred.id);
100837
+ return {
100838
+ value: cred.id,
100839
+ label: `${handle || cred.id} (OAuth)`
100840
+ };
100841
+ }
100842
+ return {
100843
+ value: cred.id,
100844
+ label: `${cred.id} (App Password)`
100845
+ };
100846
+ }));
100736
100847
  R2.info("Multiple identities found. Select one to use:");
100737
100848
  const selected = exitOnCancel(await qt({
100738
100849
  message: "Identity:",
100739
- options: identities.map((id) => ({ value: id, label: id }))
100850
+ options
100740
100851
  }));
100741
- credentials = await getCredentials(selected);
100852
+ const selectedCred = identities.find((c) => c.id === selected);
100853
+ if (selectedCred?.type === "oauth") {
100854
+ const session = await getOAuthSession(selected);
100855
+ if (session) {
100856
+ const handle = await getOAuthHandle(selected);
100857
+ credentials = {
100858
+ type: "oauth",
100859
+ did: selected,
100860
+ handle: handle || selected,
100861
+ pdsUrl: "https://bsky.social"
100862
+ };
100863
+ }
100864
+ } else {
100865
+ credentials = await getCredentials(selected);
100866
+ }
100742
100867
  if (!credentials) {
100743
100868
  R2.error("Failed to load selected credentials.");
100744
100869
  process.exit(1);
100745
100870
  }
100746
- R2.info(`Tip: Add "identity": "${selected}" to sequoia.json to use this by default.`);
100871
+ const displayId = credentials.type === "oauth" ? credentials.handle || credentials.did : credentials.identifier;
100872
+ R2.info(`Tip: Add "identity": "${displayId}" to sequoia.json to use this by default.`);
100747
100873
  }
100748
100874
  const contentDir = path10.isAbsolute(config.contentDir) ? config.contentDir : path10.join(configDir, config.contentDir);
100749
100875
  const imagesDir = config.imagesDir ? path10.isAbsolute(config.imagesDir) ? config.imagesDir : path10.join(configDir, config.imagesDir) : undefined;
@@ -100969,17 +101095,44 @@ var syncCommand = import_cmd_ts6.command({
100969
101095
  R2.info(`Publication: ${config.publicationUri}`);
100970
101096
  let credentials = await loadCredentials(config.identity);
100971
101097
  if (!credentials) {
100972
- const identities = await listCredentials();
101098
+ const identities = await listAllCredentials();
100973
101099
  if (identities.length === 0) {
100974
- R2.error("No credentials found. Run 'sequoia auth' first.");
101100
+ R2.error("No credentials found. Run 'sequoia login' or 'sequoia auth' first.");
100975
101101
  process.exit(1);
100976
101102
  }
101103
+ const options = await Promise.all(identities.map(async (cred) => {
101104
+ if (cred.type === "oauth") {
101105
+ const handle = await getOAuthHandle(cred.id);
101106
+ return {
101107
+ value: cred.id,
101108
+ label: `${handle || cred.id} (OAuth)`
101109
+ };
101110
+ }
101111
+ return {
101112
+ value: cred.id,
101113
+ label: `${cred.id} (App Password)`
101114
+ };
101115
+ }));
100977
101116
  R2.info("Multiple identities found. Select one to use:");
100978
101117
  const selected = exitOnCancel(await qt({
100979
101118
  message: "Identity:",
100980
- options: identities.map((id) => ({ value: id, label: id }))
101119
+ options
100981
101120
  }));
100982
- credentials = await getCredentials(selected);
101121
+ const selectedCred = identities.find((c) => c.id === selected);
101122
+ if (selectedCred?.type === "oauth") {
101123
+ const session = await getOAuthSession(selected);
101124
+ if (session) {
101125
+ const handle = await getOAuthHandle(selected);
101126
+ credentials = {
101127
+ type: "oauth",
101128
+ did: selected,
101129
+ handle: handle || selected,
101130
+ pdsUrl: "https://bsky.social"
101131
+ };
101132
+ }
101133
+ } else {
101134
+ credentials = await getCredentials(selected);
101135
+ }
100983
101136
  if (!credentials) {
100984
101137
  R2.error("Failed to load selected credentials.");
100985
101138
  process.exit(1);
@@ -101501,7 +101654,7 @@ Publish evergreen content to the ATmosphere
101501
101654
 
101502
101655
  > https://tangled.org/stevedylan.dev/sequoia
101503
101656
  `,
101504
- version: "0.3.0",
101657
+ version: "0.3.2",
101505
101658
  cmds: {
101506
101659
  auth: authCommand,
101507
101660
  init: initCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sequoia-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sequoia": "dist/index.js"