tangled
alpha
login
or
join now
stevedylan.dev
/
sequoia
A CLI for publishing standard.site documents to ATProto
sequoia.pub
standard
site
lexicon
cli
publishing
30
fork
atom
overview
issues
5
pulls
1
pipelines
fix: fixed issue with auth selection in init command
stevedylan.dev
5 days ago
bd2cce47
61ecaa1d
1/1
lint.yml
success
4s
+77
-4
3 changed files
expand all
collapse all
unified
split
.gitignore
packages
cli
src
commands
init.ts
lib
credential-select.ts
+1
.gitignore
···
35
35
36
36
# Bun lockfile - keep but binary cache
37
37
bun.lockb
38
38
+
packages/ui
+21
-4
packages/cli/src/commands/init.ts
···
13
13
} from "@clack/prompts";
14
14
import * as path from "node:path";
15
15
import { findConfig, generateConfigTemplate } from "../lib/config";
16
16
-
import { loadCredentials } from "../lib/credentials";
16
16
+
import { loadCredentials, listAllCredentials } from "../lib/credentials";
17
17
import { createAgent, createPublication } from "../lib/atproto";
18
18
+
import { selectCredential } from "../lib/credential-select";
18
19
import type { FrontmatterMapping, BlueskyConfig } from "../lib/types";
19
20
20
21
async function fileExists(filePath: string): Promise<boolean> {
···
186
187
}
187
188
188
189
let publicationUri: string;
189
189
-
const credentials = await loadCredentials();
190
190
+
let credentials = await loadCredentials();
190
191
191
192
if (publicationChoice === "create") {
192
193
// Need credentials to create a publication
193
194
if (!credentials) {
195
195
+
// Check if there are multiple identities - if so, prompt to select
196
196
+
const allCredentials = await listAllCredentials();
197
197
+
if (allCredentials.length > 1) {
198
198
+
credentials = await selectCredential(allCredentials);
199
199
+
} else if (allCredentials.length === 1) {
200
200
+
// Single credential exists but couldn't be loaded - try to load it explicitly
201
201
+
credentials = await selectCredential(allCredentials);
202
202
+
} else {
203
203
+
log.error(
204
204
+
"You must authenticate first. Run 'sequoia login' (recommended) or 'sequoia auth' before creating a publication.",
205
205
+
);
206
206
+
process.exit(1);
207
207
+
}
208
208
+
}
209
209
+
210
210
+
if (!credentials) {
194
211
log.error(
195
195
-
"You must authenticate first. Run 'sequoia auth' before creating a publication.",
212
212
+
"Could not load credentials. Try running 'sequoia login' again to re-authenticate.",
196
213
);
197
214
process.exit(1);
198
215
}
···
206
223
} catch (_error) {
207
224
s.stop("Failed to connect");
208
225
log.error(
209
209
-
"Failed to connect. Check your credentials with 'sequoia auth'.",
226
226
+
"Failed to connect. Try re-authenticating with 'sequoia login' or 'sequoia auth'.",
210
227
);
211
228
process.exit(1);
212
229
}
+55
packages/cli/src/lib/credential-select.ts
···
1
1
+
import { select } from "@clack/prompts";
2
2
+
import { getOAuthHandle, getOAuthSession } from "./oauth-store";
3
3
+
import { getCredentials } from "./credentials";
4
4
+
import type { Credentials } from "./types";
5
5
+
import { exitOnCancel } from "./prompts";
6
6
+
7
7
+
/**
8
8
+
* Prompt user to select from multiple credentials
9
9
+
*/
10
10
+
export async function selectCredential(
11
11
+
allCredentials: Array<{ id: string; type: "app-password" | "oauth" }>,
12
12
+
): Promise<Credentials | null> {
13
13
+
// Build options with friendly labels
14
14
+
const options = await Promise.all(
15
15
+
allCredentials.map(async ({ id, type }) => {
16
16
+
let label = id;
17
17
+
if (type === "oauth") {
18
18
+
const handle = await getOAuthHandle(id);
19
19
+
label = handle ? `${handle} (${id})` : id;
20
20
+
}
21
21
+
return {
22
22
+
value: { id, type },
23
23
+
label: `${label} [${type}]`,
24
24
+
};
25
25
+
}),
26
26
+
);
27
27
+
28
28
+
const selected = exitOnCancel(
29
29
+
await select({
30
30
+
message: "Multiple identities found. Select one:",
31
31
+
options,
32
32
+
}),
33
33
+
);
34
34
+
35
35
+
// Load the full credentials for the selected identity
36
36
+
if (selected.type === "oauth") {
37
37
+
const session = await getOAuthSession(selected.id);
38
38
+
if (session) {
39
39
+
const handle = await getOAuthHandle(selected.id);
40
40
+
return {
41
41
+
type: "oauth",
42
42
+
did: selected.id,
43
43
+
handle: handle || selected.id,
44
44
+
pdsUrl: "https://bsky.social",
45
45
+
};
46
46
+
}
47
47
+
} else {
48
48
+
const creds = await getCredentials(selected.id);
49
49
+
if (creds) {
50
50
+
return creds;
51
51
+
}
52
52
+
}
53
53
+
54
54
+
return null;
55
55
+
}