@whatcha/cli 0.0.0-stage → 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +184 -2
  3. package/dist/auth/gotrue-client.js +64 -0
  4. package/dist/auth/gotrue-client.js.map +1 -0
  5. package/dist/auth/session.js +97 -0
  6. package/dist/auth/session.js.map +1 -0
  7. package/dist/auth/token-store.js +165 -0
  8. package/dist/auth/token-store.js.map +1 -0
  9. package/dist/cli.js +103 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/diaries.js +41 -0
  12. package/dist/commands/diaries.js.map +1 -0
  13. package/dist/commands/login.js +217 -0
  14. package/dist/commands/login.js.map +1 -0
  15. package/dist/commands/profiles.js +176 -0
  16. package/dist/commands/profiles.js.map +1 -0
  17. package/dist/commands/read.js +77 -0
  18. package/dist/commands/read.js.map +1 -0
  19. package/dist/commands/write.js +75 -0
  20. package/dist/commands/write.js.map +1 -0
  21. package/dist/config.js +47 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/errors.js +46 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/http-client.js +86 -0
  26. package/dist/http-client.js.map +1 -0
  27. package/dist/index.js +16 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/lib/append-entry.js +141 -0
  30. package/dist/lib/append-entry.js.map +1 -0
  31. package/dist/lib/diary-resolve.js +50 -0
  32. package/dist/lib/diary-resolve.js.map +1 -0
  33. package/dist/lib/entry-date.js +76 -0
  34. package/dist/lib/entry-date.js.map +1 -0
  35. package/dist/lib/entry-find.js +39 -0
  36. package/dist/lib/entry-find.js.map +1 -0
  37. package/dist/lib/jwt.js +25 -0
  38. package/dist/lib/jwt.js.map +1 -0
  39. package/dist/lib/output.js +29 -0
  40. package/dist/lib/output.js.map +1 -0
  41. package/dist/types.js +6 -0
  42. package/dist/types.js.map +1 -0
  43. package/package.json +46 -4
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileTokenStore = void 0;
7
+ exports.candidateStores = candidateStores;
8
+ const node_child_process_1 = require("node:child_process");
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const config_1 = require("../config");
12
+ function execFileText(cmd, args, input) {
13
+ return new Promise((resolve, reject) => {
14
+ const child = (0, node_child_process_1.execFile)(cmd, args, { encoding: "utf8", maxBuffer: 1024 * 1024 }, (err, stdout) => {
15
+ if (err)
16
+ reject(err);
17
+ else
18
+ resolve(stdout);
19
+ });
20
+ if (input !== undefined)
21
+ child.stdin?.end(input);
22
+ });
23
+ }
24
+ async function commandExists(cmd) {
25
+ try {
26
+ await execFileText(process.platform === "win32" ? "where" : "which", [cmd]);
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ const SERVICE = "whatcha-cli";
34
+ const ACCOUNT = "whatcha";
35
+ /**
36
+ * macOS Keychain, via the `security` CLI (no native module -- `keytar` and
37
+ * friends need a node-gyp build per platform, which is a lot to ask of
38
+ * `npm ci` for one command most sessions never touch). The whole session
39
+ * (both tokens, the email, the expiry) is stored as one JSON blob under a
40
+ * single fixed account name; this is a one-account CLI, not a credential
41
+ * manager for several.
42
+ */
43
+ class MacKeychainStore {
44
+ name = "macOS Keychain";
45
+ async isAvailable() {
46
+ return process.platform === "darwin" && (await commandExists("security"));
47
+ }
48
+ async save(session) {
49
+ // -U: update in place if an entry already exists, instead of erroring.
50
+ await execFileText("security", [
51
+ "add-generic-password",
52
+ "-a",
53
+ ACCOUNT,
54
+ "-s",
55
+ SERVICE,
56
+ "-w",
57
+ JSON.stringify(session),
58
+ "-U",
59
+ ]);
60
+ }
61
+ async load() {
62
+ try {
63
+ const out = await execFileText("security", ["find-generic-password", "-a", ACCOUNT, "-s", SERVICE, "-w"]);
64
+ return JSON.parse(out.trim());
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ async clear() {
71
+ try {
72
+ await execFileText("security", ["delete-generic-password", "-a", ACCOUNT, "-s", SERVICE]);
73
+ }
74
+ catch {
75
+ // Nothing stored -- clearing an already-empty store is not an error.
76
+ }
77
+ }
78
+ }
79
+ /**
80
+ * Linux, via `secret-tool` (libsecret) -- GNOME Keyring, KWallet's libsecret
81
+ * shim, etc. Only actually works when a secret service is running, which a
82
+ * plain SSH session or a container usually does not have; `isAvailable`
83
+ * only checks the binary exists, so a headless box falls through to the
84
+ * file store the first time `save` throws (see auth/session.ts).
85
+ */
86
+ class LinuxSecretToolStore {
87
+ name = "the Secret Service (secret-tool)";
88
+ async isAvailable() {
89
+ return process.platform === "linux" && (await commandExists("secret-tool"));
90
+ }
91
+ async save(session) {
92
+ await execFileText("secret-tool", ["store", "--label=whatcha CLI session", "service", SERVICE, "account", ACCOUNT], JSON.stringify(session));
93
+ }
94
+ async load() {
95
+ try {
96
+ const out = await execFileText("secret-tool", ["lookup", "service", SERVICE, "account", ACCOUNT]);
97
+ const trimmed = out.trim();
98
+ return trimmed ? JSON.parse(trimmed) : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ async clear() {
105
+ try {
106
+ await execFileText("secret-tool", ["clear", "service", SERVICE, "account", ACCOUNT]);
107
+ }
108
+ catch {
109
+ // Nothing stored.
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * The fallback the task asks for by name: a 0600 file under the config dir.
115
+ * Always available, so it is always last in the candidate list and never
116
+ * itself falls back to anything.
117
+ */
118
+ class FileTokenStore {
119
+ name = `file (${(0, config_1.credentialsFilePath)()})`;
120
+ async isAvailable() {
121
+ return true;
122
+ }
123
+ async save(session) {
124
+ const file = (0, config_1.credentialsFilePath)();
125
+ await promises_1.default.mkdir(node_path_1.default.dirname(file), { recursive: true, mode: 0o700 });
126
+ await promises_1.default.writeFile(file, JSON.stringify(session, null, 2), { mode: 0o600 });
127
+ // Belt and suspenders: writeFile's mode only applies when it creates the
128
+ // file. An existing file (re-login) keeps whatever mode it already had,
129
+ // so set it explicitly every time.
130
+ await promises_1.default.chmod(file, 0o600);
131
+ }
132
+ async load() {
133
+ try {
134
+ const text = await promises_1.default.readFile((0, config_1.credentialsFilePath)(), "utf8");
135
+ return JSON.parse(text);
136
+ }
137
+ catch (err) {
138
+ if (err.code === "ENOENT")
139
+ return null;
140
+ throw err;
141
+ }
142
+ }
143
+ async clear() {
144
+ await promises_1.default.rm((0, config_1.credentialsFilePath)(), { force: true });
145
+ }
146
+ }
147
+ exports.FileTokenStore = FileTokenStore;
148
+ /**
149
+ * Platform-appropriate OS store first, file store always last. `save`
150
+ * picks the first available candidate and actually uses it; if that
151
+ * candidate's `save` throws (keychain present but locked, no Secret
152
+ * Service running, etc.) the caller falls back to the next one -- see
153
+ * `saveSession` in auth/session.ts, which is what prints "saved to <store
154
+ * name>" and is what tests/token-store.test.ts exercises.
155
+ */
156
+ function candidateStores() {
157
+ const stores = [];
158
+ if (process.platform === "darwin")
159
+ stores.push(new MacKeychainStore());
160
+ if (process.platform === "linux")
161
+ stores.push(new LinuxSecretToolStore());
162
+ stores.push(new FileTokenStore());
163
+ return stores;
164
+ }
165
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"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"}
package/dist/cli.js ADDED
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = run;
4
+ const errors_1 = require("./errors");
5
+ const login_1 = require("./commands/login");
6
+ const diaries_1 = require("./commands/diaries");
7
+ const write_1 = require("./commands/write");
8
+ const read_1 = require("./commands/read");
9
+ const profiles_1 = require("./commands/profiles");
10
+ const output_1 = require("./lib/output");
11
+ const USAGE = `whatcha -- command-line client for what-are-you-do-ing
12
+
13
+ Usage:
14
+ whatcha login [--email <email>] [--password <password>] [--json]
15
+ whatcha login --token <refresh-token> [--json]
16
+ whatcha diaries [--json]
17
+ whatcha write <diary> --text "..." [--time HH:MM] [--date YYYY-MM-DD] [--json]
18
+ whatcha read <diary> [--date YYYY-MM-DD] [--json]
19
+ whatcha profiles [--json]
20
+ whatcha switch <@handle> [--json]
21
+ whatcha switch --account [--json]
22
+ whatcha profile-rm <@handle> [--json]
23
+
24
+ Every command accepts --json for machine-readable output.
25
+ Credentials for login can also come from WHATCHA_EMAIL / WHATCHA_PASSWORD,
26
+ or WHATCHA_TOKEN for a pre-issued refresh token (see README.md -- this is
27
+ the escape hatch for a headless machine that cannot pass a captcha
28
+ challenge, issue #1428).
29
+ `;
30
+ /**
31
+ * The whole CLI as one testable function: argv in, exit code out, nothing
32
+ * printed that isn't part of the documented contract. index.ts is the only
33
+ * thing that calls this against real process.argv/exit -- everything in
34
+ * tests/cli.test.ts calls it directly with a fake argv instead.
35
+ */
36
+ async function run(argv) {
37
+ const [command, ...rest] = argv;
38
+ if (!command || command === "--help" || command === "-h") {
39
+ process.stdout.write(USAGE);
40
+ return command ? 0 : 1;
41
+ }
42
+ if (command === "--version") {
43
+ // require() rather than an import so this stays the one place that
44
+ // touches package.json -- no build-time JSON import wiring needed.
45
+ process.stdout.write(`${require("../package.json").version}\n`);
46
+ return 0;
47
+ }
48
+ // Only used to decide how to *format* a parse-time error below, before a
49
+ // command has had a chance to parse --json itself -- not a substitute for
50
+ // each command's own parseArgs, which is what actually validates it.
51
+ const wantsJson = rest.includes("--json");
52
+ try {
53
+ switch (command) {
54
+ case "login":
55
+ return (await (0, login_1.runLogin)((0, login_1.parseLoginArgs)(rest))).exitCode;
56
+ case "diaries":
57
+ return (await (0, diaries_1.runDiaries)((0, diaries_1.parseDiariesArgs)(rest))).exitCode;
58
+ case "write":
59
+ return (await (0, write_1.runWrite)((0, write_1.parseWriteArgs)(rest))).exitCode;
60
+ case "read":
61
+ return (await (0, read_1.runRead)((0, read_1.parseReadArgs)(rest))).exitCode;
62
+ // Sub-accounts (#1538). After a switch every command above acts as
63
+ // the profile switched into -- the saved session simply is it.
64
+ case "profiles":
65
+ return (await (0, profiles_1.runProfiles)((0, profiles_1.parseProfilesArgs)(rest))).exitCode;
66
+ case "switch":
67
+ return (await (0, profiles_1.runSwitch)((0, profiles_1.parseSwitchArgs)(rest))).exitCode;
68
+ case "profile-rm":
69
+ return (await (0, profiles_1.runRemove)((0, profiles_1.parseRemoveArgs)(rest))).exitCode;
70
+ default:
71
+ throw new errors_1.CliUsageError(`Unknown command "${command}". Run \`whatcha --help\` for usage.`);
72
+ }
73
+ }
74
+ catch (err) {
75
+ return reportError(err, wantsJson);
76
+ }
77
+ }
78
+ function reportError(err, wantsJson) {
79
+ if (err instanceof errors_1.CliUsageError) {
80
+ if (wantsJson)
81
+ (0, output_1.printJson)({ ok: false, error: { code: "USAGE", message: err.message } });
82
+ else
83
+ (0, output_1.printErrorLine)(err.message);
84
+ return 1;
85
+ }
86
+ if (err instanceof errors_1.ApiError) {
87
+ if (wantsJson)
88
+ (0, output_1.printJson)({ ok: false, error: { code: err.code, message: err.message } });
89
+ else
90
+ (0, output_1.printErrorLine)(`${err.code}: ${err.message}`);
91
+ return 1;
92
+ }
93
+ // Anything else is a bug in this CLI, not a user or API problem --
94
+ // surfaced in full (never swallowed), but still never as a raw uncaught
95
+ // exception stack trace to stdout in --json mode.
96
+ const message = err instanceof Error ? err.message : String(err);
97
+ if (wantsJson)
98
+ (0, output_1.printJson)({ ok: false, error: { code: "INTERNAL", message } });
99
+ else
100
+ (0, output_1.printErrorLine)(`whatcha: internal error: ${message}`);
101
+ return 1;
102
+ }
103
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDiariesArgs = parseDiariesArgs;
4
+ exports.runDiaries = runDiaries;
5
+ const node_util_1 = require("node:util");
6
+ const session_1 = require("../auth/session");
7
+ const errors_1 = require("../errors");
8
+ const diary_resolve_1 = require("../lib/diary-resolve");
9
+ const output_1 = require("../lib/output");
10
+ function parseDiariesArgs(argv) {
11
+ let values;
12
+ try {
13
+ ({ values } = (0, node_util_1.parseArgs)({
14
+ args: argv,
15
+ options: { json: { type: "boolean", default: false } },
16
+ allowPositionals: false,
17
+ }));
18
+ }
19
+ catch (err) {
20
+ throw new errors_1.CliUsageError(err.message);
21
+ }
22
+ return { json: values.json };
23
+ }
24
+ async function runDiaries(opts, deps = {}) {
25
+ const client = deps.client ?? (await (0, session_1.authenticatedClient)()).client;
26
+ const diaries = await (0, diary_resolve_1.listAllDiaries)(client);
27
+ if (opts.json) {
28
+ (0, output_1.printJson)({ ok: true, diaries });
29
+ return { exitCode: 0 };
30
+ }
31
+ if (diaries.length === 0) {
32
+ (0, output_1.printLine)("No diaries.");
33
+ return { exitCode: 0 };
34
+ }
35
+ for (const d of diaries) {
36
+ const entries = d.entryCount === 1 ? "1 entry" : `${d.entryCount} entries`;
37
+ (0, output_1.printLine)(`${d.id} ${d.name} [${d.groupName}] ${entries} tz=${d.timezone}`);
38
+ }
39
+ return { exitCode: 0 };
40
+ }
41
+ //# sourceMappingURL=diaries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diaries.js","sourceRoot":"","sources":["../../src/commands/diaries.ts"],"names":[],"mappings":";;AAWA,4CAYC;AAED,gCAqBC;AA9CD,yCAAsC;AACtC,6CAAsD;AACtD,sCAA0C;AAE1C,wDAAsD;AACtD,0CAAqD;AAMrD,SAAgB,gBAAgB,CAAC,IAAc;IAC7C,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,CAAC,EAAE,MAAM,EAAE,GAAG,IAAA,qBAAS,EAAC;YACtB,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACtD,gBAAgB,EAAE,KAAK;SACxB,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,sBAAa,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAe,EAAE,CAAC;AAC1C,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,IAAoB,EACpB,OAAmC,EAAE;IAErC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAA,6BAAmB,GAAE,CAAC,CAAC,MAAM,CAAC;IACnE,MAAM,OAAO,GAAG,MAAM,IAAA,8BAAc,EAAC,MAAM,CAAC,CAAC;IAE7C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,IAAA,kBAAS,EAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACjC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,IAAA,kBAAS,EAAC,aAAa,CAAC,CAAC;QACzB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,UAAU,CAAC;QAC3E,IAAA,kBAAS,EAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,SAAS,MAAM,OAAO,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseLoginArgs = parseLoginArgs;
7
+ exports.runLogin = runLogin;
8
+ const node_util_1 = require("node:util");
9
+ const node_readline_1 = __importDefault(require("node:readline"));
10
+ const session_1 = require("../auth/session");
11
+ const config_1 = require("../config");
12
+ const errors_1 = require("../errors");
13
+ const token_store_1 = require("../auth/token-store");
14
+ const gotrue_client_1 = require("../auth/gotrue-client");
15
+ const jwt_1 = require("../lib/jwt");
16
+ const output_1 = require("../lib/output");
17
+ function parseLoginArgs(argv) {
18
+ let values;
19
+ try {
20
+ ({ values } = (0, node_util_1.parseArgs)({
21
+ args: argv,
22
+ options: {
23
+ json: { type: "boolean", default: false },
24
+ email: { type: "string" },
25
+ password: { type: "string" },
26
+ token: { type: "string" },
27
+ "api-url": { type: "string" },
28
+ },
29
+ allowPositionals: false,
30
+ }));
31
+ }
32
+ catch (err) {
33
+ throw new errors_1.CliUsageError(err.message);
34
+ }
35
+ return {
36
+ json: values.json,
37
+ email: values.email,
38
+ password: values.password,
39
+ token: values.token,
40
+ apiUrl: values["api-url"] ?? (0, config_1.apiBaseUrl)(),
41
+ };
42
+ }
43
+ /** Masked stdin prompt -- readline has no built-in for this, so each keystroke is intercepted and only a literal newline is let through to the terminal. */
44
+ function promptHidden(question) {
45
+ return new Promise((resolve) => {
46
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
47
+ const rlWithOutput = rl;
48
+ const originalWrite = rlWithOutput._writeToOutput?.bind(rl);
49
+ let hidden = false;
50
+ rlWithOutput._writeToOutput = (str) => {
51
+ if (!hidden)
52
+ originalWrite?.(str);
53
+ };
54
+ rl.question(question, (answer) => {
55
+ rl.close();
56
+ process.stdout.write("\n");
57
+ resolve(answer);
58
+ });
59
+ hidden = true;
60
+ });
61
+ }
62
+ function prompt(question) {
63
+ return new Promise((resolve) => {
64
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
65
+ rl.question(question, (answer) => {
66
+ rl.close();
67
+ resolve(answer);
68
+ });
69
+ });
70
+ }
71
+ /**
72
+ * Non-interactive credentials, for the exact audience this CLI is for: an
73
+ * AI agent (or a script) invoking `whatcha` as a subprocess has no TTY to
74
+ * answer an interactive prompt at. `--email`/`--password` and
75
+ * `WHATCHA_EMAIL`/`WHATCHA_PASSWORD` are the same idea twice -- flags for a
76
+ * one-off, env vars for something that runs `whatcha login` from a config
77
+ * it does not want to put a password directly into.
78
+ */
79
+ async function resolveCredentials(opts) {
80
+ const email = opts.email ?? process.env.WHATCHA_EMAIL ?? (await prompt("Email: "));
81
+ const password = opts.password ?? process.env.WHATCHA_PASSWORD ?? (await promptHidden("Password: "));
82
+ if (!email || !password)
83
+ throw new errors_1.CliUsageError("Both an email and a password are required.");
84
+ return { email, password };
85
+ }
86
+ async function runLogin(opts, deps = {}) {
87
+ const token = opts.token ?? process.env.WHATCHA_TOKEN;
88
+ if (token)
89
+ return finishLogin(await loginWithToken(opts, deps, token), opts, deps);
90
+ const { email, password } = deps.credentials ?? (await resolveCredentials(opts));
91
+ const supabase = (0, config_1.supabaseCredentials)();
92
+ try {
93
+ const session = supabase
94
+ ? await loginWithPasswordViaGoTrue(supabase, email, password)
95
+ : await loginWithPasswordViaApi(opts, deps, email, password);
96
+ return finishLogin(session, opts, deps);
97
+ }
98
+ catch (err) {
99
+ return reportRefusal(err, opts);
100
+ }
101
+ }
102
+ /**
103
+ * The escape hatch issue #1428 asks for: skip the password grant (and
104
+ * whatever captcha it is gated behind) entirely by handing `whatcha` a
105
+ * refresh token minted some other way -- by an ops flow, a support-issued
106
+ * token, or a browser sign-in on a device that could actually solve the
107
+ * challenge. `POST /auth/refresh` is GoTrue's own rotation and is not
108
+ * captcha-gated (openapi.yaml's /auth/refresh), so this is the one login
109
+ * path that does not depend on Turnstile letting an automated caller
110
+ * through at all -- verified against openapi.yaml's documented contract,
111
+ * not against a live server (no test account for this task -- see the PR
112
+ * description).
113
+ *
114
+ * `/auth/refresh` returns no user info, so the id/email shown to the
115
+ * person come from decoding the access token's own JWT payload -- display
116
+ * only, not a trust decision (the server verifies the token on every real
117
+ * request regardless of what this reads out of it).
118
+ */
119
+ async function loginWithToken(opts, deps, refreshToken) {
120
+ const anon = deps.client ?? (0, session_1.anonymousClient)(opts.apiUrl);
121
+ let refreshed;
122
+ try {
123
+ refreshed = await anon.post("/auth/refresh", { refreshToken });
124
+ }
125
+ catch (err) {
126
+ throw new errors_1.CliUsageError("That token could not be used to start a session (it may be expired, already rotated, or not a " +
127
+ `refresh token at all). ${err instanceof errors_1.ApiError ? err.message : err.message}`);
128
+ }
129
+ const claims = (0, jwt_1.decodeJwtPayloadUnsafe)(refreshed.accessToken);
130
+ return {
131
+ userId: typeof claims?.sub === "string" ? claims.sub : "(unknown)",
132
+ email: typeof claims?.email === "string" ? claims.email : null,
133
+ accessToken: refreshed.accessToken,
134
+ refreshToken: refreshed.refreshToken,
135
+ expiresIn: refreshed.expiresIn,
136
+ };
137
+ }
138
+ /** Opt-in direct-GoTrue path -- see auth/gotrue-client.ts's header comment for why this needs WHATCHA_SUPABASE_URL/WHATCHA_SUPABASE_ANON_KEY set. */
139
+ async function loginWithPasswordViaGoTrue(supabase, email, password) {
140
+ const res = await (0, gotrue_client_1.signInWithPassword)(supabase.url, supabase.anonKey, email, password);
141
+ return {
142
+ userId: res.userId,
143
+ email: res.email,
144
+ accessToken: res.accessToken,
145
+ refreshToken: res.refreshToken,
146
+ expiresIn: res.expiresIn,
147
+ };
148
+ }
149
+ /**
150
+ * Default path: `POST /auth/login`, the API's own thin wrapper over the
151
+ * same GoTrue password grant (apps/api/src/modules/account/routes/auth.ts).
152
+ * It collapses *every* GoTrue failure into one generic 401
153
+ * INVALID_CREDENTIALS -- wrong password, unverified email, and a Turnstile
154
+ * captcha rejection all read identically from here, because the route only
155
+ * checks `status >= 400` and never looks at the GoTrue error body. See
156
+ * reportRefusal for what the CLI can and cannot tell the person because of
157
+ * that.
158
+ */
159
+ async function loginWithPasswordViaApi(opts, deps, email, password) {
160
+ const client = deps.client ?? (0, session_1.anonymousClient)(opts.apiUrl);
161
+ const res = await client.post("/auth/login", { email, password });
162
+ return {
163
+ userId: res.user.id,
164
+ email: res.user.email ?? email,
165
+ accessToken: res.accessToken,
166
+ refreshToken: res.refreshToken,
167
+ expiresIn: res.expiresIn,
168
+ };
169
+ }
170
+ function reportRefusal(err, opts) {
171
+ let message;
172
+ if (err instanceof gotrue_client_1.GoTrueError) {
173
+ // The direct-GoTrue path: its error body actually says whether this was
174
+ // a captcha rejection, unlike /auth/login's generic 401.
175
+ message = err.looksLikeCaptchaFailure()
176
+ ? "Sign-in needs a captcha challenge (Cloudflare Turnstile) that this terminal cannot solve " +
177
+ "(issue #1428). This account needs a token issued another way -- ask whoever manages it to sign " +
178
+ "in once from a browser that can pass the challenge, then run `whatcha login --token <refresh-token>` " +
179
+ "or set WHATCHA_TOKEN, instead of retrying a password sign-in here."
180
+ : `Sign-in was refused: ${err.message}`;
181
+ }
182
+ else if (err instanceof errors_1.ApiError && err.status === 401) {
183
+ message =
184
+ "Sign-in was refused. This could be a wrong email/password, or it could be that GoTrue is demanding " +
185
+ "a captcha challenge (Cloudflare Turnstile) this terminal cannot solve (issue #1428) -- /auth/login " +
186
+ "answers both the same generic way, so whatcha cannot tell them apart here. If the password is right, " +
187
+ "do not keep retrying it: this account needs a token issued another way. Sign in once from a browser " +
188
+ "that can pass the challenge, then run `whatcha login --token <refresh-token>` or set WHATCHA_TOKEN.";
189
+ }
190
+ else {
191
+ throw err;
192
+ }
193
+ if (opts.json)
194
+ (0, output_1.printJson)({ ok: false, error: { code: "LOGIN_REFUSED", message } });
195
+ else
196
+ (0, output_1.printErrorLine)(message);
197
+ return { exitCode: 1 };
198
+ }
199
+ async function finishLogin(session, opts, deps) {
200
+ const { store } = await (0, session_1.saveSession)({
201
+ apiBaseUrl: opts.apiUrl,
202
+ userId: session.userId,
203
+ email: session.email,
204
+ accessToken: session.accessToken,
205
+ refreshToken: session.refreshToken,
206
+ expiresAt: Date.now() + session.expiresIn * 1000,
207
+ }, deps.stores ?? (0, token_store_1.candidateStores)());
208
+ // Never the tokens themselves -- see lib/output.ts's header comment.
209
+ if (opts.json) {
210
+ (0, output_1.printJson)({ ok: true, userId: session.userId, email: session.email, credentialStore: store.name });
211
+ }
212
+ else {
213
+ (0, output_1.printLine)(`Logged in as ${session.email ?? session.userId}. Session saved to ${store.name}.`);
214
+ }
215
+ return { exitCode: 0 };
216
+ }
217
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}