@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.
- package/LICENSE +202 -0
- package/README.md +184 -2
- package/dist/auth/gotrue-client.js +64 -0
- package/dist/auth/gotrue-client.js.map +1 -0
- package/dist/auth/session.js +97 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/token-store.js +165 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/diaries.js +41 -0
- package/dist/commands/diaries.js.map +1 -0
- package/dist/commands/login.js +217 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/profiles.js +176 -0
- package/dist/commands/profiles.js.map +1 -0
- package/dist/commands/read.js +77 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/write.js +75 -0
- package/dist/commands/write.js.map +1 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.js +46 -0
- package/dist/errors.js.map +1 -0
- package/dist/http-client.js +86 -0
- package/dist/http-client.js.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/append-entry.js +141 -0
- package/dist/lib/append-entry.js.map +1 -0
- package/dist/lib/diary-resolve.js +50 -0
- package/dist/lib/diary-resolve.js.map +1 -0
- package/dist/lib/entry-date.js +76 -0
- package/dist/lib/entry-date.js.map +1 -0
- package/dist/lib/entry-find.js +39 -0
- package/dist/lib/entry-find.js.map +1 -0
- package/dist/lib/jwt.js +25 -0
- package/dist/lib/jwt.js.map +1 -0
- package/dist/lib/output.js +29 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- 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
|
package/dist/cli.js.map
ADDED
|
@@ -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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"}
|