numo-cli 1.2.0 → 1.3.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/dist/cli.cjs +377 -1682
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -33,27 +33,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
mod
|
|
34
34
|
));
|
|
35
35
|
|
|
36
|
-
// <define:__ADMIN_UIDS__>
|
|
37
|
-
var define_ADMIN_UIDS_default;
|
|
38
|
-
var init_define_ADMIN_UIDS = __esm({
|
|
39
|
-
"<define:__ADMIN_UIDS__>"() {
|
|
40
|
-
define_ADMIN_UIDS_default = [
|
|
41
|
-
"m4FaWpKYc6QMceKV6wiyx9uLi8n1",
|
|
42
|
-
"yQ36wj8NXKQvcAfp8TtQ4hp8HPi1",
|
|
43
|
-
"koOPCRPPNaZcQZp1icPq8KlluZk2",
|
|
44
|
-
"VuT0LASAitWqB0L1Vn1aL6nAwaw2",
|
|
45
|
-
"ODc6kNR5jMVV8cFWdKueNSUkMUm1",
|
|
46
|
-
"usrkanvWkzdB8CwFnpSLgt9kiGD3",
|
|
47
|
-
"akSj1Q2GUOZopvYaLLTDtXBBOYE2",
|
|
48
|
-
"61SQrwQAp7N182uwv7M2cCismdh1"
|
|
49
|
-
];
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
36
|
// node_modules/commander/lib/error.js
|
|
54
37
|
var require_error = __commonJS({
|
|
55
38
|
"node_modules/commander/lib/error.js"(exports2) {
|
|
56
|
-
init_define_ADMIN_UIDS();
|
|
57
39
|
var CommanderError2 = class extends Error {
|
|
58
40
|
/**
|
|
59
41
|
* Constructs the CommanderError class
|
|
@@ -89,7 +71,6 @@ var require_error = __commonJS({
|
|
|
89
71
|
// node_modules/commander/lib/argument.js
|
|
90
72
|
var require_argument = __commonJS({
|
|
91
73
|
"node_modules/commander/lib/argument.js"(exports2) {
|
|
92
|
-
init_define_ADMIN_UIDS();
|
|
93
74
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
94
75
|
var Argument2 = class {
|
|
95
76
|
/**
|
|
@@ -217,7 +198,6 @@ var require_argument = __commonJS({
|
|
|
217
198
|
// node_modules/commander/lib/help.js
|
|
218
199
|
var require_help = __commonJS({
|
|
219
200
|
"node_modules/commander/lib/help.js"(exports2) {
|
|
220
|
-
init_define_ADMIN_UIDS();
|
|
221
201
|
var { humanReadableArgName } = require_argument();
|
|
222
202
|
var Help2 = class {
|
|
223
203
|
constructor() {
|
|
@@ -632,7 +612,6 @@ var require_help = __commonJS({
|
|
|
632
612
|
// node_modules/commander/lib/option.js
|
|
633
613
|
var require_option = __commonJS({
|
|
634
614
|
"node_modules/commander/lib/option.js"(exports2) {
|
|
635
|
-
init_define_ADMIN_UIDS();
|
|
636
615
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
637
616
|
var Option2 = class {
|
|
638
617
|
/**
|
|
@@ -905,7 +884,6 @@ var require_option = __commonJS({
|
|
|
905
884
|
// node_modules/commander/lib/suggestSimilar.js
|
|
906
885
|
var require_suggestSimilar = __commonJS({
|
|
907
886
|
"node_modules/commander/lib/suggestSimilar.js"(exports2) {
|
|
908
|
-
init_define_ADMIN_UIDS();
|
|
909
887
|
var maxDistance = 3;
|
|
910
888
|
function editDistance(a, b) {
|
|
911
889
|
if (Math.abs(a.length - b.length) > maxDistance)
|
|
@@ -986,11 +964,10 @@ var require_suggestSimilar = __commonJS({
|
|
|
986
964
|
// node_modules/commander/lib/command.js
|
|
987
965
|
var require_command = __commonJS({
|
|
988
966
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
989
|
-
init_define_ADMIN_UIDS();
|
|
990
967
|
var EventEmitter = require("node:events").EventEmitter;
|
|
991
968
|
var childProcess = require("node:child_process");
|
|
992
|
-
var
|
|
993
|
-
var
|
|
969
|
+
var path3 = require("node:path");
|
|
970
|
+
var fs5 = require("node:fs");
|
|
994
971
|
var process2 = require("node:process");
|
|
995
972
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
996
973
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1922,11 +1899,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1899
|
let launchWithNode = false;
|
|
1923
1900
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1924
1901
|
function findFile(baseDir, baseName) {
|
|
1925
|
-
const localBin =
|
|
1926
|
-
if (
|
|
1927
|
-
if (sourceExt.includes(
|
|
1902
|
+
const localBin = path3.resolve(baseDir, baseName);
|
|
1903
|
+
if (fs5.existsSync(localBin)) return localBin;
|
|
1904
|
+
if (sourceExt.includes(path3.extname(baseName))) return void 0;
|
|
1928
1905
|
const foundExt = sourceExt.find(
|
|
1929
|
-
(ext) =>
|
|
1906
|
+
(ext) => fs5.existsSync(`${localBin}${ext}`)
|
|
1930
1907
|
);
|
|
1931
1908
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1932
1909
|
return void 0;
|
|
@@ -1938,21 +1915,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1938
1915
|
if (this._scriptPath) {
|
|
1939
1916
|
let resolvedScriptPath;
|
|
1940
1917
|
try {
|
|
1941
|
-
resolvedScriptPath =
|
|
1918
|
+
resolvedScriptPath = fs5.realpathSync(this._scriptPath);
|
|
1942
1919
|
} catch (err) {
|
|
1943
1920
|
resolvedScriptPath = this._scriptPath;
|
|
1944
1921
|
}
|
|
1945
|
-
executableDir =
|
|
1946
|
-
|
|
1922
|
+
executableDir = path3.resolve(
|
|
1923
|
+
path3.dirname(resolvedScriptPath),
|
|
1947
1924
|
executableDir
|
|
1948
1925
|
);
|
|
1949
1926
|
}
|
|
1950
1927
|
if (executableDir) {
|
|
1951
1928
|
let localFile = findFile(executableDir, executableFile);
|
|
1952
1929
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1953
|
-
const legacyName =
|
|
1930
|
+
const legacyName = path3.basename(
|
|
1954
1931
|
this._scriptPath,
|
|
1955
|
-
|
|
1932
|
+
path3.extname(this._scriptPath)
|
|
1956
1933
|
);
|
|
1957
1934
|
if (legacyName !== this._name) {
|
|
1958
1935
|
localFile = findFile(
|
|
@@ -1963,7 +1940,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1963
1940
|
}
|
|
1964
1941
|
executableFile = localFile || executableFile;
|
|
1965
1942
|
}
|
|
1966
|
-
launchWithNode = sourceExt.includes(
|
|
1943
|
+
launchWithNode = sourceExt.includes(path3.extname(executableFile));
|
|
1967
1944
|
let proc;
|
|
1968
1945
|
if (process2.platform !== "win32") {
|
|
1969
1946
|
if (launchWithNode) {
|
|
@@ -2803,7 +2780,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2803
2780
|
* @return {Command}
|
|
2804
2781
|
*/
|
|
2805
2782
|
nameFromFilename(filename) {
|
|
2806
|
-
this._name =
|
|
2783
|
+
this._name = path3.basename(filename, path3.extname(filename));
|
|
2807
2784
|
return this;
|
|
2808
2785
|
}
|
|
2809
2786
|
/**
|
|
@@ -2817,9 +2794,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2817
2794
|
* @param {string} [path]
|
|
2818
2795
|
* @return {(string|null|Command)}
|
|
2819
2796
|
*/
|
|
2820
|
-
executableDir(
|
|
2821
|
-
if (
|
|
2822
|
-
this._executableDir =
|
|
2797
|
+
executableDir(path4) {
|
|
2798
|
+
if (path4 === void 0) return this._executableDir;
|
|
2799
|
+
this._executableDir = path4;
|
|
2823
2800
|
return this;
|
|
2824
2801
|
}
|
|
2825
2802
|
/**
|
|
@@ -3030,7 +3007,6 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3030
3007
|
// node_modules/commander/index.js
|
|
3031
3008
|
var require_commander = __commonJS({
|
|
3032
3009
|
"node_modules/commander/index.js"(exports2) {
|
|
3033
|
-
init_define_ADMIN_UIDS();
|
|
3034
3010
|
var { Argument: Argument2 } = require_argument();
|
|
3035
3011
|
var { Command: Command2 } = require_command();
|
|
3036
3012
|
var { CommanderError: CommanderError2, InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
@@ -3053,7 +3029,6 @@ var require_commander = __commonJS({
|
|
|
3053
3029
|
// node_modules/picocolors/picocolors.js
|
|
3054
3030
|
var require_picocolors = __commonJS({
|
|
3055
3031
|
"node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
3056
|
-
init_define_ADMIN_UIDS();
|
|
3057
3032
|
var p = process || {};
|
|
3058
3033
|
var argv = p.argv || [];
|
|
3059
3034
|
var env = p.env || {};
|
|
@@ -3211,7 +3186,6 @@ var DEFAULT_TIMEOUT, MAX_RETRIES, RETRYABLE_STATUS, RETRYABLE_NETWORK_CODES, htt
|
|
|
3211
3186
|
var init_http = __esm({
|
|
3212
3187
|
"src/cli/lib/http.ts"() {
|
|
3213
3188
|
"use strict";
|
|
3214
|
-
init_define_ADMIN_UIDS();
|
|
3215
3189
|
DEFAULT_TIMEOUT = 3e4;
|
|
3216
3190
|
MAX_RETRIES = 3;
|
|
3217
3191
|
RETRYABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
@@ -3243,34 +3217,6 @@ var init_http = __esm({
|
|
|
3243
3217
|
}
|
|
3244
3218
|
});
|
|
3245
3219
|
|
|
3246
|
-
// src/cli/lib/config.ts
|
|
3247
|
-
var config_exports = {};
|
|
3248
|
-
__export(config_exports, {
|
|
3249
|
-
getFirebaseApiKey: () => getFirebaseApiKey,
|
|
3250
|
-
getFirebaseAppId: () => getFirebaseAppId,
|
|
3251
|
-
getFirebaseProjectId: () => getFirebaseProjectId,
|
|
3252
|
-
getFirestoreBaseUrl: () => getFirestoreBaseUrl
|
|
3253
|
-
});
|
|
3254
|
-
function getFirestoreBaseUrl() {
|
|
3255
|
-
const projectId = getFirebaseProjectId();
|
|
3256
|
-
return `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents`;
|
|
3257
|
-
}
|
|
3258
|
-
function getFirebaseApiKey() {
|
|
3259
|
-
return process.env.NUMO_FIREBASE_API_KEY ?? (true ? "AIzaSyAwJdEvE-ZPyGzAlJdqt_bMSsoSrHonniA" : "");
|
|
3260
|
-
}
|
|
3261
|
-
function getFirebaseProjectId() {
|
|
3262
|
-
return process.env.NUMO_FIREBASE_PROJECT_ID ?? (true ? "mindist-well" : "");
|
|
3263
|
-
}
|
|
3264
|
-
function getFirebaseAppId() {
|
|
3265
|
-
return process.env.NUMO_FIREBASE_APP_ID ?? (true ? "1:767603956046:web:d2c5911157e1cb017a261f" : "");
|
|
3266
|
-
}
|
|
3267
|
-
var init_config = __esm({
|
|
3268
|
-
"src/cli/lib/config.ts"() {
|
|
3269
|
-
"use strict";
|
|
3270
|
-
init_define_ADMIN_UIDS();
|
|
3271
|
-
}
|
|
3272
|
-
});
|
|
3273
|
-
|
|
3274
3220
|
// src/cli/lib/tty.ts
|
|
3275
3221
|
function isInteractive() {
|
|
3276
3222
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
@@ -3282,7 +3228,6 @@ var isUnicodeSupported;
|
|
|
3282
3228
|
var init_tty = __esm({
|
|
3283
3229
|
"src/cli/lib/tty.ts"() {
|
|
3284
3230
|
"use strict";
|
|
3285
|
-
init_define_ADMIN_UIDS();
|
|
3286
3231
|
isUnicodeSupported = process.platform !== "win32" || !!process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode";
|
|
3287
3232
|
}
|
|
3288
3233
|
});
|
|
@@ -3291,7 +3236,6 @@ var init_tty = __esm({
|
|
|
3291
3236
|
var require_src = __commonJS({
|
|
3292
3237
|
"node_modules/sisteransi/src/index.js"(exports2, module2) {
|
|
3293
3238
|
"use strict";
|
|
3294
|
-
init_define_ADMIN_UIDS();
|
|
3295
3239
|
var ESC = "\x1B";
|
|
3296
3240
|
var CSI = `${ESC}[`;
|
|
3297
3241
|
var beep = "\x07";
|
|
@@ -3433,7 +3377,6 @@ function St(t2, e) {
|
|
|
3433
3377
|
var import_node_util, import_node_process, k, import_node_readline, import_sisteransi, import_node_tty, at, lt, ht, O, y, L, P, M, ct, ft, X, pt, S, T, Z, Ft, j, Q, dt, tt, U, et, mt, st, it, gt, G, vt, Et, At, _, bt, z, rt, nt, B, Vt, kt, yt, Lt, Mt, Tt, Wt, $t;
|
|
3434
3378
|
var init_dist = __esm({
|
|
3435
3379
|
"node_modules/@clack/core/dist/index.mjs"() {
|
|
3436
|
-
init_define_ADMIN_UIDS();
|
|
3437
3380
|
import_node_util = require("node:util");
|
|
3438
3381
|
import_node_process = require("node:process");
|
|
3439
3382
|
k = __toESM(require("node:readline"), 1);
|
|
@@ -4100,7 +4043,6 @@ function qt({ style: e = "heavy", max: r = 100, size: s = 40, ...i } = {}) {
|
|
|
4100
4043
|
var import_node_util2, import_node_process2, import_node_fs, import_node_path, import_sisteransi2, ee, ce, Me, I2, Re, $e, de, V, he, h, x2, Oe, Pe, z2, H2, te, U2, q2, Ne, se, pe, We, me, ge, Ge, fe, Fe, ye, Ee, W2, ve, mt2, gt2, ft2, we, re, ie, Ae, ne, Ft2, yt2, Le, Et2, D2, ae, je, vt2, Ce, ke, wt2, Ve, Se, He, At2, Ue, Ke, Ct2, Ie, St2, It2, bt2, X2, Xe, xt2, _t2, Dt2, Tt2, Mt2, Rt, Ot, Pt, R2, Nt, Wt2, Gt, Q2, Lt2, jt, kt2, Vt2, Ht, Ut, Kt, be, ze, oe, Jt, Xt, Qe, K2, Yt, zt, Qt, Zt;
|
|
4101
4044
|
var init_dist2 = __esm({
|
|
4102
4045
|
"node_modules/@clack/prompts/dist/index.mjs"() {
|
|
4103
|
-
init_define_ADMIN_UIDS();
|
|
4104
4046
|
init_dist();
|
|
4105
4047
|
init_dist();
|
|
4106
4048
|
import_node_util2 = require("node:util");
|
|
@@ -5072,7 +5014,6 @@ async function promptForMissing(opts) {
|
|
|
5072
5014
|
var init_prompts = __esm({
|
|
5073
5015
|
"src/cli/lib/prompts.ts"() {
|
|
5074
5016
|
"use strict";
|
|
5075
|
-
init_define_ADMIN_UIDS();
|
|
5076
5017
|
init_tty();
|
|
5077
5018
|
}
|
|
5078
5019
|
});
|
|
@@ -5116,7 +5057,6 @@ var ExitCode, CliError, Errors;
|
|
|
5116
5057
|
var init_errors = __esm({
|
|
5117
5058
|
"src/cli/lib/errors.ts"() {
|
|
5118
5059
|
"use strict";
|
|
5119
|
-
init_define_ADMIN_UIDS();
|
|
5120
5060
|
ExitCode = {
|
|
5121
5061
|
OK: 0,
|
|
5122
5062
|
GENERAL: 1,
|
|
@@ -5182,142 +5122,12 @@ var init_errors = __esm({
|
|
|
5182
5122
|
}
|
|
5183
5123
|
});
|
|
5184
5124
|
|
|
5185
|
-
// src/cli/auth/local-server.ts
|
|
5186
|
-
function findFreePort() {
|
|
5187
|
-
return new Promise((resolve, reject) => {
|
|
5188
|
-
const server = net.createServer();
|
|
5189
|
-
server.listen(0, "127.0.0.1", () => {
|
|
5190
|
-
const address = server.address();
|
|
5191
|
-
server.close(() => resolve(address.port));
|
|
5192
|
-
});
|
|
5193
|
-
server.on("error", reject);
|
|
5194
|
-
});
|
|
5195
|
-
}
|
|
5196
|
-
function serveHtmlAndWaitForCallback(port, html, expectedState) {
|
|
5197
|
-
return new Promise((resolve, reject) => {
|
|
5198
|
-
const connections = /* @__PURE__ */ new Set();
|
|
5199
|
-
const server = http2.createServer((req, res) => {
|
|
5200
|
-
const parsed = url.parse(req.url ?? "", true);
|
|
5201
|
-
if (parsed.pathname === "/callback") {
|
|
5202
|
-
if (parsed.query.state !== expectedState) {
|
|
5203
|
-
res.writeHead(403, { "Content-Type": "text/html", "Connection": "close" });
|
|
5204
|
-
res.end("<html><body><h2>Invalid state parameter. Authentication rejected.</h2></body></html>");
|
|
5205
|
-
return;
|
|
5206
|
-
}
|
|
5207
|
-
res.writeHead(200, { "Content-Type": "text/html", "Connection": "close", "Cache-Control": "no-store" });
|
|
5208
|
-
res.end("<html><body><h2>Authentication complete. You can close this tab.</h2></body></html>");
|
|
5209
|
-
clearTimeout(timer);
|
|
5210
|
-
server.close();
|
|
5211
|
-
for (const conn of connections) conn.destroy();
|
|
5212
|
-
resolve(parsed.query);
|
|
5213
|
-
return;
|
|
5214
|
-
}
|
|
5215
|
-
if (parsed.pathname !== "/") {
|
|
5216
|
-
res.writeHead(404, { "Content-Type": "text/plain", "Connection": "close" });
|
|
5217
|
-
res.end("Not Found");
|
|
5218
|
-
return;
|
|
5219
|
-
}
|
|
5220
|
-
res.writeHead(200, {
|
|
5221
|
-
"Content-Type": "text/html",
|
|
5222
|
-
"Cache-Control": "no-store",
|
|
5223
|
-
"Content-Security-Policy": "default-src 'self' https://www.gstatic.com 'unsafe-inline'"
|
|
5224
|
-
});
|
|
5225
|
-
res.end(html);
|
|
5226
|
-
});
|
|
5227
|
-
server.on("connection", (conn) => {
|
|
5228
|
-
connections.add(conn);
|
|
5229
|
-
conn.on("close", () => connections.delete(conn));
|
|
5230
|
-
});
|
|
5231
|
-
server.listen(port, "127.0.0.1");
|
|
5232
|
-
server.on("error", reject);
|
|
5233
|
-
const timer = setTimeout(() => {
|
|
5234
|
-
server.close();
|
|
5235
|
-
for (const conn of connections) conn.destroy();
|
|
5236
|
-
reject(new Error("Timeout waiting for authentication (5 min)"));
|
|
5237
|
-
}, 5 * 60 * 1e3);
|
|
5238
|
-
timer.unref();
|
|
5239
|
-
});
|
|
5240
|
-
}
|
|
5241
|
-
var http2, net, url;
|
|
5242
|
-
var init_local_server = __esm({
|
|
5243
|
-
"src/cli/auth/local-server.ts"() {
|
|
5244
|
-
"use strict";
|
|
5245
|
-
init_define_ADMIN_UIDS();
|
|
5246
|
-
http2 = __toESM(require("http"), 1);
|
|
5247
|
-
net = __toESM(require("net"), 1);
|
|
5248
|
-
url = __toESM(require("url"), 1);
|
|
5249
|
-
}
|
|
5250
|
-
});
|
|
5251
|
-
|
|
5252
5125
|
// src/cli/auth/phone-login.ts
|
|
5253
5126
|
var phone_login_exports = {};
|
|
5254
5127
|
__export(phone_login_exports, {
|
|
5255
5128
|
authenticateWithPhone: () => authenticateWithPhone
|
|
5256
5129
|
});
|
|
5257
|
-
function buildSmsPageHtml(apiKey, projectId, appId, phoneNumber, callbackUrl, state) {
|
|
5258
|
-
const configJson = JSON.stringify({ apiKey, projectId, appId, phoneNumber, callbackUrl, state });
|
|
5259
|
-
return `<!DOCTYPE html>
|
|
5260
|
-
<html>
|
|
5261
|
-
<head>
|
|
5262
|
-
<meta charset="UTF-8">
|
|
5263
|
-
<title>Numo \u2014 Sending SMS</title>
|
|
5264
|
-
<style>
|
|
5265
|
-
body { font-family: sans-serif; max-width: 420px; margin: 80px auto; padding: 20px; text-align: center; }
|
|
5266
|
-
.spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid #ccc; border-top-color: #4285f4; border-radius: 50%; animation: spin 0.8s linear infinite; vertical-align: middle; margin-right: 8px; }
|
|
5267
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
5268
|
-
.error { color: #d32f2f; font-size: 14px; margin-top: 16px; }
|
|
5269
|
-
.success { color: #2e7d32; }
|
|
5270
|
-
</style>
|
|
5271
|
-
</head>
|
|
5272
|
-
<body>
|
|
5273
|
-
<h2>Numo</h2>
|
|
5274
|
-
<p id="status"><span class="spinner"></span> Sending SMS...</p>
|
|
5275
|
-
<p id="error" class="error"></p>
|
|
5276
|
-
|
|
5277
|
-
<div id="recaptcha-container"></div>
|
|
5278
|
-
|
|
5279
|
-
<script>window.__NUMO__ = ${configJson};</script>
|
|
5280
|
-
<script type="module">
|
|
5281
|
-
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.7.0/firebase-app.js';
|
|
5282
|
-
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber } from 'https://www.gstatic.com/firebasejs/10.7.0/firebase-auth.js';
|
|
5283
|
-
|
|
5284
|
-
const cfg = window.__NUMO__;
|
|
5285
|
-
const app = initializeApp({
|
|
5286
|
-
apiKey: cfg.apiKey,
|
|
5287
|
-
projectId: cfg.projectId,
|
|
5288
|
-
appId: cfg.appId,
|
|
5289
|
-
authDomain: cfg.projectId + '.firebaseapp.com',
|
|
5290
|
-
});
|
|
5291
|
-
const auth = getAuth(app);
|
|
5292
|
-
|
|
5293
|
-
try {
|
|
5294
|
-
const verifier = new RecaptchaVerifier(auth, 'recaptcha-container', { size: 'invisible' });
|
|
5295
|
-
const confirmationResult = await signInWithPhoneNumber(auth, cfg.phoneNumber, verifier);
|
|
5296
|
-
|
|
5297
|
-
const params = new URLSearchParams({ verificationId: confirmationResult.verificationId, state: cfg.state });
|
|
5298
|
-
document.getElementById('status').innerHTML = '<span class="success">SMS sent! Return to the terminal to enter the code.</span>';
|
|
5299
|
-
window.location.href = cfg.callbackUrl + '?' + params.toString();
|
|
5300
|
-
} catch (e) {
|
|
5301
|
-
document.getElementById('status').textContent = '';
|
|
5302
|
-
document.getElementById('error').textContent = e.message;
|
|
5303
|
-
}
|
|
5304
|
-
</script>
|
|
5305
|
-
</body>
|
|
5306
|
-
</html>`;
|
|
5307
|
-
}
|
|
5308
5130
|
async function authenticateWithPhone(spinner) {
|
|
5309
|
-
const fbApiKey = getFirebaseApiKey();
|
|
5310
|
-
const projectId = getFirebaseProjectId();
|
|
5311
|
-
const appId = getFirebaseAppId();
|
|
5312
|
-
if (!fbApiKey) {
|
|
5313
|
-
throw Errors.configMissing("NUMO_FIREBASE_API_KEY");
|
|
5314
|
-
}
|
|
5315
|
-
if (!projectId) {
|
|
5316
|
-
throw Errors.configMissing("NUMO_FIREBASE_PROJECT_ID");
|
|
5317
|
-
}
|
|
5318
|
-
if (!appId) {
|
|
5319
|
-
throw Errors.configMissing("NUMO_FIREBASE_APP_ID");
|
|
5320
|
-
}
|
|
5321
5131
|
const p = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
5322
5132
|
const phone = await promptText({
|
|
5323
5133
|
message: "Phone number (with country code)",
|
|
@@ -5327,63 +5137,55 @@ async function authenticateWithPhone(spinner) {
|
|
|
5327
5137
|
if (!/^\+[0-9]{7,15}$/.test(phone)) {
|
|
5328
5138
|
throw Errors.invalidInput("Invalid phone number. Use E.164 format: +<country code><number>", "Example: +380501234567");
|
|
5329
5139
|
}
|
|
5330
|
-
|
|
5331
|
-
const
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5140
|
+
spinner.start("Starting phone verification...");
|
|
5141
|
+
const startResp = await http.post(`${API_BASE}/api/auth/phone/start`, {
|
|
5142
|
+
phoneNumber: phone
|
|
5143
|
+
});
|
|
5144
|
+
const { sessionId, verifyUrl } = startResp.data;
|
|
5145
|
+
spinner.stop("");
|
|
5146
|
+
p.log.info("Opening browser for phone verification...");
|
|
5147
|
+
p.log.info(import_picocolors.default.dim(`If the browser does not open, visit: ${verifyUrl}`));
|
|
5336
5148
|
const { default: open } = await import("open");
|
|
5337
|
-
const cp = await open(
|
|
5149
|
+
const cp = await open(verifyUrl);
|
|
5338
5150
|
cp.unref();
|
|
5339
|
-
|
|
5340
|
-
const
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5151
|
+
spinner.start("Waiting for verification in browser...");
|
|
5152
|
+
const deadline = Date.now() + POLL_TIMEOUT;
|
|
5153
|
+
while (Date.now() < deadline) {
|
|
5154
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
5155
|
+
try {
|
|
5156
|
+
const pollResp = await http.get(`${API_BASE}/api/auth/phone/poll?session=${sessionId}`);
|
|
5157
|
+
if (pollResp.status === 200 && pollResp.data.idToken) {
|
|
5158
|
+
return {
|
|
5159
|
+
refreshToken: pollResp.data.refreshToken,
|
|
5160
|
+
uid: pollResp.data.uid,
|
|
5161
|
+
displayName: phone,
|
|
5162
|
+
idToken: pollResp.data.idToken,
|
|
5163
|
+
idTokenExpiry: Date.now() + (pollResp.data.expiresIn || 3600) * 1e3
|
|
5164
|
+
};
|
|
5165
|
+
}
|
|
5166
|
+
} catch (err) {
|
|
5167
|
+
if (err.response?.status === 404) {
|
|
5168
|
+
throw Errors.networkError("Verification session expired. Try again.");
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5358
5171
|
}
|
|
5359
|
-
|
|
5360
|
-
refreshToken,
|
|
5361
|
-
uid,
|
|
5362
|
-
displayName: phoneNumber || phone,
|
|
5363
|
-
idToken,
|
|
5364
|
-
idTokenExpiry: idToken ? Date.now() + 3600 * 1e3 : void 0
|
|
5365
|
-
};
|
|
5172
|
+
throw Errors.networkError("Phone verification timed out. Try again.");
|
|
5366
5173
|
}
|
|
5367
|
-
var
|
|
5174
|
+
var import_picocolors, API_BASE, POLL_INTERVAL, POLL_TIMEOUT;
|
|
5368
5175
|
var init_phone_login = __esm({
|
|
5369
5176
|
"src/cli/auth/phone-login.ts"() {
|
|
5370
5177
|
"use strict";
|
|
5371
|
-
init_define_ADMIN_UIDS();
|
|
5372
|
-
crypto2 = __toESM(require("crypto"), 1);
|
|
5373
5178
|
init_http();
|
|
5374
5179
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
5375
5180
|
init_errors();
|
|
5376
|
-
init_local_server();
|
|
5377
|
-
init_config();
|
|
5378
5181
|
init_prompts();
|
|
5182
|
+
API_BASE = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
5183
|
+
POLL_INTERVAL = 2e3;
|
|
5184
|
+
POLL_TIMEOUT = 5 * 60 * 1e3;
|
|
5379
5185
|
}
|
|
5380
5186
|
});
|
|
5381
5187
|
|
|
5382
|
-
// src/cli/cli.ts
|
|
5383
|
-
init_define_ADMIN_UIDS();
|
|
5384
|
-
|
|
5385
5188
|
// node_modules/commander/esm.mjs
|
|
5386
|
-
init_define_ADMIN_UIDS();
|
|
5387
5189
|
var import_index = __toESM(require_commander(), 1);
|
|
5388
5190
|
var {
|
|
5389
5191
|
program,
|
|
@@ -5404,17 +5206,14 @@ var {
|
|
|
5404
5206
|
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
5405
5207
|
|
|
5406
5208
|
// src/cli/auth/login.ts
|
|
5407
|
-
init_define_ADMIN_UIDS();
|
|
5408
5209
|
init_http();
|
|
5409
5210
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
5410
5211
|
|
|
5411
5212
|
// src/cli/auth/credentials.ts
|
|
5412
|
-
init_define_ADMIN_UIDS();
|
|
5413
5213
|
var fs2 = __toESM(require("fs"), 1);
|
|
5414
5214
|
var crypto = __toESM(require("crypto"), 1);
|
|
5415
5215
|
|
|
5416
5216
|
// src/cli/lib/dirs.ts
|
|
5417
|
-
init_define_ADMIN_UIDS();
|
|
5418
5217
|
var fs = __toESM(require("fs"), 1);
|
|
5419
5218
|
var path = __toESM(require("path"), 1);
|
|
5420
5219
|
var os = __toESM(require("os"), 1);
|
|
@@ -5501,81 +5300,45 @@ async function getIdToken() {
|
|
|
5501
5300
|
return refreshInFlight;
|
|
5502
5301
|
}
|
|
5503
5302
|
async function performRefresh(creds) {
|
|
5504
|
-
const
|
|
5505
|
-
const
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
`https://securetoken.googleapis.com/v1/token?key=${fbApiKey}`,
|
|
5510
|
-
{ grant_type: "refresh_token", refresh_token: creds.refreshToken },
|
|
5511
|
-
{ headers: { "Content-Type": "application/json" } }
|
|
5303
|
+
const apiBase = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
5304
|
+
const { http: http2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
5305
|
+
const resp = await http2.post(
|
|
5306
|
+
`${apiBase}/api/auth/refresh`,
|
|
5307
|
+
{ refreshToken: creds.refreshToken }
|
|
5512
5308
|
);
|
|
5513
|
-
creds.idToken = resp.data.
|
|
5514
|
-
creds.
|
|
5309
|
+
creds.idToken = resp.data.idToken;
|
|
5310
|
+
creds.refreshToken = resp.data.refreshToken ?? creds.refreshToken;
|
|
5311
|
+
creds.idTokenExpiry = Date.now() + (resp.data.expiresIn || 3600) * 1e3;
|
|
5515
5312
|
saveCredentials(creds);
|
|
5516
5313
|
return creds.idToken;
|
|
5517
5314
|
}
|
|
5518
5315
|
|
|
5519
5316
|
// src/cli/auth/login.ts
|
|
5520
|
-
init_config();
|
|
5521
5317
|
init_prompts();
|
|
5522
5318
|
init_errors();
|
|
5523
|
-
|
|
5524
|
-
// src/cli/lib/uid.ts
|
|
5525
|
-
init_define_ADMIN_UIDS();
|
|
5526
|
-
init_errors();
|
|
5527
|
-
var ADMIN_UIDS = typeof define_ADMIN_UIDS_default !== "undefined" ? define_ADMIN_UIDS_default : [];
|
|
5528
|
-
function requireUid() {
|
|
5529
|
-
const creds = loadCredentials();
|
|
5530
|
-
if (!creds) throw Errors.authRequired();
|
|
5531
|
-
return creds.uid;
|
|
5532
|
-
}
|
|
5533
|
-
function isAdmin() {
|
|
5534
|
-
const creds = loadCredentials();
|
|
5535
|
-
return !!creds && ADMIN_UIDS.includes(creds.uid);
|
|
5536
|
-
}
|
|
5537
|
-
function requireAdmin() {
|
|
5538
|
-
const creds = loadCredentials();
|
|
5539
|
-
if (!creds) throw Errors.authRequired();
|
|
5540
|
-
if (!ADMIN_UIDS.includes(creds.uid)) {
|
|
5541
|
-
throw new CliError("AUTH_FORBIDDEN" /* AUTH_FORBIDDEN */, "Admin access required", ExitCode.NO_PERM, {
|
|
5542
|
-
hint: "Your account does not have admin privileges."
|
|
5543
|
-
});
|
|
5544
|
-
}
|
|
5545
|
-
return creds.uid;
|
|
5546
|
-
}
|
|
5547
|
-
|
|
5548
|
-
// src/cli/auth/login.ts
|
|
5319
|
+
var API_BASE2 = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
5549
5320
|
async function authenticateWithEmail(spinner) {
|
|
5550
|
-
const fbApiKey = getFirebaseApiKey();
|
|
5551
|
-
if (!fbApiKey) {
|
|
5552
|
-
throw Errors.configMissing("NUMO_FIREBASE_API_KEY");
|
|
5553
|
-
}
|
|
5554
5321
|
const email = await promptText({ message: "Email", required: true });
|
|
5555
5322
|
const password = await promptPassword({ message: "Password" });
|
|
5556
5323
|
spinner.start("Signing in...");
|
|
5557
5324
|
const resp = await http.post(
|
|
5558
|
-
|
|
5559
|
-
{ email, password
|
|
5560
|
-
{ headers: { "Content-Type": "application/json" } }
|
|
5325
|
+
`${API_BASE2}/api/auth/login`,
|
|
5326
|
+
{ email, password }
|
|
5561
5327
|
);
|
|
5562
5328
|
return {
|
|
5563
5329
|
refreshToken: resp.data.refreshToken,
|
|
5564
|
-
uid: resp.data.
|
|
5565
|
-
displayName: resp.data.email,
|
|
5330
|
+
uid: resp.data.uid,
|
|
5331
|
+
displayName: resp.data.email ?? email,
|
|
5566
5332
|
idToken: resp.data.idToken,
|
|
5567
|
-
idTokenExpiry: Date.now() + (
|
|
5333
|
+
idTokenExpiry: Date.now() + (resp.data.expiresIn || 3600) * 1e3
|
|
5568
5334
|
};
|
|
5569
5335
|
}
|
|
5570
5336
|
function printSuccess(displayName) {
|
|
5571
5337
|
const lines = [
|
|
5572
5338
|
` ${import_picocolors2.default.dim("$")} numo tasks list --date YYYY-MM-DD List tasks for a date`,
|
|
5573
|
-
` ${import_picocolors2.default.dim("$")} numo tasks create --text "..." Create a task
|
|
5339
|
+
` ${import_picocolors2.default.dim("$")} numo tasks create --text "..." Create a task`,
|
|
5340
|
+
` ${import_picocolors2.default.dim("$")} numo profile View your profile`
|
|
5574
5341
|
];
|
|
5575
|
-
if (isAdmin()) {
|
|
5576
|
-
lines.push(` ${import_picocolors2.default.dim("$")} numo posts list Browse community posts`);
|
|
5577
|
-
}
|
|
5578
|
-
lines.push(` ${import_picocolors2.default.dim("$")} numo profile View your profile`);
|
|
5579
5342
|
console.log(`
|
|
5580
5343
|
${import_picocolors2.default.bold("Available commands:")}
|
|
5581
5344
|
${lines.join("\n")}
|
|
@@ -5623,176 +5386,12 @@ async function login(options = {}) {
|
|
|
5623
5386
|
}
|
|
5624
5387
|
|
|
5625
5388
|
// src/cli/auth/register.ts
|
|
5626
|
-
init_define_ADMIN_UIDS();
|
|
5627
5389
|
init_http();
|
|
5628
5390
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
5629
|
-
init_config();
|
|
5630
5391
|
init_prompts();
|
|
5631
5392
|
init_errors();
|
|
5632
|
-
|
|
5633
|
-
// src/cli/lib/firestore.ts
|
|
5634
|
-
init_define_ADMIN_UIDS();
|
|
5635
|
-
init_http();
|
|
5636
|
-
init_config();
|
|
5637
|
-
function toValue(v) {
|
|
5638
|
-
if (v === null || v === void 0) return { nullValue: null };
|
|
5639
|
-
if (typeof v === "string") return { stringValue: v };
|
|
5640
|
-
if (typeof v === "boolean") return { booleanValue: v };
|
|
5641
|
-
if (typeof v === "number") {
|
|
5642
|
-
return Number.isInteger(v) ? { integerValue: String(v) } : { doubleValue: v };
|
|
5643
|
-
}
|
|
5644
|
-
if (Array.isArray(v)) {
|
|
5645
|
-
return { arrayValue: { values: v.map(toValue) } };
|
|
5646
|
-
}
|
|
5647
|
-
if (typeof v === "object") {
|
|
5648
|
-
return { mapValue: { fields: toFirestoreFields(v) } };
|
|
5649
|
-
}
|
|
5650
|
-
return { stringValue: String(v) };
|
|
5651
|
-
}
|
|
5652
|
-
function toFirestoreFields(obj) {
|
|
5653
|
-
const fields = {};
|
|
5654
|
-
for (const [k2, v] of Object.entries(obj)) {
|
|
5655
|
-
fields[k2] = toValue(v);
|
|
5656
|
-
}
|
|
5657
|
-
return fields;
|
|
5658
|
-
}
|
|
5659
|
-
function fromValue(v) {
|
|
5660
|
-
if ("stringValue" in v) return v.stringValue;
|
|
5661
|
-
if ("integerValue" in v) return parseInt(v.integerValue, 10);
|
|
5662
|
-
if ("doubleValue" in v) return v.doubleValue;
|
|
5663
|
-
if ("booleanValue" in v) return v.booleanValue;
|
|
5664
|
-
if ("nullValue" in v) return null;
|
|
5665
|
-
if ("arrayValue" in v) return (v.arrayValue.values ?? []).map(fromValue);
|
|
5666
|
-
if ("mapValue" in v) return fromFirestoreFields(v.mapValue.fields ?? {});
|
|
5667
|
-
return null;
|
|
5668
|
-
}
|
|
5669
|
-
function fromFirestoreFields(fields) {
|
|
5670
|
-
const obj = {};
|
|
5671
|
-
for (const [k2, v] of Object.entries(fields)) {
|
|
5672
|
-
obj[k2] = fromValue(v);
|
|
5673
|
-
}
|
|
5674
|
-
return obj;
|
|
5675
|
-
}
|
|
5676
|
-
function fromFirestoreDoc(doc) {
|
|
5677
|
-
const id = doc.name.split("/").pop();
|
|
5678
|
-
return { id, ...doc.fields ? fromFirestoreFields(doc.fields) : {} };
|
|
5679
|
-
}
|
|
5680
|
-
async function authHeaders() {
|
|
5681
|
-
const idToken = await getIdToken();
|
|
5682
|
-
return { Authorization: `Bearer ${idToken}`, "Content-Type": "application/json" };
|
|
5683
|
-
}
|
|
5684
|
-
async function getDoc(path4) {
|
|
5685
|
-
const url2 = `${getFirestoreBaseUrl()}/${path4}`;
|
|
5686
|
-
const { data } = await http.get(url2, { headers: await authHeaders() });
|
|
5687
|
-
return fromFirestoreDoc(data);
|
|
5688
|
-
}
|
|
5689
|
-
async function createDoc(collectionPath, data) {
|
|
5690
|
-
const url2 = `${getFirestoreBaseUrl()}/${collectionPath}`;
|
|
5691
|
-
const resp = await http.post(url2, { fields: toFirestoreFields(data) }, { headers: await authHeaders() });
|
|
5692
|
-
return fromFirestoreDoc(resp.data);
|
|
5693
|
-
}
|
|
5694
|
-
async function setDoc(docPath, data) {
|
|
5695
|
-
const url2 = `${getFirestoreBaseUrl()}/${docPath}`;
|
|
5696
|
-
const resp = await http.patch(url2, { fields: toFirestoreFields(data) }, { headers: await authHeaders() });
|
|
5697
|
-
return fromFirestoreDoc(resp.data);
|
|
5698
|
-
}
|
|
5699
|
-
async function updateDoc(path4, data, fieldMask) {
|
|
5700
|
-
const url2 = `${getFirestoreBaseUrl()}/${path4}`;
|
|
5701
|
-
const qs = new URLSearchParams();
|
|
5702
|
-
for (const f of fieldMask) qs.append("updateMask.fieldPaths", f);
|
|
5703
|
-
const resp = await http.patch(`${url2}?${qs}`, { fields: toFirestoreFields(data) }, { headers: await authHeaders() });
|
|
5704
|
-
return fromFirestoreDoc(resp.data);
|
|
5705
|
-
}
|
|
5706
|
-
async function deleteDoc(path4) {
|
|
5707
|
-
const url2 = `${getFirestoreBaseUrl()}/${path4}`;
|
|
5708
|
-
await http.delete(url2, { headers: await authHeaders() });
|
|
5709
|
-
}
|
|
5710
|
-
async function runQuery(parentPath, collectionId, opts) {
|
|
5711
|
-
const base = getFirestoreBaseUrl();
|
|
5712
|
-
const url2 = parentPath ? `${base}/${parentPath}:runQuery` : `${base}:runQuery`;
|
|
5713
|
-
const structuredQuery = {
|
|
5714
|
-
from: [{ collectionId }]
|
|
5715
|
-
};
|
|
5716
|
-
if (opts.where && opts.where.length > 0) {
|
|
5717
|
-
if (opts.where.length === 1) {
|
|
5718
|
-
const w = opts.where[0];
|
|
5719
|
-
structuredQuery.where = {
|
|
5720
|
-
fieldFilter: {
|
|
5721
|
-
field: { fieldPath: w.field },
|
|
5722
|
-
op: w.op,
|
|
5723
|
-
value: toValue(w.value)
|
|
5724
|
-
}
|
|
5725
|
-
};
|
|
5726
|
-
} else {
|
|
5727
|
-
structuredQuery.where = {
|
|
5728
|
-
compositeFilter: {
|
|
5729
|
-
op: "AND",
|
|
5730
|
-
filters: opts.where.map((w) => ({
|
|
5731
|
-
fieldFilter: {
|
|
5732
|
-
field: { fieldPath: w.field },
|
|
5733
|
-
op: w.op,
|
|
5734
|
-
value: toValue(w.value)
|
|
5735
|
-
}
|
|
5736
|
-
}))
|
|
5737
|
-
}
|
|
5738
|
-
};
|
|
5739
|
-
}
|
|
5740
|
-
}
|
|
5741
|
-
if (opts.orderBy && opts.orderBy.length > 0) {
|
|
5742
|
-
structuredQuery.orderBy = opts.orderBy.map((o) => ({
|
|
5743
|
-
field: { fieldPath: o.field },
|
|
5744
|
-
direction: o.direction ?? "ASCENDING"
|
|
5745
|
-
}));
|
|
5746
|
-
}
|
|
5747
|
-
if (opts.limit) {
|
|
5748
|
-
structuredQuery.limit = opts.limit;
|
|
5749
|
-
}
|
|
5750
|
-
if (opts.startAfter && opts.startAfter.length > 0) {
|
|
5751
|
-
structuredQuery.startAt = {
|
|
5752
|
-
values: opts.startAfter.map(toValue),
|
|
5753
|
-
before: false
|
|
5754
|
-
};
|
|
5755
|
-
}
|
|
5756
|
-
const headers = await authHeaders();
|
|
5757
|
-
const { data } = await http.post(url2, { structuredQuery }, { headers });
|
|
5758
|
-
return data.filter((r) => r.document).map((r) => fromFirestoreDoc(r.document));
|
|
5759
|
-
}
|
|
5760
|
-
async function commit(writes) {
|
|
5761
|
-
const baseUrl = getFirestoreBaseUrl();
|
|
5762
|
-
const dbUrl = baseUrl.replace("/documents", "");
|
|
5763
|
-
const url2 = `${dbUrl}/documents:commit`;
|
|
5764
|
-
const toResourceName = (path4) => `${baseUrl}/${path4}`.replace("https://firestore.googleapis.com/v1/", "");
|
|
5765
|
-
const ops = writes.map((w) => {
|
|
5766
|
-
if (w.type === "delete") {
|
|
5767
|
-
return { delete: toResourceName(w.path) };
|
|
5768
|
-
}
|
|
5769
|
-
const op = {};
|
|
5770
|
-
if (w.type === "update" && w.data) {
|
|
5771
|
-
op.update = {
|
|
5772
|
-
name: toResourceName(w.path),
|
|
5773
|
-
fields: toFirestoreFields(w.data)
|
|
5774
|
-
};
|
|
5775
|
-
if (w.fieldMask) {
|
|
5776
|
-
op.updateMask = { fieldPaths: w.fieldMask };
|
|
5777
|
-
}
|
|
5778
|
-
}
|
|
5779
|
-
if (w.type === "transform") {
|
|
5780
|
-
op.update = {
|
|
5781
|
-
name: toResourceName(w.path),
|
|
5782
|
-
fields: {}
|
|
5783
|
-
};
|
|
5784
|
-
op.updateTransforms = (w.transforms ?? []).map((t2) => ({
|
|
5785
|
-
fieldPath: t2.field,
|
|
5786
|
-
increment: toValue(t2.increment)
|
|
5787
|
-
}));
|
|
5788
|
-
}
|
|
5789
|
-
return op;
|
|
5790
|
-
});
|
|
5791
|
-
await http.post(url2, { writes: ops }, { headers: await authHeaders() });
|
|
5792
|
-
}
|
|
5793
|
-
|
|
5794
|
-
// src/cli/auth/register.ts
|
|
5795
5393
|
init_tty();
|
|
5394
|
+
var API_BASE3 = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
5796
5395
|
function validateEmail(email) {
|
|
5797
5396
|
const trimmed = email.trim();
|
|
5798
5397
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
|
|
@@ -5806,92 +5405,44 @@ function validatePassword(password) {
|
|
|
5806
5405
|
}
|
|
5807
5406
|
return password;
|
|
5808
5407
|
}
|
|
5809
|
-
function generateUsername(email, uid) {
|
|
5810
|
-
const emailPart = email.split("@")[0].slice(0, 7);
|
|
5811
|
-
const uidPart = uid.slice(0, 4);
|
|
5812
|
-
return `${emailPart}-${uidPart}`;
|
|
5813
|
-
}
|
|
5814
|
-
function randomAvatar() {
|
|
5815
|
-
const index = Math.floor(Math.random() * 6) + 1;
|
|
5816
|
-
return `assets/avatar${index}-min.png`;
|
|
5817
|
-
}
|
|
5818
|
-
function buildUserDoc(email, uid, username, avatar, now2 = Date.now()) {
|
|
5819
|
-
return {
|
|
5820
|
-
email,
|
|
5821
|
-
userId: uid,
|
|
5822
|
-
signUpTag: "Numo Sign up",
|
|
5823
|
-
createdAt: now2,
|
|
5824
|
-
username,
|
|
5825
|
-
defaultAvatar: avatar,
|
|
5826
|
-
preference: {
|
|
5827
|
-
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
5828
|
-
timezones: [(/* @__PURE__ */ new Date()).getTimezoneOffset()]
|
|
5829
|
-
},
|
|
5830
|
-
fcmToken: [],
|
|
5831
|
-
badges: [],
|
|
5832
|
-
reminderPlatform: "imessage",
|
|
5833
|
-
onboarding: {},
|
|
5834
|
-
postOnboardingV1Status: "not-completed"
|
|
5835
|
-
};
|
|
5836
|
-
}
|
|
5837
5408
|
function classifySignUpError(err) {
|
|
5838
5409
|
if (err instanceof CliError) return err;
|
|
5839
5410
|
const resp = err?.response?.data?.error;
|
|
5411
|
+
const kind = resp?.kind ?? "";
|
|
5840
5412
|
const msg = resp?.message ?? "";
|
|
5841
|
-
if (msg.includes("
|
|
5413
|
+
if (kind === "CONFLICT" || msg.includes("already in use")) {
|
|
5842
5414
|
return Errors.invalidInput(
|
|
5843
5415
|
"Email already in use",
|
|
5844
5416
|
"Already have an account? Run: numo login"
|
|
5845
5417
|
);
|
|
5846
5418
|
}
|
|
5847
|
-
if (msg.includes("
|
|
5419
|
+
if (msg.includes("Invalid email")) {
|
|
5848
5420
|
return Errors.invalidInput("Invalid email address");
|
|
5849
5421
|
}
|
|
5850
|
-
if (msg.includes("
|
|
5422
|
+
if (msg.includes("Password too weak") || msg.includes("min 6")) {
|
|
5851
5423
|
return Errors.invalidInput("Password is too weak (min 6 characters)");
|
|
5852
5424
|
}
|
|
5853
|
-
if (msg.includes("OPERATION_NOT_ALLOWED")) {
|
|
5854
|
-
return new CliError(
|
|
5855
|
-
"AUTH_FORBIDDEN" /* AUTH_FORBIDDEN */,
|
|
5856
|
-
"Email registration is disabled",
|
|
5857
|
-
ExitCode.NO_PERM
|
|
5858
|
-
);
|
|
5859
|
-
}
|
|
5860
5425
|
return classifyError(err);
|
|
5861
5426
|
}
|
|
5862
5427
|
async function signUp(email, password) {
|
|
5863
|
-
const fbApiKey = getFirebaseApiKey();
|
|
5864
|
-
if (!fbApiKey) throw Errors.configMissing("NUMO_FIREBASE_API_KEY");
|
|
5865
5428
|
try {
|
|
5866
|
-
const resp = await http.post(
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5429
|
+
const resp = await http.post(`${API_BASE3}/api/auth/register`, {
|
|
5430
|
+
email,
|
|
5431
|
+
password,
|
|
5432
|
+
tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
5433
|
+
tzOffset: (/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
5434
|
+
});
|
|
5871
5435
|
return {
|
|
5872
5436
|
refreshToken: resp.data.refreshToken,
|
|
5873
|
-
uid: resp.data.
|
|
5874
|
-
displayName: resp.data.email,
|
|
5437
|
+
uid: resp.data.uid,
|
|
5438
|
+
displayName: resp.data.email ?? email,
|
|
5875
5439
|
idToken: resp.data.idToken,
|
|
5876
|
-
idTokenExpiry: Date.now() + (
|
|
5440
|
+
idTokenExpiry: Date.now() + (resp.data.expiresIn || 3600) * 1e3
|
|
5877
5441
|
};
|
|
5878
5442
|
} catch (err) {
|
|
5879
5443
|
throw classifySignUpError(err);
|
|
5880
5444
|
}
|
|
5881
5445
|
}
|
|
5882
|
-
async function setupUserProfile(uid, email) {
|
|
5883
|
-
const now2 = Date.now();
|
|
5884
|
-
const username = generateUsername(email, uid);
|
|
5885
|
-
const avatar = randomAvatar();
|
|
5886
|
-
await setDoc(`users/${uid}`, buildUserDoc(email, uid, username, avatar, now2));
|
|
5887
|
-
await commit([
|
|
5888
|
-
{
|
|
5889
|
-
type: "transform",
|
|
5890
|
-
path: "appLinks/users",
|
|
5891
|
-
transforms: [{ field: "count", increment: 1 }]
|
|
5892
|
-
}
|
|
5893
|
-
]);
|
|
5894
|
-
}
|
|
5895
5446
|
async function register(options = {}) {
|
|
5896
5447
|
const p = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
5897
5448
|
p.intro(import_picocolors3.default.bold("Numo \u2014 Register"));
|
|
@@ -5919,8 +5470,6 @@ async function register(options = {}) {
|
|
|
5919
5470
|
idToken: result.idToken,
|
|
5920
5471
|
idTokenExpiry: result.idTokenExpiry
|
|
5921
5472
|
});
|
|
5922
|
-
s.message("Setting up profile...");
|
|
5923
|
-
await setupUserProfile(result.uid, email);
|
|
5924
5473
|
s.stop(`Registered as ${import_picocolors3.default.green(result.displayName)}`);
|
|
5925
5474
|
p.outro("Welcome to Numo!");
|
|
5926
5475
|
printSuccess(result.displayName);
|
|
@@ -5933,86 +5482,17 @@ async function register(options = {}) {
|
|
|
5933
5482
|
}
|
|
5934
5483
|
}
|
|
5935
5484
|
|
|
5936
|
-
// src/cli/lib/streaks.ts
|
|
5937
|
-
init_define_ADMIN_UIDS();
|
|
5938
|
-
var fs3 = __toESM(require("fs"), 1);
|
|
5939
|
-
var path2 = __toESM(require("path"), 1);
|
|
5940
|
-
function getStreaksPath() {
|
|
5941
|
-
return path2.join(getConfigDir(), "streaks.json");
|
|
5942
|
-
}
|
|
5943
|
-
function loadStreaks() {
|
|
5944
|
-
try {
|
|
5945
|
-
const raw = fs3.readFileSync(getStreaksPath(), "utf-8");
|
|
5946
|
-
return JSON.parse(raw);
|
|
5947
|
-
} catch {
|
|
5948
|
-
return [];
|
|
5949
|
-
}
|
|
5950
|
-
}
|
|
5951
|
-
function saveStreaks(entries) {
|
|
5952
|
-
const dir = path2.dirname(getStreaksPath());
|
|
5953
|
-
if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
|
|
5954
|
-
fs3.writeFileSync(getStreaksPath(), JSON.stringify(entries, null, 2), { mode: 384 });
|
|
5955
|
-
}
|
|
5956
|
-
function clearStreaks() {
|
|
5957
|
-
try {
|
|
5958
|
-
fs3.unlinkSync(getStreaksPath());
|
|
5959
|
-
} catch {
|
|
5960
|
-
}
|
|
5961
|
-
}
|
|
5962
|
-
function recordDailyStreak(taskId, date) {
|
|
5963
|
-
const entries = loadStreaks();
|
|
5964
|
-
const dateStr = date.slice(0, 10);
|
|
5965
|
-
const existing = entries.find((e) => e.taskId === taskId);
|
|
5966
|
-
if (existing) {
|
|
5967
|
-
const lastDate = new Date(existing.lastCheck);
|
|
5968
|
-
const thisDate = new Date(dateStr);
|
|
5969
|
-
const diffMs = thisDate.getTime() - lastDate.getTime();
|
|
5970
|
-
const diffDays2 = Math.floor(diffMs / 864e5);
|
|
5971
|
-
if (diffDays2 === 1) {
|
|
5972
|
-
existing.checksInRow += 1;
|
|
5973
|
-
} else if (diffDays2 > 1) {
|
|
5974
|
-
existing.checksInRow = 1;
|
|
5975
|
-
}
|
|
5976
|
-
existing.lastCheck = dateStr;
|
|
5977
|
-
saveStreaks(entries);
|
|
5978
|
-
return existing.checksInRow;
|
|
5979
|
-
}
|
|
5980
|
-
entries.push({ taskId, lastCheck: dateStr, checksInRow: 1 });
|
|
5981
|
-
saveStreaks(entries);
|
|
5982
|
-
return 1;
|
|
5983
|
-
}
|
|
5984
|
-
function revertDailyStreak(taskId) {
|
|
5985
|
-
const entries = loadStreaks();
|
|
5986
|
-
const idx = entries.findIndex((e) => e.taskId === taskId);
|
|
5987
|
-
if (idx !== -1) {
|
|
5988
|
-
entries[idx].checksInRow = Math.max(1, entries[idx].checksInRow - 1);
|
|
5989
|
-
saveStreaks(entries);
|
|
5990
|
-
}
|
|
5991
|
-
}
|
|
5992
|
-
function removeDailyStreak(taskId) {
|
|
5993
|
-
const entries = loadStreaks().filter((e) => e.taskId !== taskId);
|
|
5994
|
-
saveStreaks(entries);
|
|
5995
|
-
}
|
|
5996
|
-
function getCompletedTodayCount() {
|
|
5997
|
-
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5998
|
-
return loadStreaks().filter((e) => e.lastCheck === today2).length;
|
|
5999
|
-
}
|
|
6000
|
-
|
|
6001
5485
|
// src/cli/commands/tasks.ts
|
|
6002
|
-
init_define_ADMIN_UIDS();
|
|
6003
5486
|
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
6004
5487
|
|
|
6005
5488
|
// src/cli/lib/actions.ts
|
|
6006
|
-
init_define_ADMIN_UIDS();
|
|
6007
5489
|
init_tty();
|
|
6008
5490
|
|
|
6009
5491
|
// src/cli/lib/output.ts
|
|
6010
|
-
init_define_ADMIN_UIDS();
|
|
6011
5492
|
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
6012
5493
|
init_tty();
|
|
6013
5494
|
|
|
6014
5495
|
// src/cli/lib/table.ts
|
|
6015
|
-
init_define_ADMIN_UIDS();
|
|
6016
5496
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
6017
5497
|
init_tty();
|
|
6018
5498
|
var BOX = isUnicodeSupported ? {
|
|
@@ -6155,12 +5635,10 @@ ${import_picocolors5.default.red("Error")}: ${structured.message}`);
|
|
|
6155
5635
|
}
|
|
6156
5636
|
|
|
6157
5637
|
// src/cli/lib/spinner.ts
|
|
6158
|
-
init_define_ADMIN_UIDS();
|
|
6159
5638
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
6160
5639
|
init_tty();
|
|
6161
5640
|
|
|
6162
5641
|
// src/cli/lib/symbols.ts
|
|
6163
|
-
init_define_ADMIN_UIDS();
|
|
6164
5642
|
init_tty();
|
|
6165
5643
|
var u = isUnicodeSupported;
|
|
6166
5644
|
var SYM = {
|
|
@@ -6359,7 +5837,7 @@ async function runWrite(opts) {
|
|
|
6359
5837
|
if (useJson(opts.global)) {
|
|
6360
5838
|
printJson(selectFields(payload, opts.global.json));
|
|
6361
5839
|
} else if (opts.onInteractive) {
|
|
6362
|
-
opts.onInteractive(
|
|
5840
|
+
opts.onInteractive(payload);
|
|
6363
5841
|
} else {
|
|
6364
5842
|
if (opts.successMessage) console.log(opts.successMessage);
|
|
6365
5843
|
if (item && Object.keys(item).length > 0) {
|
|
@@ -6385,642 +5863,143 @@ async function runDelete(opts) {
|
|
|
6385
5863
|
}
|
|
6386
5864
|
}
|
|
6387
5865
|
|
|
6388
|
-
// src/cli/
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
// src/shared/types/task.ts
|
|
6396
|
-
init_define_ADMIN_UIDS();
|
|
6397
|
-
|
|
6398
|
-
// src/shared/types/post.ts
|
|
6399
|
-
init_define_ADMIN_UIDS();
|
|
6400
|
-
|
|
6401
|
-
// src/shared/types/comment.ts
|
|
6402
|
-
init_define_ADMIN_UIDS();
|
|
6403
|
-
|
|
6404
|
-
// src/shared/types/reply.ts
|
|
6405
|
-
init_define_ADMIN_UIDS();
|
|
6406
|
-
|
|
6407
|
-
// src/shared/constants.ts
|
|
6408
|
-
init_define_ADMIN_UIDS();
|
|
6409
|
-
var POST_TAGS = [
|
|
6410
|
-
"general",
|
|
6411
|
-
"hack",
|
|
6412
|
-
"story",
|
|
6413
|
-
"meme",
|
|
6414
|
-
"other",
|
|
6415
|
-
"question",
|
|
6416
|
-
"hack-tip",
|
|
6417
|
-
"activity"
|
|
6418
|
-
];
|
|
6419
|
-
var MAX_TASKS_PER_REQUEST = 200;
|
|
6420
|
-
var KARMA_POINTS = {
|
|
6421
|
-
addTask: 2,
|
|
6422
|
-
completeTask: 5,
|
|
6423
|
-
completeSubtask: [2, 5],
|
|
6424
|
-
splitTask: 10,
|
|
6425
|
-
createPost: 10,
|
|
6426
|
-
addComment: 10
|
|
6427
|
-
};
|
|
6428
|
-
var DIFFICULTY_BONUS = [0, 5, 15, 45];
|
|
6429
|
-
var MAX_KARMA_PER_COMPLETE = 100;
|
|
6430
|
-
var MAX_POSTS_PER_REQUEST = 50;
|
|
6431
|
-
var MAX_COMMENTS_PER_REQUEST = 100;
|
|
6432
|
-
var MAX_REPLIES_PER_REQUEST = 100;
|
|
5866
|
+
// src/cli/lib/uid.ts
|
|
5867
|
+
init_errors();
|
|
5868
|
+
function requireUid() {
|
|
5869
|
+
const creds = loadCredentials();
|
|
5870
|
+
if (!creds) throw Errors.authRequired();
|
|
5871
|
+
return creds.uid;
|
|
5872
|
+
}
|
|
6433
5873
|
|
|
6434
|
-
// src/cli/lib/
|
|
6435
|
-
|
|
5874
|
+
// src/cli/lib/api-client.ts
|
|
5875
|
+
init_http();
|
|
6436
5876
|
init_errors();
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
5877
|
+
var API_BASE4 = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
5878
|
+
if (API_BASE4 !== "http://localhost:3000" && API_BASE4.startsWith("http://")) {
|
|
5879
|
+
process.stderr.write("[warn] NUMO_API_URL uses HTTP \u2014 tokens sent unencrypted. Use HTTPS in production.\n");
|
|
5880
|
+
}
|
|
5881
|
+
var KIND_EXIT = {
|
|
5882
|
+
AUTH_REQUIRED: ExitCode.NO_PERM,
|
|
5883
|
+
AUTH_EXPIRED: ExitCode.NO_PERM,
|
|
5884
|
+
AUTH_FORBIDDEN: ExitCode.NO_PERM,
|
|
5885
|
+
INVALID_INPUT: ExitCode.USAGE,
|
|
5886
|
+
MISSING_ARGUMENT: ExitCode.USAGE,
|
|
5887
|
+
NOT_FOUND: ExitCode.NOT_FOUND,
|
|
5888
|
+
CONFLICT: ExitCode.CONFLICT,
|
|
5889
|
+
RATE_LIMITED: ExitCode.TEMP_FAIL,
|
|
5890
|
+
NETWORK_ERROR: ExitCode.UNAVAILABLE,
|
|
5891
|
+
TIMEOUT: ExitCode.TEMP_FAIL,
|
|
5892
|
+
SERVICE_UNAVAILABLE: ExitCode.UNAVAILABLE
|
|
5893
|
+
};
|
|
5894
|
+
function toCliError(err) {
|
|
5895
|
+
if (err instanceof CliError) return err;
|
|
5896
|
+
const httpErr = err;
|
|
5897
|
+
if (httpErr.code === "ECONNABORTED" || httpErr.code === "ETIMEDOUT") {
|
|
5898
|
+
return new CliError("TIMEOUT" /* TIMEOUT */, "Request timed out", ExitCode.TEMP_FAIL, {
|
|
5899
|
+
hint: "The API server took too long to respond.",
|
|
5900
|
+
retryable: true
|
|
5901
|
+
});
|
|
6456
5902
|
}
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
throw new CliError("AUTH_FORBIDDEN" /* AUTH_FORBIDDEN */, `You can only ${action} your own content`, ExitCode.NO_PERM);
|
|
5903
|
+
if (httpErr.code === "ECONNREFUSED" || httpErr.code === "ECONNRESET" || httpErr.code === "ENOTFOUND") {
|
|
5904
|
+
return new CliError("NETWORK_ERROR" /* NETWORK_ERROR */, "Can't reach Numo API", ExitCode.UNAVAILABLE, {
|
|
5905
|
+
hint: "Is the API server running? Check NUMO_API_URL.",
|
|
5906
|
+
retryable: true
|
|
5907
|
+
});
|
|
6463
5908
|
}
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
// src/cli/lib/karma.ts
|
|
6474
|
-
init_define_ADMIN_UIDS();
|
|
6475
|
-
function karmaPath(uid, id) {
|
|
6476
|
-
return `users/${uid}/karma/${id}`;
|
|
6477
|
-
}
|
|
6478
|
-
async function giveKarma(uid, entity, entityId, karma, text) {
|
|
6479
|
-
const id = `${entity}_${entityId}`;
|
|
6480
|
-
const record = { entity, entityId, karma, text, createdAt: Date.now(), userId: uid };
|
|
6481
|
-
await commit([
|
|
6482
|
-
{ type: "update", path: karmaPath(uid, id), data: record },
|
|
6483
|
-
{ type: "transform", path: `users/${uid}`, transforms: [{ field: "karmaCount", increment: karma }] }
|
|
6484
|
-
]);
|
|
6485
|
-
}
|
|
6486
|
-
async function removeKarma(uid, entity, entityId) {
|
|
6487
|
-
const id = `${entity}_${entityId}`;
|
|
6488
|
-
try {
|
|
6489
|
-
const doc = await getDoc(karmaPath(uid, id));
|
|
6490
|
-
const karma = doc.karma ?? 0;
|
|
6491
|
-
const writes = [
|
|
6492
|
-
{ type: "delete", path: karmaPath(uid, id) }
|
|
6493
|
-
];
|
|
6494
|
-
if (karma > 0) {
|
|
6495
|
-
writes.push({
|
|
6496
|
-
type: "transform",
|
|
6497
|
-
path: `users/${uid}`,
|
|
6498
|
-
transforms: [{ field: "karmaCount", increment: -karma }]
|
|
6499
|
-
});
|
|
6500
|
-
}
|
|
6501
|
-
await commit(writes);
|
|
6502
|
-
} catch {
|
|
5909
|
+
const body = httpErr.response?.data;
|
|
5910
|
+
if (body?.error) {
|
|
5911
|
+
const e = body.error;
|
|
5912
|
+
const kind = e.kind ?? "UNKNOWN" /* UNKNOWN */;
|
|
5913
|
+
const exitCode = KIND_EXIT[kind] ?? ExitCode.GENERAL;
|
|
5914
|
+
return new CliError(kind, e.message ?? "Unknown error", exitCode, {
|
|
5915
|
+
retryable: e.retryable,
|
|
5916
|
+
retryAfter: e.retryAfter
|
|
5917
|
+
});
|
|
6503
5918
|
}
|
|
5919
|
+
return new CliError("UNKNOWN" /* UNKNOWN */, httpErr.message ?? "Unknown error", ExitCode.GENERAL);
|
|
6504
5920
|
}
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
const [y2, m, d] = datePart.split("-").map(Number);
|
|
6511
|
-
if (timePart) {
|
|
6512
|
-
const [h2, min] = timePart.split(":").map(Number);
|
|
6513
|
-
return new Date(y2, m - 1, d, h2, min);
|
|
6514
|
-
}
|
|
6515
|
-
return new Date(y2, m - 1, d, 0, 0);
|
|
6516
|
-
}
|
|
6517
|
-
function formatDate(d) {
|
|
6518
|
-
const yyyy = d.getFullYear();
|
|
6519
|
-
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
6520
|
-
const dd = String(d.getDate()).padStart(2, "0");
|
|
6521
|
-
const hh = String(d.getHours()).padStart(2, "0");
|
|
6522
|
-
const min = String(d.getMinutes()).padStart(2, "0");
|
|
6523
|
-
return `${yyyy}-${mm}-${dd} ${hh}:${min}`;
|
|
6524
|
-
}
|
|
6525
|
-
function endOfDay(dateStr) {
|
|
6526
|
-
return dateStr.slice(0, 10) + " 23:59";
|
|
6527
|
-
}
|
|
6528
|
-
function todayFormatted() {
|
|
6529
|
-
return formatDate(startOfDay(/* @__PURE__ */ new Date()));
|
|
6530
|
-
}
|
|
6531
|
-
function startOfDay(d) {
|
|
6532
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
6533
|
-
}
|
|
6534
|
-
function endOfDayDate(d) {
|
|
6535
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
|
|
6536
|
-
}
|
|
6537
|
-
function addDays(d, n) {
|
|
6538
|
-
const r = new Date(d);
|
|
6539
|
-
r.setDate(r.getDate() + n);
|
|
6540
|
-
return r;
|
|
6541
|
-
}
|
|
6542
|
-
function addMonths(d, n) {
|
|
6543
|
-
const r = new Date(d);
|
|
6544
|
-
r.setMonth(r.getMonth() + n);
|
|
6545
|
-
return r;
|
|
6546
|
-
}
|
|
6547
|
-
function diffDays(a, b) {
|
|
6548
|
-
return Math.floor((a.getTime() - b.getTime()) / 864e5);
|
|
6549
|
-
}
|
|
6550
|
-
function diffWeeks(a, b) {
|
|
6551
|
-
return Math.floor(diffDays(a, b) / 7);
|
|
6552
|
-
}
|
|
6553
|
-
function diffMonths(a, b) {
|
|
6554
|
-
return (a.getFullYear() - b.getFullYear()) * 12 + (a.getMonth() - b.getMonth());
|
|
6555
|
-
}
|
|
6556
|
-
function getClosestNextWeekDay(baseDate, weekDays) {
|
|
6557
|
-
const dayNums = new Set(weekDays.map((d) => WEEKDAY_MAP[d]));
|
|
6558
|
-
for (let offset = 1; offset <= 7; offset++) {
|
|
6559
|
-
const candidate = addDays(baseDate, offset);
|
|
6560
|
-
if (dayNums.has(candidate.getDay())) {
|
|
6561
|
-
candidate.setHours(baseDate.getHours(), baseDate.getMinutes(), 0, 0);
|
|
6562
|
-
return candidate;
|
|
6563
|
-
}
|
|
6564
|
-
}
|
|
6565
|
-
return addDays(baseDate, 7);
|
|
6566
|
-
}
|
|
6567
|
-
function isRepeating(task) {
|
|
6568
|
-
return task.repeat?.type !== "none";
|
|
6569
|
-
}
|
|
6570
|
-
function getTaskRemindDate(task) {
|
|
6571
|
-
if (!task.dueDate || task.completed) return null;
|
|
6572
|
-
const d = parseDate(task.dueDate);
|
|
6573
|
-
if (d.getHours() === 0 && d.getMinutes() === 0) return null;
|
|
6574
|
-
const utcY = d.getUTCFullYear();
|
|
6575
|
-
const utcM = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
6576
|
-
const utcD = String(d.getUTCDate()).padStart(2, "0");
|
|
6577
|
-
const utcH = String(d.getUTCHours()).padStart(2, "0");
|
|
6578
|
-
const utcMin = String(d.getUTCMinutes()).padStart(2, "0");
|
|
6579
|
-
return `${utcY}-${utcM}-${utcD} ${utcH}:${utcMin}`;
|
|
6580
|
-
}
|
|
6581
|
-
function getNextDueDate(task) {
|
|
6582
|
-
if (!task.dueDate) return todayFormatted();
|
|
6583
|
-
const { repeat } = task;
|
|
6584
|
-
const every = repeat.every ?? 1;
|
|
6585
|
-
const now2 = endOfDayDate(/* @__PURE__ */ new Date());
|
|
6586
|
-
let nextDate = parseDate(task.dueDate);
|
|
6587
|
-
const calculatePeriods = (diffFn) => {
|
|
6588
|
-
const diff = diffFn(now2, nextDate);
|
|
6589
|
-
const passedPeriods = Math.max(0, Math.floor(diff / every));
|
|
6590
|
-
return (passedPeriods + 1) * every;
|
|
5921
|
+
async function apiHeaders() {
|
|
5922
|
+
const token = await getIdToken();
|
|
5923
|
+
return {
|
|
5924
|
+
Authorization: `Bearer ${token}`,
|
|
5925
|
+
"Content-Type": "application/json"
|
|
6591
5926
|
};
|
|
6592
|
-
if (repeat.type === "daily") {
|
|
6593
|
-
nextDate = addDays(nextDate, calculatePeriods(diffDays));
|
|
6594
|
-
} else if (repeat.type === "weekly") {
|
|
6595
|
-
if (repeat.weekDays && repeat.weekDays.length > 0) {
|
|
6596
|
-
const closestNext = getClosestNextWeekDay(parseDate(task.dueDate), repeat.weekDays);
|
|
6597
|
-
if (now2 < closestNext) {
|
|
6598
|
-
nextDate = closestNext;
|
|
6599
|
-
} else {
|
|
6600
|
-
const periods = calculatePeriods(diffWeeks);
|
|
6601
|
-
const advanced = addDays(nextDate, periods * 7);
|
|
6602
|
-
const dayDiff = (closestNext.getDay() - advanced.getDay() + 7) % 7;
|
|
6603
|
-
nextDate = addDays(advanced, dayDiff);
|
|
6604
|
-
nextDate.setHours(parseDate(task.dueDate).getHours(), parseDate(task.dueDate).getMinutes(), 0, 0);
|
|
6605
|
-
}
|
|
6606
|
-
} else {
|
|
6607
|
-
nextDate = addDays(nextDate, calculatePeriods(diffWeeks) * 7);
|
|
6608
|
-
}
|
|
6609
|
-
} else if (repeat.type === "monthly") {
|
|
6610
|
-
nextDate = addMonths(nextDate, calculatePeriods(diffMonths));
|
|
6611
|
-
}
|
|
6612
|
-
return formatDate(nextDate);
|
|
6613
|
-
}
|
|
6614
|
-
function taskPath(uid, id) {
|
|
6615
|
-
return `users/${uid}/tasks/${id}`;
|
|
6616
|
-
}
|
|
6617
|
-
function taskHistoryPath(uid, id) {
|
|
6618
|
-
return `users/${uid}/tasksHistory/${id}`;
|
|
6619
|
-
}
|
|
6620
|
-
function orderingPath(uid) {
|
|
6621
|
-
return `users/${uid}/order/tasks`;
|
|
6622
|
-
}
|
|
6623
|
-
function progressPath(uid, date) {
|
|
6624
|
-
return `users/${uid}/progress/${date}`;
|
|
6625
5927
|
}
|
|
6626
|
-
function
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
function archiveCounterPath(uid) {
|
|
6633
|
-
return `archive/${uid}`;
|
|
6634
|
-
}
|
|
6635
|
-
function activityTotalsPath(uid) {
|
|
6636
|
-
return `users/${uid}/activity/totals`;
|
|
6637
|
-
}
|
|
6638
|
-
function reversedTimestamp(len) {
|
|
6639
|
-
const maxTs = 9999999999999;
|
|
6640
|
-
const reversed = String(maxTs - Date.now());
|
|
6641
|
-
return reversed.slice(0, len);
|
|
6642
|
-
}
|
|
6643
|
-
function randomAlphanumeric(len) {
|
|
6644
|
-
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
6645
|
-
const limit = 252;
|
|
6646
|
-
let result = "";
|
|
6647
|
-
while (result.length < len) {
|
|
6648
|
-
const bytes = crypto3.randomBytes(len - result.length);
|
|
6649
|
-
for (let i = 0; i < bytes.length && result.length < len; i++) {
|
|
6650
|
-
if (bytes[i] < limit) result += chars[bytes[i] % chars.length];
|
|
6651
|
-
}
|
|
5928
|
+
function url(path3, params) {
|
|
5929
|
+
const u2 = `${API_BASE4}${path3}`;
|
|
5930
|
+
if (!params) return u2;
|
|
5931
|
+
const sp = new URLSearchParams();
|
|
5932
|
+
for (const [k2, v] of Object.entries(params)) {
|
|
5933
|
+
if (v !== void 0) sp.set(k2, v);
|
|
6652
5934
|
}
|
|
6653
|
-
|
|
5935
|
+
const qs = sp.toString();
|
|
5936
|
+
return qs ? `${u2}?${qs}` : u2;
|
|
6654
5937
|
}
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
}
|
|
6663
|
-
function logSideEffectResults(label, results) {
|
|
6664
|
-
for (const r of results) {
|
|
6665
|
-
if (r.status === "rejected") {
|
|
6666
|
-
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
6667
|
-
process.stderr.write(`[warn] ${label}: ${msg}
|
|
6668
|
-
`);
|
|
5938
|
+
var api = {
|
|
5939
|
+
async get(path3, params) {
|
|
5940
|
+
try {
|
|
5941
|
+
const resp = await http.get(url(path3, params), { headers: await apiHeaders() });
|
|
5942
|
+
return resp.data;
|
|
5943
|
+
} catch (err) {
|
|
5944
|
+
throw toCliError(err);
|
|
6669
5945
|
}
|
|
6670
|
-
}
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
} catch {
|
|
6678
|
-
}
|
|
6679
|
-
if (!ordering.includes(taskId)) {
|
|
6680
|
-
if (position === "start") ordering.unshift(taskId);
|
|
6681
|
-
else ordering.push(taskId);
|
|
6682
|
-
}
|
|
6683
|
-
await updateDoc(orderingPath(uid), { tasksListOrdering: ordering }, ["tasksListOrdering"]);
|
|
6684
|
-
}
|
|
6685
|
-
async function removeFromOrdering(uid, taskId) {
|
|
6686
|
-
try {
|
|
6687
|
-
const doc = await getDoc(orderingPath(uid));
|
|
6688
|
-
if (Array.isArray(doc.tasksListOrdering)) {
|
|
6689
|
-
const ordering = doc.tasksListOrdering.filter((id) => id !== taskId);
|
|
6690
|
-
await updateDoc(orderingPath(uid), { tasksListOrdering: ordering }, ["tasksListOrdering"]);
|
|
5946
|
+
},
|
|
5947
|
+
async post(path3, body) {
|
|
5948
|
+
try {
|
|
5949
|
+
const resp = await http.post(url(path3), body, { headers: await apiHeaders() });
|
|
5950
|
+
return resp.data;
|
|
5951
|
+
} catch (err) {
|
|
5952
|
+
throw toCliError(err);
|
|
6691
5953
|
}
|
|
6692
|
-
}
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
const doc = await getDoc(progressPath(uid, dateKey));
|
|
6700
|
-
if (Array.isArray(doc.tasks)) tasks = doc.tasks;
|
|
6701
|
-
} catch {
|
|
6702
|
-
}
|
|
6703
|
-
if (!tasks.includes(taskId)) tasks.push(taskId);
|
|
6704
|
-
await setDoc(progressPath(uid, dateKey), { tasks });
|
|
6705
|
-
}
|
|
6706
|
-
async function removeFromProgress(uid, taskId, date) {
|
|
6707
|
-
const dateKey = date.slice(0, 10);
|
|
6708
|
-
try {
|
|
6709
|
-
const doc = await getDoc(progressPath(uid, dateKey));
|
|
6710
|
-
if (Array.isArray(doc.tasks)) {
|
|
6711
|
-
const tasks = doc.tasks.filter((id) => id !== taskId);
|
|
6712
|
-
await setDoc(progressPath(uid, dateKey), { tasks });
|
|
5954
|
+
},
|
|
5955
|
+
async patch(path3, body) {
|
|
5956
|
+
try {
|
|
5957
|
+
const resp = await http.patch(url(path3), body, { headers: await apiHeaders() });
|
|
5958
|
+
return resp.data;
|
|
5959
|
+
} catch (err) {
|
|
5960
|
+
throw toCliError(err);
|
|
6713
5961
|
}
|
|
6714
|
-
}
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
} catch {
|
|
6722
|
-
}
|
|
6723
|
-
const newStreak = (streak.streak ?? 0) + 1;
|
|
6724
|
-
const longestStreak = Math.max(streak.longestStreak ?? 0, newStreak);
|
|
6725
|
-
await setDoc(routineStreakPath(uid, taskId), {
|
|
6726
|
-
taskId,
|
|
6727
|
-
userId: uid,
|
|
6728
|
-
streak: newStreak,
|
|
6729
|
-
longestStreak,
|
|
6730
|
-
lastCompletedAt: Date.now()
|
|
6731
|
-
});
|
|
6732
|
-
}
|
|
6733
|
-
async function revertRoutineStreak(uid, taskId) {
|
|
6734
|
-
try {
|
|
6735
|
-
const doc = await getDoc(routineStreakPath(uid, taskId));
|
|
6736
|
-
const newStreak = Math.max(0, (doc.streak ?? 0) - 1);
|
|
6737
|
-
await setDoc(routineStreakPath(uid, taskId), {
|
|
6738
|
-
...doc,
|
|
6739
|
-
streak: newStreak,
|
|
6740
|
-
lastCompletedAt: newStreak > 0 ? doc.lastCompletedAt : null
|
|
6741
|
-
});
|
|
6742
|
-
} catch {
|
|
6743
|
-
}
|
|
6744
|
-
}
|
|
6745
|
-
async function deleteRoutineStreak(uid, taskId) {
|
|
6746
|
-
try {
|
|
6747
|
-
await deleteDoc(routineStreakPath(uid, taskId));
|
|
6748
|
-
} catch {
|
|
6749
|
-
}
|
|
6750
|
-
}
|
|
6751
|
-
function computeCompleteKarma(task, checksInRow) {
|
|
6752
|
-
const difficultyIdx = task.difficulty ?? 0;
|
|
6753
|
-
const bonus = DIFFICULTY_BONUS[difficultyIdx] ?? 0;
|
|
6754
|
-
const raw = (KARMA_POINTS.completeTask + bonus) * checksInRow;
|
|
6755
|
-
return Math.min(raw, MAX_KARMA_PER_COMPLETE);
|
|
6756
|
-
}
|
|
6757
|
-
async function listTasks(uid, opts) {
|
|
6758
|
-
const [activeTasks, historyTasks] = await Promise.all([
|
|
6759
|
-
runQuery(`users/${uid}`, "tasks", { where: [], limit: MAX_TASKS_PER_REQUEST }),
|
|
6760
|
-
opts.backlog ? Promise.resolve([]) : runQuery(`users/${uid}`, "tasksHistory", { where: [], limit: MAX_TASKS_PER_REQUEST })
|
|
6761
|
-
]);
|
|
6762
|
-
let pending = activeTasks;
|
|
6763
|
-
let completed = historyTasks;
|
|
6764
|
-
if (opts.backlog) {
|
|
6765
|
-
pending = pending.filter((t2) => t2.backlog === true);
|
|
6766
|
-
} else if (opts.date) {
|
|
6767
|
-
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6768
|
-
const isToday = opts.date === today2;
|
|
6769
|
-
if (isToday) {
|
|
6770
|
-
const cutoff = endOfDay(opts.date);
|
|
6771
|
-
pending = pending.filter((t2) => typeof t2.dueDate === "string" && t2.dueDate <= cutoff);
|
|
6772
|
-
} else {
|
|
6773
|
-
pending = pending.filter((t2) => typeof t2.dueDate === "string" && t2.dueDate.slice(0, 10) === opts.date);
|
|
5962
|
+
},
|
|
5963
|
+
async del(path3) {
|
|
5964
|
+
try {
|
|
5965
|
+
const resp = await http.delete(url(path3), { headers: await apiHeaders() });
|
|
5966
|
+
return resp.data;
|
|
5967
|
+
} catch (err) {
|
|
5968
|
+
throw toCliError(err);
|
|
6774
5969
|
}
|
|
6775
|
-
completed = completed.filter((t2) => {
|
|
6776
|
-
if (typeof t2.dueDate !== "string") return false;
|
|
6777
|
-
return t2.dueDate.slice(0, 10) === opts.date;
|
|
6778
|
-
});
|
|
6779
5970
|
}
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
5971
|
+
};
|
|
5972
|
+
|
|
5973
|
+
// src/cli/services/tasks.ts
|
|
5974
|
+
async function listTasks(uid, opts) {
|
|
5975
|
+
return api.get("/api/tasks", {
|
|
5976
|
+
date: opts.date,
|
|
5977
|
+
backlog: opts.backlog ? "true" : void 0,
|
|
5978
|
+
tag: opts.tag
|
|
6783
5979
|
});
|
|
6784
|
-
const filteredPending = applyTag(pending);
|
|
6785
|
-
const filteredCompleted = applyTag(completed);
|
|
6786
|
-
const allTasks = [...filteredPending, ...filteredCompleted];
|
|
6787
|
-
return { tasks: allTasks, count: allTasks.length, pendingCount: filteredPending.length, completedCount: filteredCompleted.length };
|
|
6788
5980
|
}
|
|
6789
5981
|
async function getTask(uid, id) {
|
|
6790
|
-
|
|
6791
|
-
return getDoc(taskPath(uid, id));
|
|
5982
|
+
return api.get(`/api/tasks/${encodeURIComponent(id)}`);
|
|
6792
5983
|
}
|
|
6793
5984
|
async function createTask(uid, body) {
|
|
6794
|
-
|
|
6795
|
-
const dueDate = body.dueDate ?? null;
|
|
6796
|
-
const taskData = {
|
|
6797
|
-
text: body.text,
|
|
6798
|
-
userId: uid,
|
|
6799
|
-
isPublic: body.isPublic ?? true,
|
|
6800
|
-
completed: false,
|
|
6801
|
-
completedAt: null,
|
|
6802
|
-
createdAt: now2,
|
|
6803
|
-
dueDate,
|
|
6804
|
-
remindDate: null,
|
|
6805
|
-
tags: Array.isArray(body.tags) ? body.tags : [],
|
|
6806
|
-
assets: [],
|
|
6807
|
-
note: body.note ?? "",
|
|
6808
|
-
priority: typeof body.priority === "number" ? body.priority : 0,
|
|
6809
|
-
difficulty: body.difficulty ?? null,
|
|
6810
|
-
duration: typeof body.duration === "number" ? body.duration : 10,
|
|
6811
|
-
backlog: dueDate == null,
|
|
6812
|
-
parentTaskId: null,
|
|
6813
|
-
completions: 0,
|
|
6814
|
-
repeat: body.repeat ?? { type: "none", every: null, end: null, endDate: null, endAfter: null, monthDays: null, weekDays: null },
|
|
6815
|
-
subtasks: Array.isArray(body.subtasks) ? body.subtasks.map((s) => ({ id: crypto3.randomUUID(), text: s.text, completed: false })) : [],
|
|
6816
|
-
withTime: dueDate != null && !/\b00:00$/.test(dueDate),
|
|
6817
|
-
listPosition: null,
|
|
6818
|
-
source: "cli"
|
|
6819
|
-
};
|
|
6820
|
-
taskData.remindDate = getTaskRemindDate(taskData);
|
|
6821
|
-
const taskId = generateTaskId(taskData);
|
|
6822
|
-
const doc = await setDoc(taskPath(uid, taskId), taskData);
|
|
6823
|
-
const createResults = await Promise.allSettled([
|
|
6824
|
-
giveKarma(uid, "addTask", taskId, KARMA_POINTS.addTask, String(taskData.text)),
|
|
6825
|
-
addToOrdering(uid, taskId, "end"),
|
|
6826
|
-
incrementField(activityTotalsPath(uid), "tasks.active", 1)
|
|
6827
|
-
]);
|
|
6828
|
-
logSideEffectResults("createTask", createResults);
|
|
6829
|
-
return { task: doc, karma: KARMA_POINTS.addTask };
|
|
5985
|
+
return api.post("/api/tasks", body);
|
|
6830
5986
|
}
|
|
6831
5987
|
async function updateTask(uid, id, body) {
|
|
6832
|
-
|
|
6833
|
-
const allowed = ["text", "isPublic", "dueDate", "tags", "note", "priority", "difficulty", "duration", "repeat", "subtasks"];
|
|
6834
|
-
const update = {};
|
|
6835
|
-
const fieldMask = [];
|
|
6836
|
-
for (const key of allowed) {
|
|
6837
|
-
if (key in body) {
|
|
6838
|
-
update[key] = body[key];
|
|
6839
|
-
fieldMask.push(key);
|
|
6840
|
-
}
|
|
6841
|
-
}
|
|
6842
|
-
if ("dueDate" in update) {
|
|
6843
|
-
const newDueDate = update.dueDate;
|
|
6844
|
-
update.backlog = newDueDate == null;
|
|
6845
|
-
if (!fieldMask.includes("backlog")) fieldMask.push("backlog");
|
|
6846
|
-
}
|
|
6847
|
-
await updateDoc(taskPath(uid, id), update, fieldMask);
|
|
6848
|
-
if ("dueDate" in body) {
|
|
6849
|
-
await addToOrdering(uid, id, "end");
|
|
6850
|
-
}
|
|
6851
|
-
const updated = await getDoc(taskPath(uid, id));
|
|
6852
|
-
const recalculatedRemindDate = getTaskRemindDate(updated);
|
|
6853
|
-
if (updated.remindDate !== recalculatedRemindDate) {
|
|
6854
|
-
await updateDoc(taskPath(uid, id), { remindDate: recalculatedRemindDate }, ["remindDate"]);
|
|
6855
|
-
const final = await getDoc(taskPath(uid, id));
|
|
6856
|
-
return { task: final };
|
|
6857
|
-
}
|
|
6858
|
-
return { task: updated };
|
|
5988
|
+
return api.patch(`/api/tasks/${encodeURIComponent(id)}`, body);
|
|
6859
5989
|
}
|
|
6860
5990
|
async function deleteTask(uid, id) {
|
|
6861
|
-
|
|
6862
|
-
let taskData = {};
|
|
6863
|
-
try {
|
|
6864
|
-
taskData = await getDoc(taskPath(uid, id));
|
|
6865
|
-
} catch {
|
|
6866
|
-
}
|
|
6867
|
-
const hasData = Object.keys(taskData).length > 0;
|
|
6868
|
-
const writes = [
|
|
6869
|
-
{ type: "delete", path: taskPath(uid, id) }
|
|
6870
|
-
];
|
|
6871
|
-
if (hasData) {
|
|
6872
|
-
writes.push({
|
|
6873
|
-
type: "update",
|
|
6874
|
-
path: archivePath(uid, id),
|
|
6875
|
-
data: { ...taskData, archived: true, archivedAt: Date.now() }
|
|
6876
|
-
});
|
|
6877
|
-
writes.push({
|
|
6878
|
-
type: "transform",
|
|
6879
|
-
path: archiveCounterPath(uid),
|
|
6880
|
-
transforms: [{ field: "archivedTasksCount", increment: 1 }]
|
|
6881
|
-
});
|
|
6882
|
-
}
|
|
6883
|
-
await commit(writes);
|
|
6884
|
-
try {
|
|
6885
|
-
const historyDocs = await runQuery(`users/${uid}`, "tasksHistory", {
|
|
6886
|
-
where: [{ field: "parentTaskId", op: "EQUAL", value: id }],
|
|
6887
|
-
limit: MAX_TASKS_PER_REQUEST
|
|
6888
|
-
});
|
|
6889
|
-
const historyResults = await Promise.allSettled(historyDocs.map((h2) => deleteDoc(taskHistoryPath(uid, h2.id))));
|
|
6890
|
-
logSideEffectResults("deleteTask.history", historyResults);
|
|
6891
|
-
} catch (err) {
|
|
6892
|
-
process.stderr.write(`[warn] deleteTask.history: ${err instanceof Error ? err.message : String(err)}
|
|
6893
|
-
`);
|
|
6894
|
-
}
|
|
6895
|
-
const deleteResults = await Promise.allSettled([
|
|
6896
|
-
removeFromOrdering(uid, id),
|
|
6897
|
-
deleteRoutineStreak(uid, id),
|
|
6898
|
-
incrementField(activityTotalsPath(uid), "tasks.active", -1)
|
|
6899
|
-
]);
|
|
6900
|
-
logSideEffectResults("deleteTask", deleteResults);
|
|
6901
|
-
removeDailyStreak(id);
|
|
6902
|
-
return { deleted: true, taskText: String(taskData.text ?? ""), archived: hasData };
|
|
5991
|
+
return api.del(`/api/tasks/${encodeURIComponent(id)}`);
|
|
6903
5992
|
}
|
|
6904
5993
|
async function completeTask(uid, id, date) {
|
|
6905
|
-
|
|
6906
|
-
const task = await getDoc(taskPath(uid, id));
|
|
6907
|
-
const completedAt = date ? new Date(date).getTime() : Date.now();
|
|
6908
|
-
const repeating = isRepeating(task);
|
|
6909
|
-
const historyId = repeating ? `${id}-${completedAt}` : id;
|
|
6910
|
-
const historyData = {
|
|
6911
|
-
...task,
|
|
6912
|
-
id: historyId,
|
|
6913
|
-
completedAt,
|
|
6914
|
-
completed: true,
|
|
6915
|
-
parentTaskId: id,
|
|
6916
|
-
isHistoryTask: true,
|
|
6917
|
-
completions: (task.completions ?? 0) + 1,
|
|
6918
|
-
remindDate: null
|
|
6919
|
-
};
|
|
6920
|
-
delete historyData.id;
|
|
6921
|
-
const writes = [];
|
|
6922
|
-
if (repeating) {
|
|
6923
|
-
const nextDueDate = getNextDueDate(task);
|
|
6924
|
-
const resetSubtasks = (task.subtasks ?? []).map((s) => ({ ...s, completed: false }));
|
|
6925
|
-
const updatedData = {
|
|
6926
|
-
dueDate: nextDueDate,
|
|
6927
|
-
completions: (task.completions ?? 0) + 1,
|
|
6928
|
-
completedAt,
|
|
6929
|
-
subtasks: resetSubtasks
|
|
6930
|
-
};
|
|
6931
|
-
writes.push({
|
|
6932
|
-
type: "update",
|
|
6933
|
-
path: taskPath(uid, id),
|
|
6934
|
-
data: updatedData,
|
|
6935
|
-
fieldMask: Object.keys(updatedData)
|
|
6936
|
-
});
|
|
6937
|
-
} else {
|
|
6938
|
-
writes.push({ type: "delete", path: taskPath(uid, id) });
|
|
6939
|
-
}
|
|
6940
|
-
writes.push({
|
|
6941
|
-
type: "update",
|
|
6942
|
-
path: taskHistoryPath(uid, historyId),
|
|
6943
|
-
data: historyData
|
|
6944
|
-
});
|
|
6945
|
-
await commit(writes);
|
|
6946
|
-
if (repeating) {
|
|
6947
|
-
try {
|
|
6948
|
-
const afterUpdate = await getDoc(taskPath(uid, id));
|
|
6949
|
-
await updateDoc(taskPath(uid, id), { remindDate: getTaskRemindDate(afterUpdate) }, ["remindDate"]);
|
|
6950
|
-
} catch {
|
|
6951
|
-
}
|
|
6952
|
-
}
|
|
6953
|
-
const completeDateStr = date ?? formatDate(/* @__PURE__ */ new Date());
|
|
6954
|
-
const entityId = repeating ? `${id}_${completeDateStr.slice(0, 10)}` : id;
|
|
6955
|
-
const checksInRow = recordDailyStreak(id, completeDateStr);
|
|
6956
|
-
const karmaPoints = computeCompleteKarma(task, checksInRow);
|
|
6957
|
-
const sideEffects = [
|
|
6958
|
-
giveKarma(uid, "completeTask", entityId, karmaPoints, task.text),
|
|
6959
|
-
addToProgress(uid, id, completeDateStr),
|
|
6960
|
-
incrementField(activityTotalsPath(uid), "tasks.completed", 1)
|
|
6961
|
-
];
|
|
6962
|
-
if (repeating) {
|
|
6963
|
-
sideEffects.push(recordRoutineStreak(uid, id));
|
|
6964
|
-
} else {
|
|
6965
|
-
sideEffects.push(removeFromOrdering(uid, id));
|
|
6966
|
-
sideEffects.push(incrementField(activityTotalsPath(uid), "tasks.active", -1));
|
|
6967
|
-
}
|
|
6968
|
-
const completeResults = await Promise.allSettled(sideEffects);
|
|
6969
|
-
logSideEffectResults("completeTask", completeResults);
|
|
6970
|
-
return { completed: true, taskHistory: historyData, karma: karmaPoints, checksInRow, taskText: task.text };
|
|
5994
|
+
return api.post(`/api/tasks/${encodeURIComponent(id)}/complete`, date ? { date } : void 0);
|
|
6971
5995
|
}
|
|
6972
5996
|
async function uncompleteTask(uid, taskHistoryId) {
|
|
6973
|
-
|
|
6974
|
-
const history = await getDoc(taskHistoryPath(uid, taskHistoryId));
|
|
6975
|
-
const parentTaskId = history.parentTaskId;
|
|
6976
|
-
if (!parentTaskId) throw new Error("Not a history record");
|
|
6977
|
-
const repeating = isRepeating(history);
|
|
6978
|
-
const restoredTask = {
|
|
6979
|
-
...history,
|
|
6980
|
-
completed: false,
|
|
6981
|
-
completedAt: null,
|
|
6982
|
-
completions: Math.max(0, (history.completions ?? 0) - 1)
|
|
6983
|
-
};
|
|
6984
|
-
delete restoredTask.isHistoryTask;
|
|
6985
|
-
delete restoredTask.id;
|
|
6986
|
-
restoredTask.parentTaskId = null;
|
|
6987
|
-
if (repeating) {
|
|
6988
|
-
restoredTask.dueDate = history.dueDate;
|
|
6989
|
-
}
|
|
6990
|
-
const writes = [
|
|
6991
|
-
{ type: "update", path: taskPath(uid, parentTaskId), data: restoredTask },
|
|
6992
|
-
{ type: "delete", path: taskHistoryPath(uid, taskHistoryId) }
|
|
6993
|
-
];
|
|
6994
|
-
await commit(writes);
|
|
6995
|
-
try {
|
|
6996
|
-
const updated = await getDoc(taskPath(uid, parentTaskId));
|
|
6997
|
-
await updateDoc(taskPath(uid, parentTaskId), { remindDate: getTaskRemindDate(updated) }, ["remindDate"]);
|
|
6998
|
-
} catch {
|
|
6999
|
-
}
|
|
7000
|
-
const completedAtDate = history.completedAt ? formatDate(new Date(history.completedAt)) : history.dueDate ?? formatDate(/* @__PURE__ */ new Date());
|
|
7001
|
-
const entityId = repeating ? `${parentTaskId}_${completedAtDate.slice(0, 10)}` : parentTaskId;
|
|
7002
|
-
const sideEffects = [
|
|
7003
|
-
removeKarma(uid, "completeTask", entityId),
|
|
7004
|
-
removeFromProgress(uid, parentTaskId, completedAtDate),
|
|
7005
|
-
incrementField(activityTotalsPath(uid), "tasks.completed", -1)
|
|
7006
|
-
];
|
|
7007
|
-
revertDailyStreak(parentTaskId);
|
|
7008
|
-
if (repeating) {
|
|
7009
|
-
sideEffects.push(revertRoutineStreak(uid, parentTaskId));
|
|
7010
|
-
} else {
|
|
7011
|
-
sideEffects.push(addToOrdering(uid, parentTaskId, "end"));
|
|
7012
|
-
sideEffects.push(incrementField(activityTotalsPath(uid), "tasks.active", 1));
|
|
7013
|
-
}
|
|
7014
|
-
const uncompleteResults = await Promise.allSettled(sideEffects);
|
|
7015
|
-
logSideEffectResults("uncompleteTask", uncompleteResults);
|
|
7016
|
-
const final = await getDoc(taskPath(uid, parentTaskId));
|
|
7017
|
-
return { uncompleted: true, task: final, karmaReverted: true };
|
|
5997
|
+
return api.post(`/api/tasks/${encodeURIComponent(taskHistoryId)}/uncomplete`);
|
|
7018
5998
|
}
|
|
7019
5999
|
|
|
7020
6000
|
// src/cli/lib/format.ts
|
|
7021
|
-
init_define_ADMIN_UIDS();
|
|
7022
6001
|
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
7023
|
-
function
|
|
6002
|
+
function formatDate(ts) {
|
|
7024
6003
|
if (ts == null) return "";
|
|
7025
6004
|
const d = typeof ts === "number" ? new Date(ts) : new Date(ts);
|
|
7026
6005
|
if (isNaN(d.getTime())) return String(ts);
|
|
@@ -7043,7 +6022,7 @@ function formatRelativeDate(ts) {
|
|
|
7043
6022
|
if (hours < 24) return `${hours}h ago`;
|
|
7044
6023
|
const days = Math.floor(hours / 24);
|
|
7045
6024
|
if (days < 30) return `${days}d ago`;
|
|
7046
|
-
return
|
|
6025
|
+
return formatDate(ts);
|
|
7047
6026
|
}
|
|
7048
6027
|
function formatTags(tags) {
|
|
7049
6028
|
if (!Array.isArray(tags) || tags.length === 0) return "";
|
|
@@ -7121,26 +6100,7 @@ init_prompts();
|
|
|
7121
6100
|
init_tty();
|
|
7122
6101
|
init_errors();
|
|
7123
6102
|
|
|
7124
|
-
// src/cli/lib/parse-date.ts
|
|
7125
|
-
init_define_ADMIN_UIDS();
|
|
7126
|
-
|
|
7127
|
-
// node_modules/chrono-node/dist/esm/index.js
|
|
7128
|
-
init_define_ADMIN_UIDS();
|
|
7129
|
-
|
|
7130
|
-
// node_modules/chrono-node/dist/esm/locales/en/index.js
|
|
7131
|
-
init_define_ADMIN_UIDS();
|
|
7132
|
-
|
|
7133
|
-
// node_modules/chrono-node/dist/esm/chrono.js
|
|
7134
|
-
init_define_ADMIN_UIDS();
|
|
7135
|
-
|
|
7136
|
-
// node_modules/chrono-node/dist/esm/results.js
|
|
7137
|
-
init_define_ADMIN_UIDS();
|
|
7138
|
-
|
|
7139
|
-
// node_modules/chrono-node/dist/esm/utils/dates.js
|
|
7140
|
-
init_define_ADMIN_UIDS();
|
|
7141
|
-
|
|
7142
6103
|
// node_modules/chrono-node/dist/esm/types.js
|
|
7143
|
-
init_define_ADMIN_UIDS();
|
|
7144
6104
|
var Meridiem;
|
|
7145
6105
|
(function(Meridiem2) {
|
|
7146
6106
|
Meridiem2[Meridiem2["AM"] = 0] = "AM";
|
|
@@ -7199,7 +6159,6 @@ function implySimilarTime(component, target) {
|
|
|
7199
6159
|
}
|
|
7200
6160
|
|
|
7201
6161
|
// node_modules/chrono-node/dist/esm/timezone.js
|
|
7202
|
-
init_define_ADMIN_UIDS();
|
|
7203
6162
|
var TIMEZONE_ABBR_MAP = {
|
|
7204
6163
|
ACDT: 630,
|
|
7205
6164
|
ACST: 570,
|
|
@@ -7469,7 +6428,6 @@ function toTimezoneOffset(timezoneInput, date, timezoneOverrides = {}) {
|
|
|
7469
6428
|
}
|
|
7470
6429
|
|
|
7471
6430
|
// node_modules/chrono-node/dist/esm/calculation/duration.js
|
|
7472
|
-
init_define_ADMIN_UIDS();
|
|
7473
6431
|
var EmptyDuration = {
|
|
7474
6432
|
day: 0,
|
|
7475
6433
|
second: 0,
|
|
@@ -7870,17 +6828,7 @@ var ParsingResult = class _ParsingResult {
|
|
|
7870
6828
|
}
|
|
7871
6829
|
};
|
|
7872
6830
|
|
|
7873
|
-
// node_modules/chrono-node/dist/esm/locales/en/configuration.js
|
|
7874
|
-
init_define_ADMIN_UIDS();
|
|
7875
|
-
|
|
7876
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js
|
|
7877
|
-
init_define_ADMIN_UIDS();
|
|
7878
|
-
|
|
7879
|
-
// node_modules/chrono-node/dist/esm/locales/en/constants.js
|
|
7880
|
-
init_define_ADMIN_UIDS();
|
|
7881
|
-
|
|
7882
6831
|
// node_modules/chrono-node/dist/esm/utils/pattern.js
|
|
7883
|
-
init_define_ADMIN_UIDS();
|
|
7884
6832
|
function repeatedTimeunitPattern(prefix, singleTimeunitPattern, connectorPattern = "\\s{0,5},?\\s{0,5}") {
|
|
7885
6833
|
const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
|
|
7886
6834
|
return `${prefix}${singleTimeunitPatternNoCapture}(?:${connectorPattern}${singleTimeunitPatternNoCapture}){0,10}`;
|
|
@@ -7902,7 +6850,6 @@ function matchAnyPattern(dictionary) {
|
|
|
7902
6850
|
}
|
|
7903
6851
|
|
|
7904
6852
|
// node_modules/chrono-node/dist/esm/calculation/years.js
|
|
7905
|
-
init_define_ADMIN_UIDS();
|
|
7906
6853
|
function findMostLikelyADYear(yearNumber) {
|
|
7907
6854
|
if (yearNumber < 100) {
|
|
7908
6855
|
if (yearNumber > 50) {
|
|
@@ -8180,7 +7127,6 @@ function collectDateTimeFragment(fragments, match) {
|
|
|
8180
7127
|
}
|
|
8181
7128
|
|
|
8182
7129
|
// node_modules/chrono-node/dist/esm/common/parsers/AbstractParserWithWordBoundary.js
|
|
8183
|
-
init_define_ADMIN_UIDS();
|
|
8184
7130
|
var AbstractParserWithWordBoundaryChecking = class {
|
|
8185
7131
|
innerPatternHasChange(context, currentInnerPattern) {
|
|
8186
7132
|
return this.innerPattern(context) !== currentInnerPattern;
|
|
@@ -8240,7 +7186,6 @@ var ENTimeUnitWithinFormatParser = class extends AbstractParserWithWordBoundaryC
|
|
|
8240
7186
|
};
|
|
8241
7187
|
|
|
8242
7188
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameLittleEndianParser.js
|
|
8243
|
-
init_define_ADMIN_UIDS();
|
|
8244
7189
|
var PATTERN = new RegExp(`(?:on\\s{0,3})?(${ORDINAL_NUMBER_PATTERN})(?:\\s{0,3}(?:to|\\-|\\\u2013|until|through|till)?\\s{0,3}(${ORDINAL_NUMBER_PATTERN}))?(?:-|/|\\s{0,3}(?:of)?\\s{0,3})(${matchAnyPattern(MONTH_DICTIONARY)})(?:(?:-|/|,?\\s{0,3})(${YEAR_PATTERN}(?!\\w)))?(?=\\W|$)`, "i");
|
|
8245
7190
|
var DATE_GROUP = 1;
|
|
8246
7191
|
var DATE_TO_GROUP = 2;
|
|
@@ -8277,7 +7222,6 @@ var ENMonthNameLittleEndianParser = class extends AbstractParserWithWordBoundary
|
|
|
8277
7222
|
};
|
|
8278
7223
|
|
|
8279
7224
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameMiddleEndianParser.js
|
|
8280
|
-
init_define_ADMIN_UIDS();
|
|
8281
7225
|
var PATTERN2 = new RegExp(`(${matchAnyPattern(MONTH_DICTIONARY)})(?:-|/|\\s*,?\\s*)(${ORDINAL_NUMBER_PATTERN})(?!\\s*(?:am|pm))\\s*(?:(?:to|\\-)\\s*(${ORDINAL_NUMBER_PATTERN})\\s*)?(?:(?:-|/|\\s*,\\s*|\\s+)(${YEAR_PATTERN}))?(?=\\W|$)(?!\\:\\d)`, "i");
|
|
8282
7226
|
var MONTH_NAME_GROUP2 = 1;
|
|
8283
7227
|
var DATE_GROUP2 = 2;
|
|
@@ -8327,7 +7271,6 @@ var ENMonthNameMiddleEndianParser = class extends AbstractParserWithWordBoundary
|
|
|
8327
7271
|
};
|
|
8328
7272
|
|
|
8329
7273
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameParser.js
|
|
8330
|
-
init_define_ADMIN_UIDS();
|
|
8331
7274
|
var PATTERN3 = new RegExp(`((?:in)\\s*)?(${matchAnyPattern(MONTH_DICTIONARY)})\\s*(?:(?:,|-|of)?\\s*(${YEAR_PATTERN})?)?(?=[^\\s\\w]|\\s+[^0-9]|\\s+$|$)`, "i");
|
|
8332
7275
|
var PREFIX_GROUP = 1;
|
|
8333
7276
|
var MONTH_NAME_GROUP3 = 2;
|
|
@@ -8358,7 +7301,6 @@ var ENMonthNameParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
8358
7301
|
};
|
|
8359
7302
|
|
|
8360
7303
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENYearMonthDayParser.js
|
|
8361
|
-
init_define_ADMIN_UIDS();
|
|
8362
7304
|
var PATTERN4 = new RegExp(`([0-9]{4})[-\\.\\/\\s](?:(${matchAnyPattern(MONTH_DICTIONARY)})|([0-9]{1,2}))[-\\.\\/\\s]([0-9]{1,2})(?=\\W|$)`, "i");
|
|
8363
7305
|
var YEAR_NUMBER_GROUP = 1;
|
|
8364
7306
|
var MONTH_NAME_GROUP4 = 2;
|
|
@@ -8397,7 +7339,6 @@ var ENYearMonthDayParser = class extends AbstractParserWithWordBoundaryChecking
|
|
|
8397
7339
|
};
|
|
8398
7340
|
|
|
8399
7341
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENSlashMonthFormatParser.js
|
|
8400
|
-
init_define_ADMIN_UIDS();
|
|
8401
7342
|
var PATTERN5 = new RegExp("([0-9]|0[1-9]|1[012])/([0-9]{4})", "i");
|
|
8402
7343
|
var MONTH_GROUP = 1;
|
|
8403
7344
|
var YEAR_GROUP4 = 2;
|
|
@@ -8412,11 +7353,7 @@ var ENSlashMonthFormatParser = class extends AbstractParserWithWordBoundaryCheck
|
|
|
8412
7353
|
}
|
|
8413
7354
|
};
|
|
8414
7355
|
|
|
8415
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeExpressionParser.js
|
|
8416
|
-
init_define_ADMIN_UIDS();
|
|
8417
|
-
|
|
8418
7356
|
// node_modules/chrono-node/dist/esm/common/parsers/AbstractTimeExpressionParser.js
|
|
8419
|
-
init_define_ADMIN_UIDS();
|
|
8420
7357
|
function primaryTimePattern(leftBoundary, primaryPrefix, primarySuffix, flags) {
|
|
8421
7358
|
return new RegExp(`${leftBoundary}${primaryPrefix}(\\d{1,4})(?:(?:\\.|:|\uFF1A)(\\d{1,2})(?:(?::|\uFF1A)(\\d{2})(?:\\.(\\d{1,6}))?)?)?(?:\\s*(a\\.m\\.|p\\.m\\.|am?|pm?))?${primarySuffix}`, flags);
|
|
8422
7359
|
}
|
|
@@ -8772,7 +7709,6 @@ var ENTimeExpressionParser = class extends AbstractTimeExpressionParser {
|
|
|
8772
7709
|
};
|
|
8773
7710
|
|
|
8774
7711
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js
|
|
8775
|
-
init_define_ADMIN_UIDS();
|
|
8776
7712
|
var PATTERN6 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
|
|
8777
7713
|
var STRICT_PATTERN = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
|
|
8778
7714
|
var ENTimeUnitAgoFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
@@ -8794,7 +7730,6 @@ var ENTimeUnitAgoFormatParser = class extends AbstractParserWithWordBoundaryChec
|
|
|
8794
7730
|
};
|
|
8795
7731
|
|
|
8796
7732
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js
|
|
8797
|
-
init_define_ADMIN_UIDS();
|
|
8798
7733
|
var PATTERN7 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:later|after|from now|henceforth|forward|out)(?=(?:\\W|$))`, "i");
|
|
8799
7734
|
var STRICT_PATTERN2 = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(later|after|from now)(?=\\W|$)`, "i");
|
|
8800
7735
|
var GROUP_NUM_TIMEUNITS = 1;
|
|
@@ -8816,14 +7751,7 @@ var ENTimeUnitLaterFormatParser = class extends AbstractParserWithWordBoundaryCh
|
|
|
8816
7751
|
}
|
|
8817
7752
|
};
|
|
8818
7753
|
|
|
8819
|
-
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateRangeRefiner.js
|
|
8820
|
-
init_define_ADMIN_UIDS();
|
|
8821
|
-
|
|
8822
|
-
// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js
|
|
8823
|
-
init_define_ADMIN_UIDS();
|
|
8824
|
-
|
|
8825
7754
|
// node_modules/chrono-node/dist/esm/common/abstractRefiners.js
|
|
8826
|
-
init_define_ADMIN_UIDS();
|
|
8827
7755
|
var Filter = class {
|
|
8828
7756
|
refine(context, results) {
|
|
8829
7757
|
return results.filter((r) => this.isValid(context, r));
|
|
@@ -8921,14 +7849,7 @@ var ENMergeDateRangeRefiner = class extends AbstractMergeDateRangeRefiner {
|
|
|
8921
7849
|
}
|
|
8922
7850
|
};
|
|
8923
7851
|
|
|
8924
|
-
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateTimeRefiner.js
|
|
8925
|
-
init_define_ADMIN_UIDS();
|
|
8926
|
-
|
|
8927
|
-
// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateTimeRefiner.js
|
|
8928
|
-
init_define_ADMIN_UIDS();
|
|
8929
|
-
|
|
8930
7852
|
// node_modules/chrono-node/dist/esm/calculation/mergingCalculation.js
|
|
8931
|
-
init_define_ADMIN_UIDS();
|
|
8932
7853
|
function mergeDateTimeResult(dateResult, timeResult) {
|
|
8933
7854
|
const result = dateResult.clone();
|
|
8934
7855
|
const beginDate = dateResult.start;
|
|
@@ -9013,11 +7934,7 @@ var ENMergeDateTimeRefiner = class extends AbstractMergeDateTimeRefiner {
|
|
|
9013
7934
|
}
|
|
9014
7935
|
};
|
|
9015
7936
|
|
|
9016
|
-
// node_modules/chrono-node/dist/esm/configurations.js
|
|
9017
|
-
init_define_ADMIN_UIDS();
|
|
9018
|
-
|
|
9019
7937
|
// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js
|
|
9020
|
-
init_define_ADMIN_UIDS();
|
|
9021
7938
|
var TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
|
|
9022
7939
|
var ExtractTimezoneAbbrRefiner = class {
|
|
9023
7940
|
timezoneOverrides;
|
|
@@ -9069,7 +7986,6 @@ var ExtractTimezoneAbbrRefiner = class {
|
|
|
9069
7986
|
};
|
|
9070
7987
|
|
|
9071
7988
|
// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneOffsetRefiner.js
|
|
9072
|
-
init_define_ADMIN_UIDS();
|
|
9073
7989
|
var TIMEZONE_OFFSET_PATTERN = new RegExp("^\\s*(?:\\(?(?:GMT|UTC)\\s?)?([+-])(\\d{1,2})(?::?(\\d{2}))?\\)?", "i");
|
|
9074
7990
|
var TIMEZONE_OFFSET_SIGN_GROUP = 1;
|
|
9075
7991
|
var TIMEZONE_OFFSET_HOUR_OFFSET_GROUP = 2;
|
|
@@ -9108,7 +8024,6 @@ var ExtractTimezoneOffsetRefiner = class {
|
|
|
9108
8024
|
};
|
|
9109
8025
|
|
|
9110
8026
|
// node_modules/chrono-node/dist/esm/common/refiners/OverlapRemovalRefiner.js
|
|
9111
|
-
init_define_ADMIN_UIDS();
|
|
9112
8027
|
var OverlapRemovalRefiner = class {
|
|
9113
8028
|
refine(context, results) {
|
|
9114
8029
|
if (results.length < 2) {
|
|
@@ -9145,7 +8060,6 @@ var OverlapRemovalRefiner = class {
|
|
|
9145
8060
|
};
|
|
9146
8061
|
|
|
9147
8062
|
// node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
|
|
9148
|
-
init_define_ADMIN_UIDS();
|
|
9149
8063
|
var ForwardDateRefiner = class {
|
|
9150
8064
|
refine(context, results) {
|
|
9151
8065
|
if (!context.option.forwardDate) {
|
|
@@ -9211,7 +8125,6 @@ var ForwardDateRefiner = class {
|
|
|
9211
8125
|
};
|
|
9212
8126
|
|
|
9213
8127
|
// node_modules/chrono-node/dist/esm/common/refiners/UnlikelyFormatFilter.js
|
|
9214
|
-
init_define_ADMIN_UIDS();
|
|
9215
8128
|
var UnlikelyFormatFilter = class extends Filter {
|
|
9216
8129
|
strictMode;
|
|
9217
8130
|
constructor(strictMode) {
|
|
@@ -9254,7 +8167,6 @@ var UnlikelyFormatFilter = class extends Filter {
|
|
|
9254
8167
|
};
|
|
9255
8168
|
|
|
9256
8169
|
// node_modules/chrono-node/dist/esm/common/parsers/ISOFormatParser.js
|
|
9257
|
-
init_define_ADMIN_UIDS();
|
|
9258
8170
|
var PATTERN8 = new RegExp("([0-9]{4})\\-([0-9]{1,2})\\-([0-9]{1,2})(?:T([0-9]{1,2}):([0-9]{1,2})(?::([0-9]{1,2})(?:\\.(\\d{1,4}))?)?(Z|([+-]\\d{2}):?(\\d{2})?)?)?(?=\\W|$)", "i");
|
|
9259
8171
|
var YEAR_NUMBER_GROUP2 = 1;
|
|
9260
8172
|
var MONTH_NUMBER_GROUP2 = 2;
|
|
@@ -9308,7 +8220,6 @@ var ISOFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9308
8220
|
};
|
|
9309
8221
|
|
|
9310
8222
|
// node_modules/chrono-node/dist/esm/common/refiners/MergeWeekdayComponentRefiner.js
|
|
9311
|
-
init_define_ADMIN_UIDS();
|
|
9312
8223
|
var MergeWeekdayComponentRefiner = class extends MergingRefiner {
|
|
9313
8224
|
mergeResults(textBetween, currentResult, nextResult) {
|
|
9314
8225
|
const newResult = nextResult.clone();
|
|
@@ -9339,11 +8250,7 @@ function includeCommonConfiguration(configuration2, strictMode = false) {
|
|
|
9339
8250
|
return configuration2;
|
|
9340
8251
|
}
|
|
9341
8252
|
|
|
9342
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualDateParser.js
|
|
9343
|
-
init_define_ADMIN_UIDS();
|
|
9344
|
-
|
|
9345
8253
|
// node_modules/chrono-node/dist/esm/common/casualReferences.js
|
|
9346
|
-
init_define_ADMIN_UIDS();
|
|
9347
8254
|
function now(reference) {
|
|
9348
8255
|
const targetDate = reference.getDateWithAdjustedTimezone();
|
|
9349
8256
|
const component = new ParsingComponents(reference, {});
|
|
@@ -9489,7 +8396,6 @@ var ENCasualDateParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9489
8396
|
};
|
|
9490
8397
|
|
|
9491
8398
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualTimeParser.js
|
|
9492
|
-
init_define_ADMIN_UIDS();
|
|
9493
8399
|
var PATTERN10 = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|midday|noon)(?=\W|$)/i;
|
|
9494
8400
|
var ENCasualTimeParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
9495
8401
|
innerPattern() {
|
|
@@ -9523,11 +8429,7 @@ var ENCasualTimeParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9523
8429
|
}
|
|
9524
8430
|
};
|
|
9525
8431
|
|
|
9526
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
|
|
9527
|
-
init_define_ADMIN_UIDS();
|
|
9528
|
-
|
|
9529
8432
|
// node_modules/chrono-node/dist/esm/calculation/weekdays.js
|
|
9530
|
-
init_define_ADMIN_UIDS();
|
|
9531
8433
|
function createParsingComponentsAtWeekday(reference, weekday, modifier) {
|
|
9532
8434
|
const refDate = reference.getDateWithAdjustedTimezone();
|
|
9533
8435
|
const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
|
|
@@ -9630,7 +8532,6 @@ var ENWeekdayParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9630
8532
|
};
|
|
9631
8533
|
|
|
9632
8534
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENRelativeDateFormatParser.js
|
|
9633
|
-
init_define_ADMIN_UIDS();
|
|
9634
8535
|
var PATTERN12 = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)(?=\\W|$)`, "i");
|
|
9635
8536
|
var MODIFIER_WORD_GROUP = 1;
|
|
9636
8537
|
var RELATIVE_WORD_GROUP = 2;
|
|
@@ -9676,7 +8577,6 @@ var ENRelativeDateFormatParser = class extends AbstractParserWithWordBoundaryChe
|
|
|
9676
8577
|
};
|
|
9677
8578
|
|
|
9678
8579
|
// node_modules/chrono-node/dist/esm/common/parsers/SlashDateFormatParser.js
|
|
9679
|
-
init_define_ADMIN_UIDS();
|
|
9680
8580
|
var PATTERN13 = new RegExp("([^\\d]|^)([0-3]{0,1}[0-9]{1})[\\/\\.\\-]([0-3]{0,1}[0-9]{1})(?:[\\/\\.\\-]([0-9]{4}|[0-9]{2}))?(\\W|$)", "i");
|
|
9681
8581
|
var OPENING_GROUP = 1;
|
|
9682
8582
|
var ENDING_GROUP = 5;
|
|
@@ -9745,7 +8645,6 @@ var SlashDateFormatParser = class {
|
|
|
9745
8645
|
};
|
|
9746
8646
|
|
|
9747
8647
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js
|
|
9748
|
-
init_define_ADMIN_UIDS();
|
|
9749
8648
|
var PATTERN14 = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
9750
8649
|
var PATTERN_NO_ABBR = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
|
|
9751
8650
|
var ENTimeUnitCasualRelativeFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
@@ -9775,7 +8674,6 @@ var ENTimeUnitCasualRelativeFormatParser = class extends AbstractParserWithWordB
|
|
|
9775
8674
|
};
|
|
9776
8675
|
|
|
9777
8676
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeAfterDateRefiner.js
|
|
9778
|
-
init_define_ADMIN_UIDS();
|
|
9779
8677
|
function IsPositiveFollowingReference(result) {
|
|
9780
8678
|
return result.text.match(/^[+-]/i) != null;
|
|
9781
8679
|
}
|
|
@@ -9800,7 +8698,6 @@ var ENMergeRelativeAfterDateRefiner = class extends MergingRefiner {
|
|
|
9800
8698
|
};
|
|
9801
8699
|
|
|
9802
8700
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeFollowByDateRefiner.js
|
|
9803
|
-
init_define_ADMIN_UIDS();
|
|
9804
8701
|
function hasImpliedEarlierReferenceDate(result) {
|
|
9805
8702
|
return result.text.match(/\s+(before|from)$/i) != null;
|
|
9806
8703
|
}
|
|
@@ -9831,7 +8728,6 @@ var ENMergeRelativeFollowByDateRefiner = class extends MergingRefiner {
|
|
|
9831
8728
|
};
|
|
9832
8729
|
|
|
9833
8730
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENExtractYearSuffixRefiner.js
|
|
9834
|
-
init_define_ADMIN_UIDS();
|
|
9835
8731
|
var YEAR_SUFFIX_PATTERN = new RegExp(`^\\s*(${YEAR_PATTERN})`, "i");
|
|
9836
8732
|
var YEAR_GROUP6 = 1;
|
|
9837
8733
|
var ENExtractYearSuffixRefiner = class {
|
|
@@ -9863,7 +8759,6 @@ var ENExtractYearSuffixRefiner = class {
|
|
|
9863
8759
|
};
|
|
9864
8760
|
|
|
9865
8761
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENUnlikelyFormatFilter.js
|
|
9866
|
-
init_define_ADMIN_UIDS();
|
|
9867
8762
|
var ENUnlikelyFormatFilter = class extends Filter {
|
|
9868
8763
|
constructor() {
|
|
9869
8764
|
super();
|
|
@@ -10045,7 +8940,7 @@ var GB = new Chrono(configuration.createCasualConfiguration(true));
|
|
|
10045
8940
|
|
|
10046
8941
|
// node_modules/chrono-node/dist/esm/index.js
|
|
10047
8942
|
var casual2 = casual;
|
|
10048
|
-
function
|
|
8943
|
+
function parseDate(text, ref, option) {
|
|
10049
8944
|
return casual2.parseDate(text, ref, option);
|
|
10050
8945
|
}
|
|
10051
8946
|
|
|
@@ -10054,7 +8949,7 @@ function parseHumanDate(input) {
|
|
|
10054
8949
|
const trimmed = input.trim();
|
|
10055
8950
|
if (!trimmed) return null;
|
|
10056
8951
|
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) return trimmed;
|
|
10057
|
-
const parsed =
|
|
8952
|
+
const parsed = parseDate(trimmed, /* @__PURE__ */ new Date(), { forwardDate: true });
|
|
10058
8953
|
if (!parsed) return null;
|
|
10059
8954
|
const date = parsed.toISOString().slice(0, 10);
|
|
10060
8955
|
const hours = parsed.getHours();
|
|
@@ -10072,10 +8967,9 @@ function parseHumanDateOnly(input) {
|
|
|
10072
8967
|
}
|
|
10073
8968
|
|
|
10074
8969
|
// src/cli/lib/stdin.ts
|
|
10075
|
-
|
|
10076
|
-
var fs4 = __toESM(require("fs"), 1);
|
|
8970
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
10077
8971
|
function readStdinLines() {
|
|
10078
|
-
const input =
|
|
8972
|
+
const input = fs3.readFileSync(0, "utf8");
|
|
10079
8973
|
return input.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
10080
8974
|
}
|
|
10081
8975
|
|
|
@@ -10095,7 +8989,7 @@ async function pickTask(uid, id, actionName) {
|
|
|
10095
8989
|
message: `Select task to ${actionName}`,
|
|
10096
8990
|
options: pending.map((t2) => ({
|
|
10097
8991
|
value: t2.id,
|
|
10098
|
-
label: `${truncate(
|
|
8992
|
+
label: `${truncate(t2.text, 50)} ${import_picocolors8.default.dim(t2.id)}`
|
|
10099
8993
|
}))
|
|
10100
8994
|
});
|
|
10101
8995
|
return selected;
|
|
@@ -10120,13 +9014,12 @@ function extractTime(dueDate) {
|
|
|
10120
9014
|
const time = parts[1];
|
|
10121
9015
|
return time === "00:00" ? "" : time;
|
|
10122
9016
|
}
|
|
10123
|
-
function
|
|
10124
|
-
|
|
10125
|
-
return !!repeat?.type && repeat.type !== "none";
|
|
9017
|
+
function isRepeating(t2) {
|
|
9018
|
+
return !!t2.repeat?.type && t2.repeat.type !== "none";
|
|
10126
9019
|
}
|
|
10127
9020
|
function getCheckIndicator(t2) {
|
|
10128
9021
|
if (t2.completed) return import_picocolors8.default.green(SYM.check);
|
|
10129
|
-
if (
|
|
9022
|
+
if (isRepeating(t2)) return import_picocolors8.default.blue(SYM.repeat);
|
|
10130
9023
|
return import_picocolors8.default.dim(SYM.circle);
|
|
10131
9024
|
}
|
|
10132
9025
|
function sortTasksForDisplay(tasks) {
|
|
@@ -10134,16 +9027,16 @@ function sortTasksForDisplay(tasks) {
|
|
|
10134
9027
|
return [...tasks].sort((a, b) => {
|
|
10135
9028
|
const timeA = extractTime(a.dueDate);
|
|
10136
9029
|
const timeB = extractTime(b.dueDate);
|
|
10137
|
-
const repA =
|
|
10138
|
-
const repB =
|
|
9030
|
+
const repA = isRepeating(a);
|
|
9031
|
+
const repB = isRepeating(b);
|
|
10139
9032
|
if (timeA && !timeB) return -1;
|
|
10140
9033
|
if (!timeA && timeB) return 1;
|
|
10141
9034
|
if (timeA && timeB) return timeA.localeCompare(timeB);
|
|
10142
9035
|
if (repA && !repB) return -1;
|
|
10143
9036
|
if (!repA && repB) return 1;
|
|
10144
9037
|
if (repA && repB) {
|
|
10145
|
-
const ra = a.repeat
|
|
10146
|
-
const rb = b.repeat
|
|
9038
|
+
const ra = a.repeat.type ?? "";
|
|
9039
|
+
const rb = b.repeat.type ?? "";
|
|
10147
9040
|
return (repeatOrder[ra] ?? 99) - (repeatOrder[rb] ?? 99);
|
|
10148
9041
|
}
|
|
10149
9042
|
return 0;
|
|
@@ -10155,7 +9048,7 @@ function printTaskDetail(t2) {
|
|
|
10155
9048
|
printRecord([
|
|
10156
9049
|
["ID", dim(t2.id)],
|
|
10157
9050
|
["Text", t2.text],
|
|
10158
|
-
["Due",
|
|
9051
|
+
["Due", formatDate(t2.dueDate) || dim("none (backlog)")],
|
|
10159
9052
|
["Status", t2.completed ? import_picocolors8.default.green("completed") : import_picocolors8.default.yellow("pending")],
|
|
10160
9053
|
["Tags", formatTags(t2.tags) || dim("none")],
|
|
10161
9054
|
["Difficulty", formatDifficulty(t2.difficulty) || dim("not set")],
|
|
@@ -10164,18 +9057,18 @@ function printTaskDetail(t2) {
|
|
|
10164
9057
|
["Note", t2.note || dim("none")],
|
|
10165
9058
|
["Public", t2.isPublic ? import_picocolors8.default.green("yes") : import_picocolors8.default.yellow("no")],
|
|
10166
9059
|
["Completions", String(t2.completions ?? 0)],
|
|
10167
|
-
["Created",
|
|
9060
|
+
["Created", formatDate(t2.createdAt)]
|
|
10168
9061
|
]);
|
|
10169
9062
|
console.log("");
|
|
10170
9063
|
}
|
|
10171
9064
|
function printTaskLine(t2) {
|
|
10172
9065
|
const check = getCheckIndicator(t2);
|
|
10173
|
-
const rawText = truncate(
|
|
9066
|
+
const rawText = truncate(t2.text, 50);
|
|
10174
9067
|
const text = t2.completed ? import_picocolors8.default.strikethrough(import_picocolors8.default.dim(rawText)) : rawText;
|
|
10175
9068
|
const time = extractTime(t2.dueDate);
|
|
10176
9069
|
const tags = formatTags(t2.tags);
|
|
10177
9070
|
const difficulty = formatDifficulty(t2.difficulty);
|
|
10178
|
-
const id = import_picocolors8.default.dim(
|
|
9071
|
+
const id = import_picocolors8.default.dim(t2.id);
|
|
10179
9072
|
const parts = [check, text];
|
|
10180
9073
|
if (time) parts.push(import_picocolors8.default.cyan(time));
|
|
10181
9074
|
if (tags) parts.push(tags);
|
|
@@ -10204,8 +9097,7 @@ function registerTasksCommands(program3) {
|
|
|
10204
9097
|
console.log(` ${import_picocolors8.default.bold("Backlog")} ${import_picocolors8.default.dim(`(${items.length})`)}`);
|
|
10205
9098
|
} else {
|
|
10206
9099
|
const viewDate = date ? /* @__PURE__ */ new Date(date + "T00:00:00") : /* @__PURE__ */ new Date();
|
|
10207
|
-
|
|
10208
|
-
console.log(formatWeekdayHeader(viewDate, streakCount));
|
|
9100
|
+
console.log(formatWeekdayHeader(viewDate, completed.length));
|
|
10209
9101
|
}
|
|
10210
9102
|
const tagLine = formatTagsSummary(items);
|
|
10211
9103
|
if (tagLine) console.log(`
|
|
@@ -10412,11 +9304,10 @@ Examples:
|
|
|
10412
9304
|
dataKey: "task",
|
|
10413
9305
|
spinnerMessage: "Creating task...",
|
|
10414
9306
|
onInteractive: (_task, payload) => {
|
|
10415
|
-
const task = payload
|
|
9307
|
+
const { task, karma } = payload;
|
|
10416
9308
|
const check = import_picocolors8.default.green(SYM.check);
|
|
10417
9309
|
console.log(`
|
|
10418
9310
|
${check} Created ${task.text}`);
|
|
10419
|
-
const karma = payload.karma;
|
|
10420
9311
|
if (karma) {
|
|
10421
9312
|
console.log(` ${formatKarmaGain(karma)}${" ".repeat(20)}${import_picocolors8.default.dim(task.id)}`);
|
|
10422
9313
|
}
|
|
@@ -10471,9 +9362,9 @@ Examples:
|
|
|
10471
9362
|
fn: () => updateTask(uid, taskId, body),
|
|
10472
9363
|
dataKey: "task",
|
|
10473
9364
|
spinnerMessage: "Updating task...",
|
|
10474
|
-
onInteractive: (
|
|
9365
|
+
onInteractive: (payload) => {
|
|
10475
9366
|
console.log(`
|
|
10476
|
-
${import_picocolors8.default.green("Updated!")} ${task.text} ${import_picocolors8.default.dim(task.id)}
|
|
9367
|
+
${import_picocolors8.default.green("Updated!")} ${payload.task.text} ${import_picocolors8.default.dim(payload.task.id)}
|
|
10477
9368
|
`);
|
|
10478
9369
|
}
|
|
10479
9370
|
});
|
|
@@ -10503,7 +9394,7 @@ Examples:
|
|
|
10503
9394
|
let taskText = taskId;
|
|
10504
9395
|
try {
|
|
10505
9396
|
const task = await getTask(uid, taskId);
|
|
10506
|
-
taskText =
|
|
9397
|
+
taskText = task.text ?? taskId;
|
|
10507
9398
|
} catch {
|
|
10508
9399
|
}
|
|
10509
9400
|
const confirmed = await promptConfirm({
|
|
@@ -10521,9 +9412,8 @@ Examples:
|
|
|
10521
9412
|
spinnerMessage: "Deleting task...",
|
|
10522
9413
|
onInteractive: (data) => {
|
|
10523
9414
|
const cross = import_picocolors8.default.red(SYM.cross);
|
|
10524
|
-
const text = data.taskText || taskId;
|
|
10525
9415
|
console.log(`
|
|
10526
|
-
${cross} Deleted ${
|
|
9416
|
+
${cross} Deleted ${data.taskText || taskId}`);
|
|
10527
9417
|
if (data.archived) console.log(` ${import_picocolors8.default.dim("Archived")}`);
|
|
10528
9418
|
console.log("");
|
|
10529
9419
|
}
|
|
@@ -10555,13 +9445,10 @@ Examples:
|
|
|
10555
9445
|
spinnerMessage: "Completing task...",
|
|
10556
9446
|
onInteractive: (data) => {
|
|
10557
9447
|
const check = import_picocolors8.default.green(SYM.check);
|
|
10558
|
-
const text = data.taskText ?? taskId;
|
|
10559
9448
|
console.log(`
|
|
10560
|
-
${check} Done! ${
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
if (karma) {
|
|
10564
|
-
console.log(` ${formatKarmaGain(karma, checksInRow)}`);
|
|
9449
|
+
${check} Done! ${data.taskText ?? taskId}`);
|
|
9450
|
+
if (data.karma) {
|
|
9451
|
+
console.log(` ${formatKarmaGain(data.karma, data.checksInRow)}`);
|
|
10565
9452
|
}
|
|
10566
9453
|
console.log("");
|
|
10567
9454
|
}
|
|
@@ -10593,9 +9480,8 @@ Examples:
|
|
|
10593
9480
|
spinnerMessage: "Uncompleting task...",
|
|
10594
9481
|
onInteractive: (data) => {
|
|
10595
9482
|
const arrow = SYM.undo;
|
|
10596
|
-
const text = data.text ?? taskId;
|
|
10597
9483
|
console.log(`
|
|
10598
|
-
${import_picocolors8.default.yellow(arrow)} Reverted ${text}`);
|
|
9484
|
+
${import_picocolors8.default.yellow(arrow)} Reverted ${data.task.text ?? taskId}`);
|
|
10599
9485
|
console.log(` ${import_picocolors8.default.dim("Karma adjustment applied")}`);
|
|
10600
9486
|
console.log("");
|
|
10601
9487
|
}
|
|
@@ -10606,13 +9492,10 @@ Examples:
|
|
|
10606
9492
|
}
|
|
10607
9493
|
|
|
10608
9494
|
// src/cli/commands/posts.ts
|
|
10609
|
-
init_define_ADMIN_UIDS();
|
|
10610
9495
|
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
10611
9496
|
|
|
10612
9497
|
// src/cli/lib/pagination.ts
|
|
10613
|
-
init_define_ADMIN_UIDS();
|
|
10614
9498
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
10615
|
-
init_errors();
|
|
10616
9499
|
function printPaginationHint(opts) {
|
|
10617
9500
|
if (!opts.nextCursor) return;
|
|
10618
9501
|
const parts = [opts.command, `--cursor ${opts.nextCursor}`];
|
|
@@ -10622,223 +9505,56 @@ ${import_picocolors9.default.dim("Next page:")} ${import_picocolors9.default.dim
|
|
|
10622
9505
|
}
|
|
10623
9506
|
|
|
10624
9507
|
// src/cli/services/posts.ts
|
|
10625
|
-
init_define_ADMIN_UIDS();
|
|
10626
|
-
function generateSlug(title) {
|
|
10627
|
-
return title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 80) + "-" + Date.now().toString(36);
|
|
10628
|
-
}
|
|
10629
9508
|
async function listPosts(opts) {
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
};
|
|
10635
|
-
if (opts.cursor) {
|
|
10636
|
-
const cursorDoc = await getDoc(`posts/${opts.cursor}`);
|
|
10637
|
-
if (cursorDoc.createdAt != null) {
|
|
10638
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10639
|
-
}
|
|
10640
|
-
}
|
|
10641
|
-
const docs = await runQuery("", "posts", queryOpts);
|
|
10642
|
-
const hasMore = docs.length > lim;
|
|
10643
|
-
const posts = docs.slice(0, lim);
|
|
10644
|
-
return {
|
|
10645
|
-
posts,
|
|
10646
|
-
nextCursor: hasMore ? posts[posts.length - 1].id : void 0
|
|
10647
|
-
};
|
|
9509
|
+
return api.get("/api/posts", {
|
|
9510
|
+
cursor: opts.cursor,
|
|
9511
|
+
limit: opts.limit?.toString()
|
|
9512
|
+
});
|
|
10648
9513
|
}
|
|
10649
9514
|
async function getPost(id) {
|
|
10650
|
-
|
|
10651
|
-
const post = await getDoc(`posts/${id}`);
|
|
10652
|
-
if (post.authorId) {
|
|
10653
|
-
try {
|
|
10654
|
-
const author = await getDoc(`users/${post.authorId}`);
|
|
10655
|
-
post.authorName = author.username ?? null;
|
|
10656
|
-
} catch {
|
|
10657
|
-
}
|
|
10658
|
-
}
|
|
10659
|
-
return post;
|
|
9515
|
+
return api.get(`/api/posts/${encodeURIComponent(id)}`);
|
|
10660
9516
|
}
|
|
10661
9517
|
async function createPost(uid, body) {
|
|
10662
|
-
|
|
10663
|
-
const postData = {
|
|
10664
|
-
title: body.title,
|
|
10665
|
-
body: body.body,
|
|
10666
|
-
tag: body.tag,
|
|
10667
|
-
authorId: uid,
|
|
10668
|
-
slug: generateSlug(body.title),
|
|
10669
|
-
isPublic: body.isPublic !== false,
|
|
10670
|
-
createdAt: now2,
|
|
10671
|
-
updatedAt: now2,
|
|
10672
|
-
commentsCount: 0,
|
|
10673
|
-
likesCount: 0
|
|
10674
|
-
};
|
|
10675
|
-
const doc = await createDoc("posts", postData);
|
|
10676
|
-
const postId = doc.id;
|
|
10677
|
-
try {
|
|
10678
|
-
await giveKarma(uid, "createPost", postId, KARMA_POINTS.createPost, String(body.title));
|
|
10679
|
-
} catch {
|
|
10680
|
-
}
|
|
10681
|
-
try {
|
|
10682
|
-
await incrementField(`users/${uid}/activity/totals`, "posts.written", 1);
|
|
10683
|
-
} catch {
|
|
10684
|
-
}
|
|
10685
|
-
return { post: doc, karma: KARMA_POINTS.createPost };
|
|
9518
|
+
return api.post("/api/posts", body);
|
|
10686
9519
|
}
|
|
10687
9520
|
async function updatePost(uid, id, body) {
|
|
10688
|
-
|
|
10689
|
-
const post = await getDoc(`posts/${id}`);
|
|
10690
|
-
checkOwnership(post, uid, "update");
|
|
10691
|
-
const allowed = ["title", "body", "tag", "isPublic"];
|
|
10692
|
-
const update = { updatedAt: Date.now() };
|
|
10693
|
-
const fieldMask = ["updatedAt"];
|
|
10694
|
-
for (const key of allowed) {
|
|
10695
|
-
if (key in body) {
|
|
10696
|
-
update[key] = body[key];
|
|
10697
|
-
fieldMask.push(key);
|
|
10698
|
-
}
|
|
10699
|
-
}
|
|
10700
|
-
await updateDoc(`posts/${id}`, update, fieldMask);
|
|
10701
|
-
const updated = await getDoc(`posts/${id}`);
|
|
10702
|
-
return { post: updated };
|
|
9521
|
+
return api.patch(`/api/posts/${encodeURIComponent(id)}`, body);
|
|
10703
9522
|
}
|
|
10704
9523
|
async function deletePost(uid, id) {
|
|
10705
|
-
|
|
10706
|
-
const post = await getDoc(`posts/${id}`);
|
|
10707
|
-
checkOwnership(post, uid, "delete");
|
|
10708
|
-
await deleteDoc(`posts/${id}`);
|
|
9524
|
+
await api.del(`/api/posts/${encodeURIComponent(id)}`);
|
|
10709
9525
|
}
|
|
10710
9526
|
|
|
10711
9527
|
// src/cli/services/comments.ts
|
|
10712
|
-
init_define_ADMIN_UIDS();
|
|
10713
9528
|
async function listComments(postId, opts) {
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
limit: lim + 1
|
|
10719
|
-
};
|
|
10720
|
-
if (opts.cursor) {
|
|
10721
|
-
const cursorDoc = await getDoc(`posts/${postId}/comments/${opts.cursor}`);
|
|
10722
|
-
if (cursorDoc.createdAt != null) {
|
|
10723
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10724
|
-
}
|
|
10725
|
-
}
|
|
10726
|
-
const docs = await runQuery(`posts/${postId}`, "comments", queryOpts);
|
|
10727
|
-
const hasMore = docs.length > lim;
|
|
10728
|
-
const comments = docs.slice(0, lim);
|
|
10729
|
-
const uids = [...new Set(comments.map((c) => c.userId).filter(Boolean))];
|
|
10730
|
-
const userMap = {};
|
|
10731
|
-
await Promise.all(uids.map(async (uid) => {
|
|
10732
|
-
try {
|
|
10733
|
-
const user = await getDoc(`users/${uid}`);
|
|
10734
|
-
if (user.username) userMap[uid] = user.username;
|
|
10735
|
-
} catch {
|
|
10736
|
-
}
|
|
10737
|
-
}));
|
|
10738
|
-
for (const c of comments) {
|
|
10739
|
-
c.authorName = userMap[c.userId] ?? null;
|
|
10740
|
-
}
|
|
10741
|
-
return {
|
|
10742
|
-
comments,
|
|
10743
|
-
nextCursor: hasMore ? comments[comments.length - 1].id : void 0
|
|
10744
|
-
};
|
|
9529
|
+
return api.get(`/api/posts/${encodeURIComponent(postId)}/comments`, {
|
|
9530
|
+
cursor: opts.cursor,
|
|
9531
|
+
limit: opts.limit?.toString()
|
|
9532
|
+
});
|
|
10745
9533
|
}
|
|
10746
9534
|
async function createComment(uid, postId, text) {
|
|
10747
|
-
|
|
10748
|
-
const now2 = Date.now();
|
|
10749
|
-
const commentData = {
|
|
10750
|
-
postId,
|
|
10751
|
-
userId: uid,
|
|
10752
|
-
text,
|
|
10753
|
-
createdAt: now2,
|
|
10754
|
-
updatedAt: now2,
|
|
10755
|
-
textLength: text.length,
|
|
10756
|
-
likes: [],
|
|
10757
|
-
repliesCount: 0
|
|
10758
|
-
};
|
|
10759
|
-
const doc = await createDoc(`posts/${postId}/comments`, commentData);
|
|
10760
|
-
const commentId = doc.id;
|
|
10761
|
-
await incrementField(`posts/${postId}`, "commentsCount", 1);
|
|
10762
|
-
try {
|
|
10763
|
-
await giveKarma(uid, "addComment", `${postId}_${commentId}`, KARMA_POINTS.addComment, text);
|
|
10764
|
-
} catch {
|
|
10765
|
-
}
|
|
10766
|
-
try {
|
|
10767
|
-
await incrementField(`users/${uid}/activity/totals`, "comments.written", 1);
|
|
10768
|
-
} catch {
|
|
10769
|
-
}
|
|
10770
|
-
return { comment: doc, karma: KARMA_POINTS.addComment };
|
|
9535
|
+
return api.post(`/api/posts/${encodeURIComponent(postId)}/comments`, { text });
|
|
10771
9536
|
}
|
|
10772
9537
|
async function deleteComment(uid, postId, commentId) {
|
|
10773
|
-
|
|
10774
|
-
validateDocId(commentId, "Comment ID");
|
|
10775
|
-
const comment = await getDoc(`posts/${postId}/comments/${commentId}`);
|
|
10776
|
-
checkOwnership(comment, uid, "delete");
|
|
10777
|
-
await deleteDoc(`posts/${postId}/comments/${commentId}`);
|
|
10778
|
-
await incrementField(`posts/${postId}`, "commentsCount", -1);
|
|
9538
|
+
await api.del(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}`);
|
|
10779
9539
|
}
|
|
10780
9540
|
|
|
10781
9541
|
// src/cli/services/replies.ts
|
|
10782
|
-
init_define_ADMIN_UIDS();
|
|
10783
9542
|
async function listReplies(postId, commentId, opts) {
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
orderBy: [{ field: "createdAt", direction: "ASCENDING" }],
|
|
10789
|
-
limit: lim + 1
|
|
10790
|
-
};
|
|
10791
|
-
if (opts.cursor) {
|
|
10792
|
-
const cursorDoc = await getDoc(`posts/${postId}/comments/${commentId}/replies/${opts.cursor}`);
|
|
10793
|
-
if (cursorDoc.createdAt != null) {
|
|
10794
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10795
|
-
}
|
|
10796
|
-
}
|
|
10797
|
-
const docs = await runQuery(`posts/${postId}/comments/${commentId}`, "replies", queryOpts);
|
|
10798
|
-
const hasMore = docs.length > lim;
|
|
10799
|
-
const replies = docs.slice(0, lim);
|
|
10800
|
-
return {
|
|
10801
|
-
replies,
|
|
10802
|
-
nextCursor: hasMore ? replies[replies.length - 1].id : void 0
|
|
10803
|
-
};
|
|
9543
|
+
return api.get(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies`, {
|
|
9544
|
+
cursor: opts.cursor,
|
|
9545
|
+
limit: opts.limit?.toString()
|
|
9546
|
+
});
|
|
10804
9547
|
}
|
|
10805
9548
|
async function createReply(uid, postId, commentId, text) {
|
|
10806
|
-
|
|
10807
|
-
validateDocId(commentId, "Comment ID");
|
|
10808
|
-
const now2 = Date.now();
|
|
10809
|
-
const replyData = {
|
|
10810
|
-
postId,
|
|
10811
|
-
userId: uid,
|
|
10812
|
-
text,
|
|
10813
|
-
createdAt: now2,
|
|
10814
|
-
updatedAt: now2,
|
|
10815
|
-
textLength: text.length,
|
|
10816
|
-
likes: [],
|
|
10817
|
-
parentCommentId: commentId
|
|
10818
|
-
};
|
|
10819
|
-
const doc = await createDoc(`posts/${postId}/comments/${commentId}/replies`, replyData);
|
|
10820
|
-
const replyId = doc.id;
|
|
10821
|
-
await incrementField(`posts/${postId}/comments/${commentId}`, "repliesCount", 1);
|
|
10822
|
-
try {
|
|
10823
|
-
await giveKarma(uid, "addComment", `${postId}_${replyId}`, KARMA_POINTS.addComment, text);
|
|
10824
|
-
} catch {
|
|
10825
|
-
}
|
|
10826
|
-
try {
|
|
10827
|
-
await incrementField(`users/${uid}/activity/totals`, "replies.written", 1);
|
|
10828
|
-
} catch {
|
|
10829
|
-
}
|
|
10830
|
-
return { reply: doc, karma: KARMA_POINTS.addComment };
|
|
9549
|
+
return api.post(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies`, { text });
|
|
10831
9550
|
}
|
|
10832
9551
|
async function deleteReply(uid, postId, commentId, replyId) {
|
|
10833
|
-
|
|
10834
|
-
validateDocId(commentId, "Comment ID");
|
|
10835
|
-
validateDocId(replyId, "Reply ID");
|
|
10836
|
-
const reply = await getDoc(`posts/${postId}/comments/${commentId}/replies/${replyId}`);
|
|
10837
|
-
checkOwnership(reply, uid, "delete");
|
|
10838
|
-
await deleteDoc(`posts/${postId}/comments/${commentId}/replies/${replyId}`);
|
|
10839
|
-
await incrementField(`posts/${postId}/comments/${commentId}`, "repliesCount", -1);
|
|
9552
|
+
await api.del(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies/${encodeURIComponent(replyId)}`);
|
|
10840
9553
|
}
|
|
10841
9554
|
|
|
9555
|
+
// src/cli/types/api.ts
|
|
9556
|
+
var POST_TAGS = ["general", "hack", "story", "meme", "other", "question", "hack-tip", "activity"];
|
|
9557
|
+
|
|
10842
9558
|
// src/cli/commands/posts.ts
|
|
10843
9559
|
init_prompts();
|
|
10844
9560
|
init_tty();
|
|
@@ -10851,7 +9567,7 @@ async function pickComment(postId, commentId) {
|
|
|
10851
9567
|
message: "Select comment",
|
|
10852
9568
|
options: comments.map((c) => ({
|
|
10853
9569
|
value: c.id,
|
|
10854
|
-
label: `${truncate(
|
|
9570
|
+
label: `${truncate(c.text ?? "", 60)} ${import_picocolors10.default.dim(c.authorName ?? c.userId ?? "")}`
|
|
10855
9571
|
}))
|
|
10856
9572
|
});
|
|
10857
9573
|
}
|
|
@@ -10864,16 +9580,16 @@ async function pickReply(postId, commentId, replyId) {
|
|
|
10864
9580
|
message: "Select reply",
|
|
10865
9581
|
options: replies.map((r) => ({
|
|
10866
9582
|
value: r.id,
|
|
10867
|
-
label: `${truncate(
|
|
9583
|
+
label: `${truncate(r.text ?? "", 60)} ${import_picocolors10.default.dim(r.userId ?? "")}`
|
|
10868
9584
|
}))
|
|
10869
9585
|
});
|
|
10870
9586
|
}
|
|
10871
9587
|
function printPostLine(p) {
|
|
10872
|
-
const tag = import_picocolors10.default.cyan(
|
|
10873
|
-
const title = truncate(
|
|
9588
|
+
const tag = import_picocolors10.default.cyan(p.tag ?? "");
|
|
9589
|
+
const title = truncate(p.title, 55);
|
|
10874
9590
|
const comments = p.commentsCount ? import_picocolors10.default.dim(`${p.commentsCount} comments`) : "";
|
|
10875
9591
|
const time = formatRelativeDate(p.createdAt);
|
|
10876
|
-
const id = import_picocolors10.default.dim(
|
|
9592
|
+
const id = import_picocolors10.default.dim(p.id);
|
|
10877
9593
|
const parts = [tag, import_picocolors10.default.bold(title)];
|
|
10878
9594
|
if (comments) parts.push(comments);
|
|
10879
9595
|
parts.push(import_picocolors10.default.dim(time));
|
|
@@ -10890,7 +9606,7 @@ function printPostDetail(p) {
|
|
|
10890
9606
|
console.log(` ${import_picocolors10.default.dim(SYM.dash.repeat(40))}`);
|
|
10891
9607
|
printRecord([
|
|
10892
9608
|
["ID", import_picocolors10.default.dim(p.id)],
|
|
10893
|
-
["Tag", import_picocolors10.default.cyan(
|
|
9609
|
+
["Tag", import_picocolors10.default.cyan(p.tag ?? "")],
|
|
10894
9610
|
["Author", p.authorName ?? p.authorId],
|
|
10895
9611
|
["Comments", p.commentsCount != null ? String(p.commentsCount) : null],
|
|
10896
9612
|
["Likes", p.likesCount != null ? String(p.likesCount) : null],
|
|
@@ -10899,18 +9615,18 @@ function printPostDetail(p) {
|
|
|
10899
9615
|
console.log("");
|
|
10900
9616
|
}
|
|
10901
9617
|
function printCommentLine(c) {
|
|
10902
|
-
const author = import_picocolors10.default.bold(
|
|
9618
|
+
const author = import_picocolors10.default.bold(c.authorName ?? c.userId ?? "");
|
|
10903
9619
|
const time = import_picocolors10.default.dim(formatRelativeDate(c.createdAt));
|
|
10904
9620
|
const replies = c.repliesCount ? import_picocolors10.default.dim(`\xB7 ${c.repliesCount} replies`) : "";
|
|
10905
|
-
const text =
|
|
9621
|
+
const text = c.text ?? "";
|
|
10906
9622
|
console.log(` ${author} ${time}${replies}`);
|
|
10907
9623
|
console.log(` ${text}`);
|
|
10908
9624
|
console.log("");
|
|
10909
9625
|
}
|
|
10910
9626
|
function printReplyLine(r) {
|
|
10911
|
-
const text = truncate(
|
|
9627
|
+
const text = truncate(r.text ?? "", 60);
|
|
10912
9628
|
const time = formatRelativeDate(r.createdAt);
|
|
10913
|
-
const id = import_picocolors10.default.dim(
|
|
9629
|
+
const id = import_picocolors10.default.dim(r.id);
|
|
10914
9630
|
console.log(` ${text} ${import_picocolors10.default.dim(time)} ${id}`);
|
|
10915
9631
|
}
|
|
10916
9632
|
function registerPostsCommands(program3) {
|
|
@@ -11012,148 +9728,134 @@ Examples:
|
|
|
11012
9728
|
}
|
|
11013
9729
|
});
|
|
11014
9730
|
});
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
options: POST_TAGS.map((t2) => ({ value: t2, label: t2 }))
|
|
11026
|
-
});
|
|
11027
|
-
}
|
|
11028
|
-
const body = { title, body: postBody, tag };
|
|
11029
|
-
await runCreate({
|
|
11030
|
-
global: opts,
|
|
11031
|
-
fn: () => createPost(uid, body),
|
|
11032
|
-
dataKey: "post",
|
|
11033
|
-
spinnerMessage: "Creating post...",
|
|
11034
|
-
onInteractive: (post) => {
|
|
11035
|
-
console.log(`
|
|
11036
|
-
${import_picocolors10.default.green("Posted!")} ${post.title} ${import_picocolors10.default.dim(post.id)}
|
|
11037
|
-
`);
|
|
11038
|
-
}
|
|
9731
|
+
posts.command("create").description("Create a new post").option("--title <title>").option("--body <body>").option("--tag <tag>", "general|hack|story|meme|other|question|hack-tip|activity").action(async function() {
|
|
9732
|
+
const opts = this.optsWithGlobals();
|
|
9733
|
+
const uid = requireUid();
|
|
9734
|
+
const title = await promptForMissing({ value: opts.title, message: "Title", placeholder: "Post title" });
|
|
9735
|
+
const postBody = await promptForMissing({ value: opts.body, message: "Body", placeholder: "Post body" });
|
|
9736
|
+
let tag = opts.tag;
|
|
9737
|
+
if (!tag) {
|
|
9738
|
+
tag = await promptSelect({
|
|
9739
|
+
message: "Tag",
|
|
9740
|
+
options: POST_TAGS.map((t2) => ({ value: t2, label: t2 }))
|
|
11039
9741
|
});
|
|
9742
|
+
}
|
|
9743
|
+
const body = { title, body: postBody, tag };
|
|
9744
|
+
await runCreate({
|
|
9745
|
+
global: opts,
|
|
9746
|
+
fn: () => createPost(uid, body),
|
|
9747
|
+
dataKey: "post",
|
|
9748
|
+
spinnerMessage: "Creating post...",
|
|
9749
|
+
onInteractive: (post, payload) => {
|
|
9750
|
+
console.log(`
|
|
9751
|
+
${import_picocolors10.default.green("Posted!")} ${payload.post.title} ${import_picocolors10.default.dim(payload.post.id)}
|
|
9752
|
+
`);
|
|
9753
|
+
}
|
|
11040
9754
|
});
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
9755
|
+
});
|
|
9756
|
+
posts.command("update [id]").description("Update a post").option("--title <title>").option("--body <body>").option("--tag <tag>").action(async function(id) {
|
|
9757
|
+
const opts = this.optsWithGlobals();
|
|
9758
|
+
const uid = requireUid();
|
|
9759
|
+
const postId = await promptForMissing({ value: id, message: "Post ID" });
|
|
9760
|
+
const body = {};
|
|
9761
|
+
const hasAnyFlag = opts.title || opts.body || opts.tag;
|
|
9762
|
+
if (!hasAnyFlag && isInteractive() && !opts.json) {
|
|
9763
|
+
const title = await promptText({ message: "Title (enter to skip)", required: false });
|
|
9764
|
+
if (title) body.title = title;
|
|
9765
|
+
const postBody = await promptText({ message: "Body (enter to skip)", required: false });
|
|
9766
|
+
if (postBody) body.body = postBody;
|
|
9767
|
+
const changeTag = await promptText({ message: "Tag (enter to skip)", placeholder: POST_TAGS.join("|"), required: false });
|
|
9768
|
+
if (changeTag) body.tag = changeTag;
|
|
9769
|
+
} else {
|
|
9770
|
+
if (opts.title) body.title = opts.title;
|
|
9771
|
+
if (opts.body) body.body = opts.body;
|
|
9772
|
+
if (opts.tag) body.tag = opts.tag;
|
|
9773
|
+
}
|
|
9774
|
+
await runWrite({
|
|
9775
|
+
global: opts,
|
|
9776
|
+
fn: () => updatePost(uid, postId, body),
|
|
9777
|
+
dataKey: "post",
|
|
9778
|
+
spinnerMessage: "Updating post...",
|
|
9779
|
+
onInteractive: (payload) => {
|
|
9780
|
+
console.log(`
|
|
9781
|
+
${import_picocolors10.default.green("Updated!")} ${payload.post.title} ${import_picocolors10.default.dim(payload.post.id)}
|
|
11067
9782
|
`);
|
|
11068
|
-
|
|
11069
|
-
});
|
|
9783
|
+
}
|
|
11070
9784
|
});
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
9785
|
+
});
|
|
9786
|
+
posts.command("delete [id]").description("Delete a post").action(async function(id) {
|
|
9787
|
+
const uid = requireUid();
|
|
9788
|
+
const postId = await promptForMissing({ value: id, message: "Post ID" });
|
|
9789
|
+
await runDelete({
|
|
9790
|
+
global: this.optsWithGlobals(),
|
|
9791
|
+
fn: () => deletePost(uid, postId),
|
|
9792
|
+
successMessage: ` ${import_picocolors10.default.green("Deleted!")} Post ${import_picocolors10.default.dim(postId)}`,
|
|
9793
|
+
spinnerMessage: "Deleting post..."
|
|
11080
9794
|
});
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
9795
|
+
});
|
|
9796
|
+
posts.command("comment [postId]").description("Add a comment to a post").option("--text <text>").action(async function(postId) {
|
|
9797
|
+
const opts = this.optsWithGlobals();
|
|
9798
|
+
const uid = requireUid();
|
|
9799
|
+
const resolvedPostId = await promptForMissing({ value: postId, message: "Post ID" });
|
|
9800
|
+
const text = await promptForMissing({ value: opts.text, message: "Comment text", placeholder: "Your comment" });
|
|
9801
|
+
await runCreate({
|
|
9802
|
+
global: opts,
|
|
9803
|
+
fn: () => createComment(uid, resolvedPostId, text),
|
|
9804
|
+
dataKey: "comment",
|
|
9805
|
+
spinnerMessage: "Adding comment...",
|
|
9806
|
+
onInteractive: (comment, payload) => {
|
|
9807
|
+
console.log(`
|
|
9808
|
+
${import_picocolors10.default.green("Commented!")} ${truncate(payload.comment.text ?? "", 50)} ${import_picocolors10.default.dim(payload.comment.id)}
|
|
11094
9809
|
`);
|
|
11095
|
-
|
|
11096
|
-
});
|
|
9810
|
+
}
|
|
11097
9811
|
});
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
9812
|
+
});
|
|
9813
|
+
posts.command("comment-delete [postId] [commentId]").description("Delete a comment").action(async function(postId, commentId) {
|
|
9814
|
+
const uid = requireUid();
|
|
9815
|
+
const resolvedPostId = await promptForMissing({ value: postId, message: "Post ID" });
|
|
9816
|
+
const resolvedCommentId = await pickComment(resolvedPostId, commentId);
|
|
9817
|
+
await runDelete({
|
|
9818
|
+
global: this.optsWithGlobals(),
|
|
9819
|
+
fn: () => deleteComment(uid, resolvedPostId, resolvedCommentId),
|
|
9820
|
+
successMessage: ` ${import_picocolors10.default.green("Deleted!")} Comment ${import_picocolors10.default.dim(resolvedCommentId)}`,
|
|
9821
|
+
spinnerMessage: "Deleting comment..."
|
|
11108
9822
|
});
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
9823
|
+
});
|
|
9824
|
+
posts.command("reply [postId] [commentId]").description("Add a reply to a comment").option("--text <text>").action(async function(postId, commentId) {
|
|
9825
|
+
const opts = this.optsWithGlobals();
|
|
9826
|
+
const uid = requireUid();
|
|
9827
|
+
const resolvedPostId = await promptForMissing({ value: postId, message: "Post ID" });
|
|
9828
|
+
const resolvedCommentId = await pickComment(resolvedPostId, commentId);
|
|
9829
|
+
const text = await promptForMissing({ value: opts.text, message: "Reply text", placeholder: "Your reply" });
|
|
9830
|
+
await runCreate({
|
|
9831
|
+
global: opts,
|
|
9832
|
+
fn: () => createReply(uid, resolvedPostId, resolvedCommentId, text),
|
|
9833
|
+
dataKey: "reply",
|
|
9834
|
+
spinnerMessage: "Adding reply...",
|
|
9835
|
+
onInteractive: (reply, payload) => {
|
|
9836
|
+
console.log(`
|
|
9837
|
+
${import_picocolors10.default.green("Replied!")} ${truncate(payload.reply.text ?? "", 50)} ${import_picocolors10.default.dim(payload.reply.id)}
|
|
11123
9838
|
`);
|
|
11124
|
-
|
|
11125
|
-
});
|
|
9839
|
+
}
|
|
11126
9840
|
});
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
9841
|
+
});
|
|
9842
|
+
posts.command("reply-delete [postId] [commentId] [replyId]").description("Delete a reply").action(async function(postId, commentId, replyId) {
|
|
9843
|
+
const uid = requireUid();
|
|
9844
|
+
const resolvedPostId = await promptForMissing({ value: postId, message: "Post ID" });
|
|
9845
|
+
const resolvedCommentId = await pickComment(resolvedPostId, commentId);
|
|
9846
|
+
const resolvedReplyId = await pickReply(resolvedPostId, resolvedCommentId, replyId);
|
|
9847
|
+
await runDelete({
|
|
9848
|
+
global: this.optsWithGlobals(),
|
|
9849
|
+
fn: () => deleteReply(uid, resolvedPostId, resolvedCommentId, resolvedReplyId),
|
|
9850
|
+
successMessage: ` ${import_picocolors10.default.green("Deleted!")} Reply ${import_picocolors10.default.dim(resolvedReplyId)}`,
|
|
9851
|
+
spinnerMessage: "Deleting reply..."
|
|
11138
9852
|
});
|
|
11139
|
-
}
|
|
9853
|
+
});
|
|
11140
9854
|
}
|
|
11141
9855
|
|
|
11142
|
-
// src/cli/commands/profile.ts
|
|
11143
|
-
init_define_ADMIN_UIDS();
|
|
11144
|
-
|
|
11145
9856
|
// src/cli/services/profile.ts
|
|
11146
|
-
init_define_ADMIN_UIDS();
|
|
11147
9857
|
async function getProfile() {
|
|
11148
|
-
|
|
11149
|
-
if (!creds) throw new Error("Not logged in. Run: numo login");
|
|
11150
|
-
const doc = await getDoc(`users/${creds.uid}`);
|
|
11151
|
-
return {
|
|
11152
|
-
uid: creds.uid,
|
|
11153
|
-
email: creds.email ?? null,
|
|
11154
|
-
username: doc.username ?? null,
|
|
11155
|
-
photoURL: doc.photoURL ?? null
|
|
11156
|
-
};
|
|
9858
|
+
return api.get("/api/profile");
|
|
11157
9859
|
}
|
|
11158
9860
|
|
|
11159
9861
|
// src/cli/commands/profile.ts
|
|
@@ -11177,11 +9879,9 @@ function registerProfileCommands(program3) {
|
|
|
11177
9879
|
}
|
|
11178
9880
|
|
|
11179
9881
|
// src/cli/commands/doctor.ts
|
|
11180
|
-
init_define_ADMIN_UIDS();
|
|
11181
9882
|
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
11182
|
-
init_config();
|
|
11183
|
-
init_config();
|
|
11184
9883
|
init_tty();
|
|
9884
|
+
var API_BASE5 = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
11185
9885
|
async function runChecks() {
|
|
11186
9886
|
const checks = [];
|
|
11187
9887
|
const nodeVersion = process.version;
|
|
@@ -11191,6 +9891,11 @@ async function runChecks() {
|
|
|
11191
9891
|
status: major >= 18 ? "ok" : "fail",
|
|
11192
9892
|
message: major >= 18 ? `Node ${nodeVersion}` : `Node ${nodeVersion} \u2014 requires >= 18`
|
|
11193
9893
|
});
|
|
9894
|
+
checks.push({
|
|
9895
|
+
name: "api_url",
|
|
9896
|
+
status: process.env.NUMO_API_URL ? "ok" : "warn",
|
|
9897
|
+
message: process.env.NUMO_API_URL ? `API URL: ${API_BASE5}` : `NUMO_API_URL not set (using default: ${API_BASE5})`
|
|
9898
|
+
});
|
|
11194
9899
|
const creds = loadCredentials();
|
|
11195
9900
|
checks.push({
|
|
11196
9901
|
name: "credentials",
|
|
@@ -11207,18 +9912,11 @@ async function runChecks() {
|
|
|
11207
9912
|
} else {
|
|
11208
9913
|
checks.push({ name: "token", status: "fail", message: "Skipped (no credentials)" });
|
|
11209
9914
|
}
|
|
11210
|
-
const apiKey = getFirebaseApiKey();
|
|
11211
|
-
checks.push({
|
|
11212
|
-
name: "api_key",
|
|
11213
|
-
status: apiKey ? "ok" : "fail",
|
|
11214
|
-
message: apiKey ? "Firebase API key configured" : "NUMO_FIREBASE_API_KEY not set"
|
|
11215
|
-
});
|
|
11216
9915
|
try {
|
|
11217
|
-
const
|
|
11218
|
-
|
|
11219
|
-
checks.push({ name: "firebase_reachable", status: "ok", message: `Firebase reachable (HTTP ${resp.status})` });
|
|
9916
|
+
const resp = await fetch(`${API_BASE5}/api/health`, { signal: AbortSignal.timeout(5e3) });
|
|
9917
|
+
checks.push({ name: "api_reachable", status: "ok", message: `API server reachable (HTTP ${resp.status})` });
|
|
11220
9918
|
} catch (err) {
|
|
11221
|
-
checks.push({ name: "
|
|
9919
|
+
checks.push({ name: "api_reachable", status: "fail", message: `API server unreachable: ${err.message}` });
|
|
11222
9920
|
}
|
|
11223
9921
|
return checks;
|
|
11224
9922
|
}
|
|
@@ -11249,19 +9947,18 @@ function registerDoctorCommand(program3) {
|
|
|
11249
9947
|
}
|
|
11250
9948
|
|
|
11251
9949
|
// src/cli/lib/update-check.ts
|
|
11252
|
-
|
|
11253
|
-
var
|
|
11254
|
-
var path3 = __toESM(require("path"), 1);
|
|
9950
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
9951
|
+
var path2 = __toESM(require("path"), 1);
|
|
11255
9952
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
11256
9953
|
init_tty();
|
|
11257
9954
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
11258
9955
|
var PACKAGE_NAME = "numo-cli";
|
|
11259
9956
|
function getStatePath() {
|
|
11260
|
-
return
|
|
9957
|
+
return path2.join(getConfigDir(), "update-check.json");
|
|
11261
9958
|
}
|
|
11262
9959
|
function loadState() {
|
|
11263
9960
|
try {
|
|
11264
|
-
return JSON.parse(
|
|
9961
|
+
return JSON.parse(fs4.readFileSync(getStatePath(), "utf8"));
|
|
11265
9962
|
} catch {
|
|
11266
9963
|
return { lastCheck: 0 };
|
|
11267
9964
|
}
|
|
@@ -11269,7 +9966,7 @@ function loadState() {
|
|
|
11269
9966
|
function saveState(state) {
|
|
11270
9967
|
try {
|
|
11271
9968
|
ensureConfigDir();
|
|
11272
|
-
|
|
9969
|
+
fs4.writeFileSync(getStatePath(), JSON.stringify(state), { mode: 384 });
|
|
11273
9970
|
} catch {
|
|
11274
9971
|
}
|
|
11275
9972
|
}
|
|
@@ -11318,7 +10015,7 @@ function fetchLatestVersion(state) {
|
|
|
11318
10015
|
// src/cli/cli.ts
|
|
11319
10016
|
init_tty();
|
|
11320
10017
|
init_errors();
|
|
11321
|
-
var CLI_VERSION = true ? "1.
|
|
10018
|
+
var CLI_VERSION = true ? "1.3.0" : "0.0.0-dev";
|
|
11322
10019
|
var program2 = new Command();
|
|
11323
10020
|
program2.name("numo").description("CLI for Numo \u2014 programmatic access for humans and AI agents").version(CLI_VERSION).option("--json [fields]", "Output as JSON (optionally: comma-separated field names)").option("-q, --quiet", "Suppress interactive output, implies --json").hook("preAction", (thisCommand) => {
|
|
11324
10021
|
const opts = thisCommand.optsWithGlobals();
|
|
@@ -11340,7 +10037,7 @@ program2.command("login").description("Login with your Numo account").option("--
|
|
|
11340
10037
|
Examples:
|
|
11341
10038
|
$ numo login # Interactive (email/password)
|
|
11342
10039
|
$ numo login --phone # SMS OTP flow`);
|
|
11343
|
-
program2.command("register").description("Create a new Numo account").option("--email <email>", "Email address").option("--password <password>", "Password (min 6
|
|
10040
|
+
program2.command("register").description("Create a new Numo account").option("--email <email>", "Email address").option("--password <password>", "Password (min 6 chars; visible in ps/history \u2014 prefer interactive mode)").action(async (opts) => {
|
|
11344
10041
|
await register(opts);
|
|
11345
10042
|
}).addHelpText("after", `
|
|
11346
10043
|
Examples:
|
|
@@ -11348,7 +10045,6 @@ Examples:
|
|
|
11348
10045
|
$ numo register --email user@example.com --password s3cret # Non-interactive`);
|
|
11349
10046
|
program2.command("logout").description("Clear stored credentials").action(() => {
|
|
11350
10047
|
clearCredentials();
|
|
11351
|
-
clearStreaks();
|
|
11352
10048
|
console.log(import_picocolors13.default.green("Logged out."));
|
|
11353
10049
|
});
|
|
11354
10050
|
program2.command("whoami").description("Show current auth status (no API call)").action(function() {
|
|
@@ -11401,17 +10097,16 @@ program2.command("add [text...]").description("Quick-add a task (today, public,
|
|
|
11401
10097
|
dataKey: "task",
|
|
11402
10098
|
spinnerMessage: "Creating task...",
|
|
11403
10099
|
onInteractive: (_task, payload) => {
|
|
11404
|
-
const task = payload
|
|
10100
|
+
const { task, karma } = payload;
|
|
11405
10101
|
const check = import_picocolors13.default.green(SYM.check);
|
|
11406
10102
|
console.log(`
|
|
11407
10103
|
${check} Created ${task.text} ${import_picocolors13.default.dim(task.id)}`);
|
|
11408
|
-
const karma = payload.karma;
|
|
11409
10104
|
if (karma) console.log(` ${formatKarmaGain(karma)}`);
|
|
11410
10105
|
console.log("");
|
|
11411
10106
|
}
|
|
11412
10107
|
});
|
|
11413
10108
|
});
|
|
11414
|
-
|
|
10109
|
+
registerPostsCommands(program2);
|
|
11415
10110
|
registerProfileCommands(program2);
|
|
11416
10111
|
registerDoctorCommand(program2);
|
|
11417
10112
|
program2.command("commands").description("List all available commands").action(function() {
|