chirpie 1.2.4 → 1.2.6
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/README.md +1 -1
- package/dist/index.js +74 -12
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ chirpie thread "One" "Two" "Three" -a 550e8400-e29b-41d4-a716-446655440000
|
|
|
80
80
|
# What is queued
|
|
81
81
|
chirpie posts --status scheduled
|
|
82
82
|
|
|
83
|
-
# Put the link in the first comment. X
|
|
83
|
+
# Put the link in the first comment. X only today (Threads, Instagram and Facebook coming soon),
|
|
84
84
|
# and it counts as one post against your monthly quota.
|
|
85
85
|
chirpie post "We rebuilt scheduling this week." \
|
|
86
86
|
--first-comment "Full write-up: https://example.com/blog/scheduling"
|
package/dist/index.js
CHANGED
|
@@ -967,6 +967,58 @@ postsCommand.command("unhide <id>").description("Put a hidden post back in your
|
|
|
967
967
|
|
|
968
968
|
// src/commands/accounts.ts
|
|
969
969
|
import { Command as Command6 } from "commander";
|
|
970
|
+
|
|
971
|
+
// src/lib/connect-input.ts
|
|
972
|
+
var BLUESKY_APP_PASSWORD_REQUIRED = "bluesky_app_password_required";
|
|
973
|
+
var BLUESKY_APP_PASSWORD_MESSAGE = "Bluesky needs an app password, not your account password. Create one in Bluesky under Settings, Privacy and Security, App Passwords (https://bsky.app/settings/app-passwords). It looks like xxxx-xxxx-xxxx-xxxx.";
|
|
974
|
+
var APP_PASSWORD = /^[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$/;
|
|
975
|
+
function normalizeBlueskyAppPassword(raw) {
|
|
976
|
+
return raw.trim();
|
|
977
|
+
}
|
|
978
|
+
function isBlueskyAppPassword(raw) {
|
|
979
|
+
return APP_PASSWORD.test(normalizeBlueskyAppPassword(raw));
|
|
980
|
+
}
|
|
981
|
+
var MASTODON_INSTANCE_INVALID = "mastodon_instance_invalid";
|
|
982
|
+
var MASTODON_INSTANCE_MESSAGE = "Enter your Mastodon server (mastodon.social), your handle (@you@mastodon.social) or your profile URL (https://mastodon.social/@you).";
|
|
983
|
+
var DOTTED_NAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/i;
|
|
984
|
+
var ADDRESS = /^[^@/\s]+@([^@/\s]+)$/;
|
|
985
|
+
var REMOTE_PROFILE_PATH = /^\/@[^@/]+@([^@/]+)(?:\/|$)/;
|
|
986
|
+
function toUrl(candidate) {
|
|
987
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(candidate) ? candidate : `https://${candidate}`;
|
|
988
|
+
try {
|
|
989
|
+
return new URL(withScheme);
|
|
990
|
+
} catch {
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function fromUrl(url) {
|
|
995
|
+
if (url.protocol !== "https:") return null;
|
|
996
|
+
if (!DOTTED_NAME.test(url.hostname)) return null;
|
|
997
|
+
return { origin: url.origin, host: url.host };
|
|
998
|
+
}
|
|
999
|
+
function parseMastodonInstance(raw) {
|
|
1000
|
+
const trimmed = raw.trim();
|
|
1001
|
+
if (!trimmed || /\s/.test(trimmed)) return null;
|
|
1002
|
+
const bare = trimmed.replace(/^@+/, "");
|
|
1003
|
+
const address = ADDRESS.exec(bare);
|
|
1004
|
+
const url = toUrl(address ? address[1] : bare);
|
|
1005
|
+
if (!url) return null;
|
|
1006
|
+
const remote = REMOTE_PROFILE_PATH.exec(url.pathname);
|
|
1007
|
+
if (!address && remote) {
|
|
1008
|
+
let decoded;
|
|
1009
|
+
try {
|
|
1010
|
+
decoded = decodeURIComponent(remote[1]);
|
|
1011
|
+
} catch {
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
1014
|
+
const home = toUrl(decoded);
|
|
1015
|
+
if (!home) return null;
|
|
1016
|
+
return fromUrl(home);
|
|
1017
|
+
}
|
|
1018
|
+
return fromUrl(url);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// src/commands/accounts.ts
|
|
970
1022
|
async function readSecretFromStdin(prompt) {
|
|
971
1023
|
const stdin = process.stdin;
|
|
972
1024
|
process.stderr.write(prompt);
|
|
@@ -1158,24 +1210,29 @@ accountsCommand.command("connect-x").description("Connect a new X/Twitter accoun
|
|
|
1158
1210
|
process.exit(1);
|
|
1159
1211
|
}
|
|
1160
1212
|
});
|
|
1161
|
-
accountsCommand.command("connect-bluesky").description(
|
|
1213
|
+
accountsCommand.command("connect-bluesky").description(
|
|
1214
|
+
"Connect a Bluesky account with an app password (xxxx-xxxx-xxxx-xxxx), never your account password"
|
|
1215
|
+
).requiredOption(
|
|
1162
1216
|
"--handle <handle>",
|
|
1163
1217
|
"Bluesky handle. The first part alone is enough (user), and user.bsky.social, a handle on your own domain, or the account email also work"
|
|
1164
|
-
).option(
|
|
1218
|
+
).option(
|
|
1219
|
+
"--app-password <password>",
|
|
1220
|
+
"Bluesky app password, shaped xxxx-xxxx-xxxx-xxxx. Create one in Bluesky under Settings, Privacy and Security, App Passwords (omit to enter securely via prompt)"
|
|
1221
|
+
).option("--json", "Output raw JSON").action(async (opts, cmd) => {
|
|
1165
1222
|
const client = getClient();
|
|
1166
1223
|
try {
|
|
1167
1224
|
let appPassword = opts.appPassword;
|
|
1168
1225
|
if (!appPassword) {
|
|
1169
|
-
appPassword = await readSecretFromStdin("App
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1226
|
+
appPassword = await readSecretFromStdin("App password (xxxx-xxxx-xxxx-xxxx): ");
|
|
1227
|
+
}
|
|
1228
|
+
if (!appPassword || !isBlueskyAppPassword(appPassword)) {
|
|
1229
|
+
error(`${BLUESKY_APP_PASSWORD_REQUIRED}: ${BLUESKY_APP_PASSWORD_MESSAGE}`);
|
|
1230
|
+
process.exit(1);
|
|
1174
1231
|
}
|
|
1175
1232
|
const result = await client.connectBlueskyAccount({
|
|
1176
1233
|
platform: "bluesky",
|
|
1177
1234
|
identifier: opts.handle,
|
|
1178
|
-
app_password: appPassword
|
|
1235
|
+
app_password: normalizeBlueskyAppPassword(appPassword)
|
|
1179
1236
|
});
|
|
1180
1237
|
if (wantsJson(opts, cmd)) {
|
|
1181
1238
|
json(result);
|
|
@@ -1183,7 +1240,7 @@ accountsCommand.command("connect-bluesky").description("Connect a Bluesky accoun
|
|
|
1183
1240
|
info(`Connected Bluesky account: ${result.username} (${result.platform_user_id})`);
|
|
1184
1241
|
}
|
|
1185
1242
|
} catch (err) {
|
|
1186
|
-
|
|
1243
|
+
apiError(err, "Failed to connect Bluesky account");
|
|
1187
1244
|
process.exit(1);
|
|
1188
1245
|
}
|
|
1189
1246
|
});
|
|
@@ -1232,13 +1289,18 @@ accountsCommand.command("connect-threads").description("Connect a Threads (Meta)
|
|
|
1232
1289
|
});
|
|
1233
1290
|
accountsCommand.command("connect-mastodon").description("Connect a Mastodon account via OAuth").requiredOption(
|
|
1234
1291
|
"--instance <instance>",
|
|
1235
|
-
"Mastodon
|
|
1292
|
+
"Your Mastodon server, handle or profile URL: mastodon.social, @you@mastodon.social and https://mastodon.social/@you all work. Only the server is kept"
|
|
1236
1293
|
).option("--json", "Output raw JSON").action(async (opts, cmd) => {
|
|
1294
|
+
const instance = parseMastodonInstance(String(opts.instance ?? ""));
|
|
1295
|
+
if (!instance) {
|
|
1296
|
+
error(`${MASTODON_INSTANCE_INVALID}: ${MASTODON_INSTANCE_MESSAGE}`);
|
|
1297
|
+
process.exit(1);
|
|
1298
|
+
}
|
|
1237
1299
|
const client = getClient();
|
|
1238
1300
|
try {
|
|
1239
1301
|
const result = await client.connectMastodonAccount({
|
|
1240
1302
|
platform: "mastodon",
|
|
1241
|
-
instance_url:
|
|
1303
|
+
instance_url: instance.origin
|
|
1242
1304
|
});
|
|
1243
1305
|
if (wantsJson(opts, cmd)) {
|
|
1244
1306
|
json(result);
|
|
@@ -1249,7 +1311,7 @@ accountsCommand.command("connect-mastodon").description("Connect a Mastodon acco
|
|
|
1249
1311
|
`);
|
|
1250
1312
|
}
|
|
1251
1313
|
} catch (err) {
|
|
1252
|
-
|
|
1314
|
+
apiError(err, "Failed to start Mastodon connection");
|
|
1253
1315
|
process.exit(1);
|
|
1254
1316
|
}
|
|
1255
1317
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chirpie",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Chirpie CLI: post to social media from the command line",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"license": "MIT",
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@chirpie/sdk": "^1.2.
|
|
47
|
+
"@chirpie/sdk": "^1.2.6",
|
|
48
48
|
"commander": "^13"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|