fluncle 0.65.0 → 0.66.0
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/fluncle.mjs +287 -34
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -423,7 +423,7 @@ function parseVersion(version) {
|
|
|
423
423
|
var currentVersion;
|
|
424
424
|
var init_version = __esm(() => {
|
|
425
425
|
init_output();
|
|
426
|
-
currentVersion = "0.
|
|
426
|
+
currentVersion = "0.66.0".trim() ? "0.66.0".trim() : "0.1.0";
|
|
427
427
|
});
|
|
428
428
|
|
|
429
429
|
// src/update-notifier.ts
|
|
@@ -657,7 +657,68 @@ var init_env = __esm(() => {
|
|
|
657
657
|
envProfiles2 = ["local", "production"];
|
|
658
658
|
});
|
|
659
659
|
|
|
660
|
+
// src/user-token.ts
|
|
661
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
662
|
+
import { homedir as homedir3 } from "node:os";
|
|
663
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
664
|
+
function activeProfile() {
|
|
665
|
+
const profile = process.env.FLUNCLE_ENV ?? defaultEnvProfile2;
|
|
666
|
+
return envProfiles3.includes(profile) ? profile : defaultEnvProfile2;
|
|
667
|
+
}
|
|
668
|
+
function userTokenPath() {
|
|
669
|
+
return join3(homedir3(), ".config", "fluncle", `user.${activeProfile()}.json`);
|
|
670
|
+
}
|
|
671
|
+
function isStoredUserToken(value) {
|
|
672
|
+
if (typeof value !== "object" || value === null) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
const record = value;
|
|
676
|
+
return typeof record.token === "string" && typeof record.baseUrl === "string";
|
|
677
|
+
}
|
|
678
|
+
function readUserToken() {
|
|
679
|
+
try {
|
|
680
|
+
const parsed = JSON.parse(readFileSync(userTokenPath(), "utf8"));
|
|
681
|
+
return isStoredUserToken(parsed) ? parsed : undefined;
|
|
682
|
+
} catch {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function writeUserToken(value) {
|
|
687
|
+
const path2 = userTokenPath();
|
|
688
|
+
mkdirSync(dirname2(path2), { recursive: true });
|
|
689
|
+
writeFileSync(path2, `${JSON.stringify(value, null, 2)}
|
|
690
|
+
`, { mode: 384 });
|
|
691
|
+
}
|
|
692
|
+
function clearUserToken() {
|
|
693
|
+
const path2 = userTokenPath();
|
|
694
|
+
try {
|
|
695
|
+
readFileSync(path2);
|
|
696
|
+
} catch {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
rmSync(path2, { force: true });
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
var envProfiles3, defaultEnvProfile2 = "production";
|
|
703
|
+
var init_user_token = __esm(() => {
|
|
704
|
+
envProfiles3 = ["local", "production"];
|
|
705
|
+
});
|
|
706
|
+
|
|
660
707
|
// src/api.ts
|
|
708
|
+
async function userApiGet(path2) {
|
|
709
|
+
return apiRequest(path2, {
|
|
710
|
+
headers: userHeaders()
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function userHeaders() {
|
|
714
|
+
const stored = readUserToken();
|
|
715
|
+
if (!stored) {
|
|
716
|
+
throw new CliError2("not_logged_in", "You're not signed in. Run `fluncle login` to link this device to your account.");
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
Authorization: `Bearer ${stored.token}`
|
|
720
|
+
};
|
|
721
|
+
}
|
|
661
722
|
async function publicApiGet(path2) {
|
|
662
723
|
return apiRequest(path2);
|
|
663
724
|
}
|
|
@@ -747,6 +808,7 @@ function parseJson(text) {
|
|
|
747
808
|
var init_api = __esm(() => {
|
|
748
809
|
init_env();
|
|
749
810
|
init_output();
|
|
811
|
+
init_user_token();
|
|
750
812
|
});
|
|
751
813
|
|
|
752
814
|
// src/commands/recent.ts
|
|
@@ -1163,7 +1225,7 @@ __export(exports_mixtapes, {
|
|
|
1163
1225
|
mixtapeDeleteCommand: () => mixtapeDeleteCommand,
|
|
1164
1226
|
mixtapeCreateCommand: () => mixtapeCreateCommand
|
|
1165
1227
|
});
|
|
1166
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1228
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
1167
1229
|
async function mixtapesCommand() {
|
|
1168
1230
|
const response = await publicApiGet("/api/mixtapes");
|
|
1169
1231
|
return response.mixtapes;
|
|
@@ -1294,7 +1356,7 @@ function parseCueFile(filePath) {
|
|
|
1294
1356
|
if (!existsSync(filePath)) {
|
|
1295
1357
|
throw new CliError2("file_not_found", `Cue file not found: ${filePath}`);
|
|
1296
1358
|
}
|
|
1297
|
-
const text =
|
|
1359
|
+
const text = readFileSync2(filePath, "utf-8");
|
|
1298
1360
|
const trimmed = text.trim();
|
|
1299
1361
|
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
1300
1362
|
try {
|
|
@@ -1505,6 +1567,36 @@ var init_interactive = __esm(() => {
|
|
|
1505
1567
|
// src/links.ts
|
|
1506
1568
|
var spotifyPlaylistUrl2 = "https://open.spotify.com/playlist/1m5LADqpLjiBERdtqrIiL0", telegramUrl2 = "https://t.me/fluncle";
|
|
1507
1569
|
|
|
1570
|
+
// src/open-external.ts
|
|
1571
|
+
async function openExternal(target) {
|
|
1572
|
+
const command = platformOpenCommand();
|
|
1573
|
+
if (!command) {
|
|
1574
|
+
console.log(target);
|
|
1575
|
+
throw new CliError2("unsupported_platform", `Automatic opening is only supported on macOS and Linux. Open this manually: ${target}`);
|
|
1576
|
+
}
|
|
1577
|
+
const child = Bun.spawn([command, target], {
|
|
1578
|
+
stderr: "pipe",
|
|
1579
|
+
stdout: "pipe"
|
|
1580
|
+
});
|
|
1581
|
+
const exitCode = await child.exited;
|
|
1582
|
+
if (exitCode !== 0) {
|
|
1583
|
+
const stderr = await new Response(child.stderr).text();
|
|
1584
|
+
throw new CliError2("open_failed", stderr.trim() || `Could not open ${target} with ${command}`);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
function platformOpenCommand() {
|
|
1588
|
+
if (process.platform === "darwin") {
|
|
1589
|
+
return "open";
|
|
1590
|
+
}
|
|
1591
|
+
if (process.platform === "linux") {
|
|
1592
|
+
return "xdg-open";
|
|
1593
|
+
}
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
var init_open_external = __esm(() => {
|
|
1597
|
+
init_output();
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1508
1600
|
// src/commands/recent.ts
|
|
1509
1601
|
function mapTrack2(track) {
|
|
1510
1602
|
if (track.type === "mixtape") {
|
|
@@ -1598,31 +1690,6 @@ function spotifyPlaylistUrlToAppUri(playlistUrl) {
|
|
|
1598
1690
|
}
|
|
1599
1691
|
return `spotify:playlist:${playlistId}`;
|
|
1600
1692
|
}
|
|
1601
|
-
async function openExternal(target) {
|
|
1602
|
-
const command = platformOpenCommand();
|
|
1603
|
-
if (!command) {
|
|
1604
|
-
console.log(target);
|
|
1605
|
-
throw new CliError2("unsupported_platform", `Automatic opening is only supported on macOS and Linux. Open this manually: ${target}`);
|
|
1606
|
-
}
|
|
1607
|
-
const child = Bun.spawn([command, target], {
|
|
1608
|
-
stderr: "pipe",
|
|
1609
|
-
stdout: "pipe"
|
|
1610
|
-
});
|
|
1611
|
-
const exitCode = await child.exited;
|
|
1612
|
-
if (exitCode !== 0) {
|
|
1613
|
-
const stderr = await new Response(child.stderr).text();
|
|
1614
|
-
throw new CliError2("open_failed", stderr.trim() || `Could not open ${target} with ${command}`);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
function platformOpenCommand() {
|
|
1618
|
-
if (process.platform === "darwin") {
|
|
1619
|
-
return "open";
|
|
1620
|
-
}
|
|
1621
|
-
if (process.platform === "linux") {
|
|
1622
|
-
return "xdg-open";
|
|
1623
|
-
}
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
1693
|
async function selectRecentTrack(tracks) {
|
|
1627
1694
|
return await selectWithKeyboard(tracks, {
|
|
1628
1695
|
nonInteractiveMessage: SELECT_NON_INTERACTIVE_MESSAGE,
|
|
@@ -1649,6 +1716,7 @@ var TELEGRAM_APP_URI = "tg://resolve?domain=fluncle", SPOTIFY_PLAYLIST_OPEN_URL,
|
|
|
1649
1716
|
var init_open = __esm(() => {
|
|
1650
1717
|
init_format();
|
|
1651
1718
|
init_interactive();
|
|
1719
|
+
init_open_external();
|
|
1652
1720
|
init_output();
|
|
1653
1721
|
init_recent2();
|
|
1654
1722
|
SPOTIFY_PLAYLIST_OPEN_URL = `${spotifyPlaylistUrl2}?si=054d3c6cbcf14a36`;
|
|
@@ -1756,6 +1824,162 @@ var init_submit = __esm(() => {
|
|
|
1756
1824
|
init_output();
|
|
1757
1825
|
});
|
|
1758
1826
|
|
|
1827
|
+
// src/commands/login.ts
|
|
1828
|
+
var exports_login = {};
|
|
1829
|
+
__export(exports_login, {
|
|
1830
|
+
logoutCommand: () => logoutCommand,
|
|
1831
|
+
loginCommand: () => loginCommand
|
|
1832
|
+
});
|
|
1833
|
+
async function loginCommand() {
|
|
1834
|
+
const baseUrl = getApiBaseUrl();
|
|
1835
|
+
const existing = readUserToken();
|
|
1836
|
+
if (existing) {
|
|
1837
|
+
const who2 = existing.user?.username ?? existing.user?.name ?? "your account";
|
|
1838
|
+
console.log(`Already signed in as ${who2}. Run \`fluncle logout\` first to switch accounts.`);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
const device = await requestDeviceCode(baseUrl);
|
|
1842
|
+
const verificationUrl = device.verification_uri_complete ?? device.verification_uri;
|
|
1843
|
+
console.log(`Linking this device to your Fluncle account.
|
|
1844
|
+
`);
|
|
1845
|
+
console.log(` Code: ${device.user_code}`);
|
|
1846
|
+
console.log(` Open: ${verificationUrl}
|
|
1847
|
+
`);
|
|
1848
|
+
console.log("Opening your browser to approve… (sign in if you aren't already)");
|
|
1849
|
+
try {
|
|
1850
|
+
await openExternal(verificationUrl);
|
|
1851
|
+
} catch {
|
|
1852
|
+
console.log("Couldn't open a browser automatically — visit the URL above to approve.");
|
|
1853
|
+
}
|
|
1854
|
+
const token = await pollForToken(baseUrl, device);
|
|
1855
|
+
const user = await fetchSessionUser(baseUrl, token.access_token);
|
|
1856
|
+
writeUserToken({
|
|
1857
|
+
baseUrl,
|
|
1858
|
+
token: token.access_token,
|
|
1859
|
+
user
|
|
1860
|
+
});
|
|
1861
|
+
const who = user?.username ?? user?.name ?? "cosmonaut";
|
|
1862
|
+
console.log(`
|
|
1863
|
+
Aboard, ${who}. This device is linked to your Galaxy.`);
|
|
1864
|
+
console.log("Try `fluncle me` to see your progress.");
|
|
1865
|
+
}
|
|
1866
|
+
async function logoutCommand() {
|
|
1867
|
+
const stored = readUserToken();
|
|
1868
|
+
if (stored) {
|
|
1869
|
+
await revokeSession(stored.baseUrl, stored.token).catch(() => {
|
|
1870
|
+
return;
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
const cleared = clearUserToken();
|
|
1874
|
+
console.log(cleared ? "Signed out. This device is no longer linked to your account." : "Not signed in — nothing to do.");
|
|
1875
|
+
}
|
|
1876
|
+
async function requestDeviceCode(baseUrl) {
|
|
1877
|
+
const response = await fetch(`${baseUrl}/api/auth/device/code`, {
|
|
1878
|
+
body: JSON.stringify({ client_id: CLI_CLIENT_ID, scope: DEVICE_SCOPE }),
|
|
1879
|
+
headers: { "Content-Type": "application/json" },
|
|
1880
|
+
method: "POST"
|
|
1881
|
+
});
|
|
1882
|
+
const data = await response.json().catch(() => {
|
|
1883
|
+
return;
|
|
1884
|
+
});
|
|
1885
|
+
if (!response.ok || !data?.device_code || !data.user_code) {
|
|
1886
|
+
throw new CliError2("device_code_failed", `Couldn't start the login flow (${response.status}). Try again in a moment.`);
|
|
1887
|
+
}
|
|
1888
|
+
return data;
|
|
1889
|
+
}
|
|
1890
|
+
async function pollForToken(baseUrl, device) {
|
|
1891
|
+
let intervalMs = Math.max(device.interval, 1) * 1000;
|
|
1892
|
+
const deadline = Date.now() + Math.max(device.expires_in, 1) * 1000;
|
|
1893
|
+
await sleep(intervalMs);
|
|
1894
|
+
while (Date.now() < deadline) {
|
|
1895
|
+
const response = await fetch(`${baseUrl}/api/auth/device/token`, {
|
|
1896
|
+
body: JSON.stringify({
|
|
1897
|
+
client_id: CLI_CLIENT_ID,
|
|
1898
|
+
device_code: device.device_code,
|
|
1899
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
1900
|
+
}),
|
|
1901
|
+
headers: { "Content-Type": "application/json" },
|
|
1902
|
+
method: "POST"
|
|
1903
|
+
});
|
|
1904
|
+
const data = await response.json().catch(() => {
|
|
1905
|
+
return;
|
|
1906
|
+
});
|
|
1907
|
+
if (response.ok && data && "access_token" in data && data.access_token) {
|
|
1908
|
+
return data;
|
|
1909
|
+
}
|
|
1910
|
+
const pending = data && "error" in data ? data : undefined;
|
|
1911
|
+
switch (pending?.error) {
|
|
1912
|
+
case "authorization_pending":
|
|
1913
|
+
break;
|
|
1914
|
+
case "slow_down":
|
|
1915
|
+
intervalMs += 5000;
|
|
1916
|
+
break;
|
|
1917
|
+
case "access_denied":
|
|
1918
|
+
throw new CliError2("access_denied", "Sign-in was denied in the browser. Nothing linked.");
|
|
1919
|
+
case "expired_token":
|
|
1920
|
+
throw new CliError2("expired_token", "The login request timed out. Run `fluncle login` again.");
|
|
1921
|
+
default:
|
|
1922
|
+
throw new CliError2("device_token_failed", pending?.error_description ?? `Login failed (${response.status}).`);
|
|
1923
|
+
}
|
|
1924
|
+
await sleep(intervalMs);
|
|
1925
|
+
}
|
|
1926
|
+
throw new CliError2("expired_token", "The login request timed out. Run `fluncle login` again.");
|
|
1927
|
+
}
|
|
1928
|
+
async function fetchSessionUser(baseUrl, token) {
|
|
1929
|
+
const response = await fetch(`${baseUrl}/api/me`, {
|
|
1930
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1931
|
+
});
|
|
1932
|
+
if (!response.ok) {
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
const data = await response.json().catch(() => {
|
|
1936
|
+
return;
|
|
1937
|
+
});
|
|
1938
|
+
return data?.user ?? undefined;
|
|
1939
|
+
}
|
|
1940
|
+
async function revokeSession(baseUrl, token) {
|
|
1941
|
+
await fetch(`${baseUrl}/api/auth/sign-out`, {
|
|
1942
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1943
|
+
method: "POST"
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
function sleep(ms) {
|
|
1947
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1948
|
+
}
|
|
1949
|
+
var CLI_CLIENT_ID = "fluncle-cli", DEVICE_SCOPE = "galaxy-sync";
|
|
1950
|
+
var init_login = __esm(() => {
|
|
1951
|
+
init_env();
|
|
1952
|
+
init_open_external();
|
|
1953
|
+
init_output();
|
|
1954
|
+
init_user_token();
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
// src/commands/me.ts
|
|
1958
|
+
var exports_me = {};
|
|
1959
|
+
__export(exports_me, {
|
|
1960
|
+
meCommand: () => meCommand
|
|
1961
|
+
});
|
|
1962
|
+
async function meCommand() {
|
|
1963
|
+
const [me, progress] = await Promise.all([
|
|
1964
|
+
userApiGet("/api/me"),
|
|
1965
|
+
userApiGet("/api/me/galaxy-progress")
|
|
1966
|
+
]);
|
|
1967
|
+
if (!me.user) {
|
|
1968
|
+
throw new Error("Your sign-in expired. Run `fluncle login` to link this device again.");
|
|
1969
|
+
}
|
|
1970
|
+
return {
|
|
1971
|
+
collectedCount: progress.collectedLogIds.length,
|
|
1972
|
+
deaths: progress.deaths,
|
|
1973
|
+
joinedAt: me.user.createdAt,
|
|
1974
|
+
name: me.user.displayUsername ?? me.user.username ?? "cosmonaut",
|
|
1975
|
+
userId: me.user.id,
|
|
1976
|
+
wins: progress.wins
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
var init_me = __esm(() => {
|
|
1980
|
+
init_api();
|
|
1981
|
+
});
|
|
1982
|
+
|
|
1759
1983
|
// src/brand.ts
|
|
1760
1984
|
var fluncleAsciiLogo2 = `███████╗██╗ ██╗ ██╗███╗ ██╗ ██████╗██╗ ███████╗
|
|
1761
1985
|
██╔════╝██║ ██║ ██║████╗ ██║██╔════╝██║ ██╔════╝
|
|
@@ -1914,7 +2138,7 @@ function parseVersion2(version) {
|
|
|
1914
2138
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
1915
2139
|
var init_version2 = __esm(() => {
|
|
1916
2140
|
init_output();
|
|
1917
|
-
currentVersion2 = "0.
|
|
2141
|
+
currentVersion2 = "0.66.0".trim() ? "0.66.0".trim() : "0.1.0";
|
|
1918
2142
|
});
|
|
1919
2143
|
|
|
1920
2144
|
// ../../packages/registry/src/index.ts
|
|
@@ -3280,7 +3504,7 @@ __export(exports_newsletter, {
|
|
|
3280
3504
|
newsletterListCommand: () => newsletterListCommand,
|
|
3281
3505
|
newsletterDraftCommand: () => newsletterDraftCommand
|
|
3282
3506
|
});
|
|
3283
|
-
import { existsSync as existsSync2, readFileSync as
|
|
3507
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
3284
3508
|
function buildBody2(options, { requireContent }) {
|
|
3285
3509
|
const body = {};
|
|
3286
3510
|
if (options.contentFile !== undefined) {
|
|
@@ -3303,7 +3527,7 @@ function readContentFile(filePath) {
|
|
|
3303
3527
|
if (!existsSync2(filePath)) {
|
|
3304
3528
|
throw new CliError2("file_not_found", `Content file not found: ${filePath}`);
|
|
3305
3529
|
}
|
|
3306
|
-
const text =
|
|
3530
|
+
const text = readFileSync3(filePath, "utf-8");
|
|
3307
3531
|
try {
|
|
3308
3532
|
return JSON.parse(text);
|
|
3309
3533
|
} catch (error) {
|
|
@@ -3937,7 +4161,7 @@ var COORD_FALLBACK2 = "—";
|
|
|
3937
4161
|
var init_format2 = () => {};
|
|
3938
4162
|
|
|
3939
4163
|
// src/cli.ts
|
|
3940
|
-
import { existsSync as existsSync3, readFileSync as
|
|
4164
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
3941
4165
|
import path2 from "path";
|
|
3942
4166
|
|
|
3943
4167
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -6057,6 +6281,7 @@ ${fluncleAsciiLogo}
|
|
|
6057
6281
|
`).addHelpText("after", rootHelpSections);
|
|
6058
6282
|
addListenCommands(program2);
|
|
6059
6283
|
addShareCommands(program2);
|
|
6284
|
+
addAccountCommands(program2);
|
|
6060
6285
|
addMetaCommands(program2);
|
|
6061
6286
|
addTrackCommands(program2);
|
|
6062
6287
|
addAdminCommands(program2);
|
|
@@ -6120,6 +6345,20 @@ function addShareCommands(program2) {
|
|
|
6120
6345
|
await submitCommand2(input.join(" ") || undefined);
|
|
6121
6346
|
});
|
|
6122
6347
|
}
|
|
6348
|
+
function addAccountCommands(program2) {
|
|
6349
|
+
program2.command("login").description("Link this device to your Fluncle account (sync your Galaxy)").action(async () => {
|
|
6350
|
+
const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), exports_login));
|
|
6351
|
+
await loginCommand2();
|
|
6352
|
+
});
|
|
6353
|
+
program2.command("logout").description("Unlink this device from your account").action(async () => {
|
|
6354
|
+
const { logoutCommand: logoutCommand2 } = await Promise.resolve().then(() => (init_login(), exports_login));
|
|
6355
|
+
await logoutCommand2();
|
|
6356
|
+
});
|
|
6357
|
+
program2.command("me").description("Your account and Galaxy progress (sign in with `fluncle login`)").option("--json", "Print JSON", false).action(async (options) => {
|
|
6358
|
+
const { meCommand: meCommand2 } = await Promise.resolve().then(() => (init_me(), exports_me));
|
|
6359
|
+
await runMe(options, meCommand2);
|
|
6360
|
+
});
|
|
6361
|
+
}
|
|
6123
6362
|
function addMetaCommands(program2) {
|
|
6124
6363
|
program2.command("about").description("Fluncle, and where to find him").action(async () => {
|
|
6125
6364
|
const { aboutCommand: aboutCommand2 } = await Promise.resolve().then(() => (init_about(), exports_about));
|
|
@@ -6389,7 +6628,7 @@ async function runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCo
|
|
|
6389
6628
|
console.log(` mime: ${result.mime}`);
|
|
6390
6629
|
}
|
|
6391
6630
|
async function runTrackObserve(idOrLogId, options, trackObserveCommand2) {
|
|
6392
|
-
const script = options.scriptFile ?
|
|
6631
|
+
const script = options.scriptFile ? readFileSync4(options.scriptFile, "utf8") : options.script;
|
|
6393
6632
|
if (!idOrLogId || !script || !script.trim()) {
|
|
6394
6633
|
throw new Error("Usage: fluncle admin tracks observe <track_id|log_id> (--script <text> | --script-file <file>) [--voice-id <id>] [--duration-ms <ms>] [--context-note <text>] [--json]");
|
|
6395
6634
|
}
|
|
@@ -6445,7 +6684,7 @@ async function runTrackContext(idOrLogId, options, trackContextCommand2) {
|
|
|
6445
6684
|
}
|
|
6446
6685
|
}
|
|
6447
6686
|
async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
|
|
6448
|
-
const note = options.scriptFile ?
|
|
6687
|
+
const note = options.scriptFile ? readFileSync4(options.scriptFile, "utf8") : options.script;
|
|
6449
6688
|
if (!idOrLogId || !note || !note.trim()) {
|
|
6450
6689
|
throw new Error("Usage: fluncle admin tracks note <track_id|log_id> (--script <text> | --script-file <file>) [--json]");
|
|
6451
6690
|
}
|
|
@@ -7200,6 +7439,20 @@ async function runStatus(options, statusCommand2) {
|
|
|
7200
7439
|
console.log(statusLines2(snapshot).join(`
|
|
7201
7440
|
`));
|
|
7202
7441
|
}
|
|
7442
|
+
async function runMe(options, meCommand2) {
|
|
7443
|
+
const me = await meCommand2();
|
|
7444
|
+
if (options.json) {
|
|
7445
|
+
printJson({ ok: true, ...me });
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
console.log(me.name);
|
|
7449
|
+
console.log([
|
|
7450
|
+
`${me.collectedCount} ${me.collectedCount === 1 ? "finding" : "findings"} collected`,
|
|
7451
|
+
`${me.wins} ${me.wins === 1 ? "win" : "wins"}`,
|
|
7452
|
+
`${me.deaths} ${me.deaths === 1 ? "death" : "deaths"}`
|
|
7453
|
+
].join(" \xB7 "));
|
|
7454
|
+
console.log(`Aboard since ${new Date(me.joinedAt).toLocaleDateString()}`);
|
|
7455
|
+
}
|
|
7203
7456
|
function rejectUnexpectedPositionals(positionals) {
|
|
7204
7457
|
if (positionals.length === 0) {
|
|
7205
7458
|
return;
|
package/package.json
CHANGED