numo-cli 1.1.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 +392 -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,640 +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
|
-
}
|
|
6626
|
-
function routineStreakPath(uid, taskId) {
|
|
6627
|
-
return `users/${uid}/routineStreaks/${taskId}`;
|
|
6628
|
-
}
|
|
6629
|
-
function archivePath(uid, taskId) {
|
|
6630
|
-
return `archive/${uid}/tasks/${taskId}`;
|
|
6631
|
-
}
|
|
6632
|
-
function archiveCounterPath(uid) {
|
|
6633
|
-
return `archive/${uid}`;
|
|
6634
5927
|
}
|
|
6635
|
-
function
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
const
|
|
6640
|
-
|
|
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
|
-
note: body.note ?? "",
|
|
6807
|
-
priority: typeof body.priority === "number" ? body.priority : 0,
|
|
6808
|
-
difficulty: body.difficulty ?? null,
|
|
6809
|
-
duration: typeof body.duration === "number" ? body.duration : 10,
|
|
6810
|
-
backlog: dueDate == null,
|
|
6811
|
-
parentTaskId: null,
|
|
6812
|
-
completions: 0,
|
|
6813
|
-
repeat: body.repeat ?? { type: "none", every: null, end: null, endDate: null, endAfter: null, monthDays: null, weekDays: null },
|
|
6814
|
-
subtasks: Array.isArray(body.subtasks) ? body.subtasks.map((s) => ({ id: crypto3.randomUUID(), text: s.text, completed: false })) : [],
|
|
6815
|
-
withTime: dueDate != null && !/\b00:00$/.test(dueDate),
|
|
6816
|
-
listPosition: null
|
|
6817
|
-
};
|
|
6818
|
-
taskData.remindDate = getTaskRemindDate(taskData);
|
|
6819
|
-
const taskId = generateTaskId(taskData);
|
|
6820
|
-
const doc = await setDoc(taskPath(uid, taskId), taskData);
|
|
6821
|
-
const createResults = await Promise.allSettled([
|
|
6822
|
-
giveKarma(uid, "addTask", taskId, KARMA_POINTS.addTask, String(taskData.text)),
|
|
6823
|
-
addToOrdering(uid, taskId, "end"),
|
|
6824
|
-
incrementField(activityTotalsPath(uid), "tasks.active", 1)
|
|
6825
|
-
]);
|
|
6826
|
-
logSideEffectResults("createTask", createResults);
|
|
6827
|
-
return { task: doc, karma: KARMA_POINTS.addTask };
|
|
5985
|
+
return api.post("/api/tasks", body);
|
|
6828
5986
|
}
|
|
6829
5987
|
async function updateTask(uid, id, body) {
|
|
6830
|
-
|
|
6831
|
-
const allowed = ["text", "isPublic", "dueDate", "tags", "note", "priority", "difficulty", "duration", "repeat", "subtasks"];
|
|
6832
|
-
const update = {};
|
|
6833
|
-
const fieldMask = [];
|
|
6834
|
-
for (const key of allowed) {
|
|
6835
|
-
if (key in body) {
|
|
6836
|
-
update[key] = body[key];
|
|
6837
|
-
fieldMask.push(key);
|
|
6838
|
-
}
|
|
6839
|
-
}
|
|
6840
|
-
if ("dueDate" in update) {
|
|
6841
|
-
const newDueDate = update.dueDate;
|
|
6842
|
-
update.backlog = newDueDate == null;
|
|
6843
|
-
if (!fieldMask.includes("backlog")) fieldMask.push("backlog");
|
|
6844
|
-
}
|
|
6845
|
-
await updateDoc(taskPath(uid, id), update, fieldMask);
|
|
6846
|
-
if ("dueDate" in body) {
|
|
6847
|
-
await addToOrdering(uid, id, "end");
|
|
6848
|
-
}
|
|
6849
|
-
const updated = await getDoc(taskPath(uid, id));
|
|
6850
|
-
const recalculatedRemindDate = getTaskRemindDate(updated);
|
|
6851
|
-
if (updated.remindDate !== recalculatedRemindDate) {
|
|
6852
|
-
await updateDoc(taskPath(uid, id), { remindDate: recalculatedRemindDate }, ["remindDate"]);
|
|
6853
|
-
const final = await getDoc(taskPath(uid, id));
|
|
6854
|
-
return { task: final };
|
|
6855
|
-
}
|
|
6856
|
-
return { task: updated };
|
|
5988
|
+
return api.patch(`/api/tasks/${encodeURIComponent(id)}`, body);
|
|
6857
5989
|
}
|
|
6858
5990
|
async function deleteTask(uid, id) {
|
|
6859
|
-
|
|
6860
|
-
let taskData = {};
|
|
6861
|
-
try {
|
|
6862
|
-
taskData = await getDoc(taskPath(uid, id));
|
|
6863
|
-
} catch {
|
|
6864
|
-
}
|
|
6865
|
-
const hasData = Object.keys(taskData).length > 0;
|
|
6866
|
-
const writes = [
|
|
6867
|
-
{ type: "delete", path: taskPath(uid, id) }
|
|
6868
|
-
];
|
|
6869
|
-
if (hasData) {
|
|
6870
|
-
writes.push({
|
|
6871
|
-
type: "update",
|
|
6872
|
-
path: archivePath(uid, id),
|
|
6873
|
-
data: { ...taskData, archived: true, archivedAt: Date.now() }
|
|
6874
|
-
});
|
|
6875
|
-
writes.push({
|
|
6876
|
-
type: "transform",
|
|
6877
|
-
path: archiveCounterPath(uid),
|
|
6878
|
-
transforms: [{ field: "archivedTasksCount", increment: 1 }]
|
|
6879
|
-
});
|
|
6880
|
-
}
|
|
6881
|
-
await commit(writes);
|
|
6882
|
-
try {
|
|
6883
|
-
const historyDocs = await runQuery(`users/${uid}`, "tasksHistory", {
|
|
6884
|
-
where: [{ field: "parentTaskId", op: "EQUAL", value: id }],
|
|
6885
|
-
limit: MAX_TASKS_PER_REQUEST
|
|
6886
|
-
});
|
|
6887
|
-
const historyResults = await Promise.allSettled(historyDocs.map((h2) => deleteDoc(taskHistoryPath(uid, h2.id))));
|
|
6888
|
-
logSideEffectResults("deleteTask.history", historyResults);
|
|
6889
|
-
} catch (err) {
|
|
6890
|
-
process.stderr.write(`[warn] deleteTask.history: ${err instanceof Error ? err.message : String(err)}
|
|
6891
|
-
`);
|
|
6892
|
-
}
|
|
6893
|
-
const deleteResults = await Promise.allSettled([
|
|
6894
|
-
removeFromOrdering(uid, id),
|
|
6895
|
-
deleteRoutineStreak(uid, id),
|
|
6896
|
-
incrementField(activityTotalsPath(uid), "tasks.active", -1)
|
|
6897
|
-
]);
|
|
6898
|
-
logSideEffectResults("deleteTask", deleteResults);
|
|
6899
|
-
removeDailyStreak(id);
|
|
6900
|
-
return { deleted: true, taskText: String(taskData.text ?? ""), archived: hasData };
|
|
5991
|
+
return api.del(`/api/tasks/${encodeURIComponent(id)}`);
|
|
6901
5992
|
}
|
|
6902
5993
|
async function completeTask(uid, id, date) {
|
|
6903
|
-
|
|
6904
|
-
const task = await getDoc(taskPath(uid, id));
|
|
6905
|
-
const completedAt = date ? new Date(date).getTime() : Date.now();
|
|
6906
|
-
const repeating = isRepeating(task);
|
|
6907
|
-
const historyId = repeating ? `${id}-${completedAt}` : id;
|
|
6908
|
-
const historyData = {
|
|
6909
|
-
...task,
|
|
6910
|
-
id: historyId,
|
|
6911
|
-
completedAt,
|
|
6912
|
-
completed: true,
|
|
6913
|
-
parentTaskId: id,
|
|
6914
|
-
isHistoryTask: true,
|
|
6915
|
-
completions: (task.completions ?? 0) + 1,
|
|
6916
|
-
remindDate: null
|
|
6917
|
-
};
|
|
6918
|
-
delete historyData.id;
|
|
6919
|
-
const writes = [];
|
|
6920
|
-
if (repeating) {
|
|
6921
|
-
const nextDueDate = getNextDueDate(task);
|
|
6922
|
-
const resetSubtasks = (task.subtasks ?? []).map((s) => ({ ...s, completed: false }));
|
|
6923
|
-
const updatedData = {
|
|
6924
|
-
dueDate: nextDueDate,
|
|
6925
|
-
completions: (task.completions ?? 0) + 1,
|
|
6926
|
-
completedAt,
|
|
6927
|
-
subtasks: resetSubtasks
|
|
6928
|
-
};
|
|
6929
|
-
writes.push({
|
|
6930
|
-
type: "update",
|
|
6931
|
-
path: taskPath(uid, id),
|
|
6932
|
-
data: updatedData,
|
|
6933
|
-
fieldMask: Object.keys(updatedData)
|
|
6934
|
-
});
|
|
6935
|
-
} else {
|
|
6936
|
-
writes.push({ type: "delete", path: taskPath(uid, id) });
|
|
6937
|
-
}
|
|
6938
|
-
writes.push({
|
|
6939
|
-
type: "update",
|
|
6940
|
-
path: taskHistoryPath(uid, historyId),
|
|
6941
|
-
data: historyData
|
|
6942
|
-
});
|
|
6943
|
-
await commit(writes);
|
|
6944
|
-
if (repeating) {
|
|
6945
|
-
try {
|
|
6946
|
-
const afterUpdate = await getDoc(taskPath(uid, id));
|
|
6947
|
-
await updateDoc(taskPath(uid, id), { remindDate: getTaskRemindDate(afterUpdate) }, ["remindDate"]);
|
|
6948
|
-
} catch {
|
|
6949
|
-
}
|
|
6950
|
-
}
|
|
6951
|
-
const completeDateStr = date ?? formatDate(/* @__PURE__ */ new Date());
|
|
6952
|
-
const entityId = repeating ? `${id}_${completeDateStr.slice(0, 10)}` : id;
|
|
6953
|
-
const checksInRow = recordDailyStreak(id, completeDateStr);
|
|
6954
|
-
const karmaPoints = computeCompleteKarma(task, checksInRow);
|
|
6955
|
-
const sideEffects = [
|
|
6956
|
-
giveKarma(uid, "completeTask", entityId, karmaPoints, task.text),
|
|
6957
|
-
addToProgress(uid, id, completeDateStr),
|
|
6958
|
-
incrementField(activityTotalsPath(uid), "tasks.completed", 1)
|
|
6959
|
-
];
|
|
6960
|
-
if (repeating) {
|
|
6961
|
-
sideEffects.push(recordRoutineStreak(uid, id));
|
|
6962
|
-
} else {
|
|
6963
|
-
sideEffects.push(removeFromOrdering(uid, id));
|
|
6964
|
-
sideEffects.push(incrementField(activityTotalsPath(uid), "tasks.active", -1));
|
|
6965
|
-
}
|
|
6966
|
-
const completeResults = await Promise.allSettled(sideEffects);
|
|
6967
|
-
logSideEffectResults("completeTask", completeResults);
|
|
6968
|
-
return { completed: true, taskHistory: historyData, karma: karmaPoints, checksInRow, taskText: task.text };
|
|
5994
|
+
return api.post(`/api/tasks/${encodeURIComponent(id)}/complete`, date ? { date } : void 0);
|
|
6969
5995
|
}
|
|
6970
5996
|
async function uncompleteTask(uid, taskHistoryId) {
|
|
6971
|
-
|
|
6972
|
-
const history = await getDoc(taskHistoryPath(uid, taskHistoryId));
|
|
6973
|
-
const parentTaskId = history.parentTaskId;
|
|
6974
|
-
if (!parentTaskId) throw new Error("Not a history record");
|
|
6975
|
-
const repeating = isRepeating(history);
|
|
6976
|
-
const restoredTask = {
|
|
6977
|
-
...history,
|
|
6978
|
-
completed: false,
|
|
6979
|
-
completedAt: null,
|
|
6980
|
-
completions: Math.max(0, (history.completions ?? 0) - 1)
|
|
6981
|
-
};
|
|
6982
|
-
delete restoredTask.isHistoryTask;
|
|
6983
|
-
delete restoredTask.id;
|
|
6984
|
-
restoredTask.parentTaskId = null;
|
|
6985
|
-
if (repeating) {
|
|
6986
|
-
restoredTask.dueDate = history.dueDate;
|
|
6987
|
-
}
|
|
6988
|
-
const writes = [
|
|
6989
|
-
{ type: "update", path: taskPath(uid, parentTaskId), data: restoredTask },
|
|
6990
|
-
{ type: "delete", path: taskHistoryPath(uid, taskHistoryId) }
|
|
6991
|
-
];
|
|
6992
|
-
await commit(writes);
|
|
6993
|
-
try {
|
|
6994
|
-
const updated = await getDoc(taskPath(uid, parentTaskId));
|
|
6995
|
-
await updateDoc(taskPath(uid, parentTaskId), { remindDate: getTaskRemindDate(updated) }, ["remindDate"]);
|
|
6996
|
-
} catch {
|
|
6997
|
-
}
|
|
6998
|
-
const completedAtDate = history.completedAt ? formatDate(new Date(history.completedAt)) : history.dueDate ?? formatDate(/* @__PURE__ */ new Date());
|
|
6999
|
-
const entityId = repeating ? `${parentTaskId}_${completedAtDate.slice(0, 10)}` : parentTaskId;
|
|
7000
|
-
const sideEffects = [
|
|
7001
|
-
removeKarma(uid, "completeTask", entityId),
|
|
7002
|
-
removeFromProgress(uid, parentTaskId, completedAtDate),
|
|
7003
|
-
incrementField(activityTotalsPath(uid), "tasks.completed", -1)
|
|
7004
|
-
];
|
|
7005
|
-
revertDailyStreak(parentTaskId);
|
|
7006
|
-
if (repeating) {
|
|
7007
|
-
sideEffects.push(revertRoutineStreak(uid, parentTaskId));
|
|
7008
|
-
} else {
|
|
7009
|
-
sideEffects.push(addToOrdering(uid, parentTaskId, "end"));
|
|
7010
|
-
sideEffects.push(incrementField(activityTotalsPath(uid), "tasks.active", 1));
|
|
7011
|
-
}
|
|
7012
|
-
const uncompleteResults = await Promise.allSettled(sideEffects);
|
|
7013
|
-
logSideEffectResults("uncompleteTask", uncompleteResults);
|
|
7014
|
-
const final = await getDoc(taskPath(uid, parentTaskId));
|
|
7015
|
-
return { uncompleted: true, task: final, karmaReverted: true };
|
|
5997
|
+
return api.post(`/api/tasks/${encodeURIComponent(taskHistoryId)}/uncomplete`);
|
|
7016
5998
|
}
|
|
7017
5999
|
|
|
7018
6000
|
// src/cli/lib/format.ts
|
|
7019
|
-
init_define_ADMIN_UIDS();
|
|
7020
6001
|
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
7021
|
-
function
|
|
6002
|
+
function formatDate(ts) {
|
|
7022
6003
|
if (ts == null) return "";
|
|
7023
6004
|
const d = typeof ts === "number" ? new Date(ts) : new Date(ts);
|
|
7024
6005
|
if (isNaN(d.getTime())) return String(ts);
|
|
@@ -7041,7 +6022,7 @@ function formatRelativeDate(ts) {
|
|
|
7041
6022
|
if (hours < 24) return `${hours}h ago`;
|
|
7042
6023
|
const days = Math.floor(hours / 24);
|
|
7043
6024
|
if (days < 30) return `${days}d ago`;
|
|
7044
|
-
return
|
|
6025
|
+
return formatDate(ts);
|
|
7045
6026
|
}
|
|
7046
6027
|
function formatTags(tags) {
|
|
7047
6028
|
if (!Array.isArray(tags) || tags.length === 0) return "";
|
|
@@ -7119,26 +6100,7 @@ init_prompts();
|
|
|
7119
6100
|
init_tty();
|
|
7120
6101
|
init_errors();
|
|
7121
6102
|
|
|
7122
|
-
// src/cli/lib/parse-date.ts
|
|
7123
|
-
init_define_ADMIN_UIDS();
|
|
7124
|
-
|
|
7125
|
-
// node_modules/chrono-node/dist/esm/index.js
|
|
7126
|
-
init_define_ADMIN_UIDS();
|
|
7127
|
-
|
|
7128
|
-
// node_modules/chrono-node/dist/esm/locales/en/index.js
|
|
7129
|
-
init_define_ADMIN_UIDS();
|
|
7130
|
-
|
|
7131
|
-
// node_modules/chrono-node/dist/esm/chrono.js
|
|
7132
|
-
init_define_ADMIN_UIDS();
|
|
7133
|
-
|
|
7134
|
-
// node_modules/chrono-node/dist/esm/results.js
|
|
7135
|
-
init_define_ADMIN_UIDS();
|
|
7136
|
-
|
|
7137
|
-
// node_modules/chrono-node/dist/esm/utils/dates.js
|
|
7138
|
-
init_define_ADMIN_UIDS();
|
|
7139
|
-
|
|
7140
6103
|
// node_modules/chrono-node/dist/esm/types.js
|
|
7141
|
-
init_define_ADMIN_UIDS();
|
|
7142
6104
|
var Meridiem;
|
|
7143
6105
|
(function(Meridiem2) {
|
|
7144
6106
|
Meridiem2[Meridiem2["AM"] = 0] = "AM";
|
|
@@ -7197,7 +6159,6 @@ function implySimilarTime(component, target) {
|
|
|
7197
6159
|
}
|
|
7198
6160
|
|
|
7199
6161
|
// node_modules/chrono-node/dist/esm/timezone.js
|
|
7200
|
-
init_define_ADMIN_UIDS();
|
|
7201
6162
|
var TIMEZONE_ABBR_MAP = {
|
|
7202
6163
|
ACDT: 630,
|
|
7203
6164
|
ACST: 570,
|
|
@@ -7467,7 +6428,6 @@ function toTimezoneOffset(timezoneInput, date, timezoneOverrides = {}) {
|
|
|
7467
6428
|
}
|
|
7468
6429
|
|
|
7469
6430
|
// node_modules/chrono-node/dist/esm/calculation/duration.js
|
|
7470
|
-
init_define_ADMIN_UIDS();
|
|
7471
6431
|
var EmptyDuration = {
|
|
7472
6432
|
day: 0,
|
|
7473
6433
|
second: 0,
|
|
@@ -7868,17 +6828,7 @@ var ParsingResult = class _ParsingResult {
|
|
|
7868
6828
|
}
|
|
7869
6829
|
};
|
|
7870
6830
|
|
|
7871
|
-
// node_modules/chrono-node/dist/esm/locales/en/configuration.js
|
|
7872
|
-
init_define_ADMIN_UIDS();
|
|
7873
|
-
|
|
7874
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js
|
|
7875
|
-
init_define_ADMIN_UIDS();
|
|
7876
|
-
|
|
7877
|
-
// node_modules/chrono-node/dist/esm/locales/en/constants.js
|
|
7878
|
-
init_define_ADMIN_UIDS();
|
|
7879
|
-
|
|
7880
6831
|
// node_modules/chrono-node/dist/esm/utils/pattern.js
|
|
7881
|
-
init_define_ADMIN_UIDS();
|
|
7882
6832
|
function repeatedTimeunitPattern(prefix, singleTimeunitPattern, connectorPattern = "\\s{0,5},?\\s{0,5}") {
|
|
7883
6833
|
const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
|
|
7884
6834
|
return `${prefix}${singleTimeunitPatternNoCapture}(?:${connectorPattern}${singleTimeunitPatternNoCapture}){0,10}`;
|
|
@@ -7900,7 +6850,6 @@ function matchAnyPattern(dictionary) {
|
|
|
7900
6850
|
}
|
|
7901
6851
|
|
|
7902
6852
|
// node_modules/chrono-node/dist/esm/calculation/years.js
|
|
7903
|
-
init_define_ADMIN_UIDS();
|
|
7904
6853
|
function findMostLikelyADYear(yearNumber) {
|
|
7905
6854
|
if (yearNumber < 100) {
|
|
7906
6855
|
if (yearNumber > 50) {
|
|
@@ -8178,7 +7127,6 @@ function collectDateTimeFragment(fragments, match) {
|
|
|
8178
7127
|
}
|
|
8179
7128
|
|
|
8180
7129
|
// node_modules/chrono-node/dist/esm/common/parsers/AbstractParserWithWordBoundary.js
|
|
8181
|
-
init_define_ADMIN_UIDS();
|
|
8182
7130
|
var AbstractParserWithWordBoundaryChecking = class {
|
|
8183
7131
|
innerPatternHasChange(context, currentInnerPattern) {
|
|
8184
7132
|
return this.innerPattern(context) !== currentInnerPattern;
|
|
@@ -8238,7 +7186,6 @@ var ENTimeUnitWithinFormatParser = class extends AbstractParserWithWordBoundaryC
|
|
|
8238
7186
|
};
|
|
8239
7187
|
|
|
8240
7188
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameLittleEndianParser.js
|
|
8241
|
-
init_define_ADMIN_UIDS();
|
|
8242
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");
|
|
8243
7190
|
var DATE_GROUP = 1;
|
|
8244
7191
|
var DATE_TO_GROUP = 2;
|
|
@@ -8275,7 +7222,6 @@ var ENMonthNameLittleEndianParser = class extends AbstractParserWithWordBoundary
|
|
|
8275
7222
|
};
|
|
8276
7223
|
|
|
8277
7224
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameMiddleEndianParser.js
|
|
8278
|
-
init_define_ADMIN_UIDS();
|
|
8279
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");
|
|
8280
7226
|
var MONTH_NAME_GROUP2 = 1;
|
|
8281
7227
|
var DATE_GROUP2 = 2;
|
|
@@ -8325,7 +7271,6 @@ var ENMonthNameMiddleEndianParser = class extends AbstractParserWithWordBoundary
|
|
|
8325
7271
|
};
|
|
8326
7272
|
|
|
8327
7273
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENMonthNameParser.js
|
|
8328
|
-
init_define_ADMIN_UIDS();
|
|
8329
7274
|
var PATTERN3 = new RegExp(`((?:in)\\s*)?(${matchAnyPattern(MONTH_DICTIONARY)})\\s*(?:(?:,|-|of)?\\s*(${YEAR_PATTERN})?)?(?=[^\\s\\w]|\\s+[^0-9]|\\s+$|$)`, "i");
|
|
8330
7275
|
var PREFIX_GROUP = 1;
|
|
8331
7276
|
var MONTH_NAME_GROUP3 = 2;
|
|
@@ -8356,7 +7301,6 @@ var ENMonthNameParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
8356
7301
|
};
|
|
8357
7302
|
|
|
8358
7303
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENYearMonthDayParser.js
|
|
8359
|
-
init_define_ADMIN_UIDS();
|
|
8360
7304
|
var PATTERN4 = new RegExp(`([0-9]{4})[-\\.\\/\\s](?:(${matchAnyPattern(MONTH_DICTIONARY)})|([0-9]{1,2}))[-\\.\\/\\s]([0-9]{1,2})(?=\\W|$)`, "i");
|
|
8361
7305
|
var YEAR_NUMBER_GROUP = 1;
|
|
8362
7306
|
var MONTH_NAME_GROUP4 = 2;
|
|
@@ -8395,7 +7339,6 @@ var ENYearMonthDayParser = class extends AbstractParserWithWordBoundaryChecking
|
|
|
8395
7339
|
};
|
|
8396
7340
|
|
|
8397
7341
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENSlashMonthFormatParser.js
|
|
8398
|
-
init_define_ADMIN_UIDS();
|
|
8399
7342
|
var PATTERN5 = new RegExp("([0-9]|0[1-9]|1[012])/([0-9]{4})", "i");
|
|
8400
7343
|
var MONTH_GROUP = 1;
|
|
8401
7344
|
var YEAR_GROUP4 = 2;
|
|
@@ -8410,11 +7353,7 @@ var ENSlashMonthFormatParser = class extends AbstractParserWithWordBoundaryCheck
|
|
|
8410
7353
|
}
|
|
8411
7354
|
};
|
|
8412
7355
|
|
|
8413
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeExpressionParser.js
|
|
8414
|
-
init_define_ADMIN_UIDS();
|
|
8415
|
-
|
|
8416
7356
|
// node_modules/chrono-node/dist/esm/common/parsers/AbstractTimeExpressionParser.js
|
|
8417
|
-
init_define_ADMIN_UIDS();
|
|
8418
7357
|
function primaryTimePattern(leftBoundary, primaryPrefix, primarySuffix, flags) {
|
|
8419
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);
|
|
8420
7359
|
}
|
|
@@ -8770,7 +7709,6 @@ var ENTimeExpressionParser = class extends AbstractTimeExpressionParser {
|
|
|
8770
7709
|
};
|
|
8771
7710
|
|
|
8772
7711
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js
|
|
8773
|
-
init_define_ADMIN_UIDS();
|
|
8774
7712
|
var PATTERN6 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
|
|
8775
7713
|
var STRICT_PATTERN = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
|
|
8776
7714
|
var ENTimeUnitAgoFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
@@ -8792,7 +7730,6 @@ var ENTimeUnitAgoFormatParser = class extends AbstractParserWithWordBoundaryChec
|
|
|
8792
7730
|
};
|
|
8793
7731
|
|
|
8794
7732
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js
|
|
8795
|
-
init_define_ADMIN_UIDS();
|
|
8796
7733
|
var PATTERN7 = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:later|after|from now|henceforth|forward|out)(?=(?:\\W|$))`, "i");
|
|
8797
7734
|
var STRICT_PATTERN2 = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(later|after|from now)(?=\\W|$)`, "i");
|
|
8798
7735
|
var GROUP_NUM_TIMEUNITS = 1;
|
|
@@ -8814,14 +7751,7 @@ var ENTimeUnitLaterFormatParser = class extends AbstractParserWithWordBoundaryCh
|
|
|
8814
7751
|
}
|
|
8815
7752
|
};
|
|
8816
7753
|
|
|
8817
|
-
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateRangeRefiner.js
|
|
8818
|
-
init_define_ADMIN_UIDS();
|
|
8819
|
-
|
|
8820
|
-
// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js
|
|
8821
|
-
init_define_ADMIN_UIDS();
|
|
8822
|
-
|
|
8823
7754
|
// node_modules/chrono-node/dist/esm/common/abstractRefiners.js
|
|
8824
|
-
init_define_ADMIN_UIDS();
|
|
8825
7755
|
var Filter = class {
|
|
8826
7756
|
refine(context, results) {
|
|
8827
7757
|
return results.filter((r) => this.isValid(context, r));
|
|
@@ -8919,14 +7849,7 @@ var ENMergeDateRangeRefiner = class extends AbstractMergeDateRangeRefiner {
|
|
|
8919
7849
|
}
|
|
8920
7850
|
};
|
|
8921
7851
|
|
|
8922
|
-
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeDateTimeRefiner.js
|
|
8923
|
-
init_define_ADMIN_UIDS();
|
|
8924
|
-
|
|
8925
|
-
// node_modules/chrono-node/dist/esm/common/refiners/AbstractMergeDateTimeRefiner.js
|
|
8926
|
-
init_define_ADMIN_UIDS();
|
|
8927
|
-
|
|
8928
7852
|
// node_modules/chrono-node/dist/esm/calculation/mergingCalculation.js
|
|
8929
|
-
init_define_ADMIN_UIDS();
|
|
8930
7853
|
function mergeDateTimeResult(dateResult, timeResult) {
|
|
8931
7854
|
const result = dateResult.clone();
|
|
8932
7855
|
const beginDate = dateResult.start;
|
|
@@ -9011,11 +7934,7 @@ var ENMergeDateTimeRefiner = class extends AbstractMergeDateTimeRefiner {
|
|
|
9011
7934
|
}
|
|
9012
7935
|
};
|
|
9013
7936
|
|
|
9014
|
-
// node_modules/chrono-node/dist/esm/configurations.js
|
|
9015
|
-
init_define_ADMIN_UIDS();
|
|
9016
|
-
|
|
9017
7937
|
// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js
|
|
9018
|
-
init_define_ADMIN_UIDS();
|
|
9019
7938
|
var TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
|
|
9020
7939
|
var ExtractTimezoneAbbrRefiner = class {
|
|
9021
7940
|
timezoneOverrides;
|
|
@@ -9067,7 +7986,6 @@ var ExtractTimezoneAbbrRefiner = class {
|
|
|
9067
7986
|
};
|
|
9068
7987
|
|
|
9069
7988
|
// node_modules/chrono-node/dist/esm/common/refiners/ExtractTimezoneOffsetRefiner.js
|
|
9070
|
-
init_define_ADMIN_UIDS();
|
|
9071
7989
|
var TIMEZONE_OFFSET_PATTERN = new RegExp("^\\s*(?:\\(?(?:GMT|UTC)\\s?)?([+-])(\\d{1,2})(?::?(\\d{2}))?\\)?", "i");
|
|
9072
7990
|
var TIMEZONE_OFFSET_SIGN_GROUP = 1;
|
|
9073
7991
|
var TIMEZONE_OFFSET_HOUR_OFFSET_GROUP = 2;
|
|
@@ -9106,7 +8024,6 @@ var ExtractTimezoneOffsetRefiner = class {
|
|
|
9106
8024
|
};
|
|
9107
8025
|
|
|
9108
8026
|
// node_modules/chrono-node/dist/esm/common/refiners/OverlapRemovalRefiner.js
|
|
9109
|
-
init_define_ADMIN_UIDS();
|
|
9110
8027
|
var OverlapRemovalRefiner = class {
|
|
9111
8028
|
refine(context, results) {
|
|
9112
8029
|
if (results.length < 2) {
|
|
@@ -9143,7 +8060,6 @@ var OverlapRemovalRefiner = class {
|
|
|
9143
8060
|
};
|
|
9144
8061
|
|
|
9145
8062
|
// node_modules/chrono-node/dist/esm/common/refiners/ForwardDateRefiner.js
|
|
9146
|
-
init_define_ADMIN_UIDS();
|
|
9147
8063
|
var ForwardDateRefiner = class {
|
|
9148
8064
|
refine(context, results) {
|
|
9149
8065
|
if (!context.option.forwardDate) {
|
|
@@ -9209,7 +8125,6 @@ var ForwardDateRefiner = class {
|
|
|
9209
8125
|
};
|
|
9210
8126
|
|
|
9211
8127
|
// node_modules/chrono-node/dist/esm/common/refiners/UnlikelyFormatFilter.js
|
|
9212
|
-
init_define_ADMIN_UIDS();
|
|
9213
8128
|
var UnlikelyFormatFilter = class extends Filter {
|
|
9214
8129
|
strictMode;
|
|
9215
8130
|
constructor(strictMode) {
|
|
@@ -9252,7 +8167,6 @@ var UnlikelyFormatFilter = class extends Filter {
|
|
|
9252
8167
|
};
|
|
9253
8168
|
|
|
9254
8169
|
// node_modules/chrono-node/dist/esm/common/parsers/ISOFormatParser.js
|
|
9255
|
-
init_define_ADMIN_UIDS();
|
|
9256
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");
|
|
9257
8171
|
var YEAR_NUMBER_GROUP2 = 1;
|
|
9258
8172
|
var MONTH_NUMBER_GROUP2 = 2;
|
|
@@ -9306,7 +8220,6 @@ var ISOFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9306
8220
|
};
|
|
9307
8221
|
|
|
9308
8222
|
// node_modules/chrono-node/dist/esm/common/refiners/MergeWeekdayComponentRefiner.js
|
|
9309
|
-
init_define_ADMIN_UIDS();
|
|
9310
8223
|
var MergeWeekdayComponentRefiner = class extends MergingRefiner {
|
|
9311
8224
|
mergeResults(textBetween, currentResult, nextResult) {
|
|
9312
8225
|
const newResult = nextResult.clone();
|
|
@@ -9337,11 +8250,7 @@ function includeCommonConfiguration(configuration2, strictMode = false) {
|
|
|
9337
8250
|
return configuration2;
|
|
9338
8251
|
}
|
|
9339
8252
|
|
|
9340
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualDateParser.js
|
|
9341
|
-
init_define_ADMIN_UIDS();
|
|
9342
|
-
|
|
9343
8253
|
// node_modules/chrono-node/dist/esm/common/casualReferences.js
|
|
9344
|
-
init_define_ADMIN_UIDS();
|
|
9345
8254
|
function now(reference) {
|
|
9346
8255
|
const targetDate = reference.getDateWithAdjustedTimezone();
|
|
9347
8256
|
const component = new ParsingComponents(reference, {});
|
|
@@ -9487,7 +8396,6 @@ var ENCasualDateParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9487
8396
|
};
|
|
9488
8397
|
|
|
9489
8398
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENCasualTimeParser.js
|
|
9490
|
-
init_define_ADMIN_UIDS();
|
|
9491
8399
|
var PATTERN10 = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|midday|noon)(?=\W|$)/i;
|
|
9492
8400
|
var ENCasualTimeParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
9493
8401
|
innerPattern() {
|
|
@@ -9521,11 +8429,7 @@ var ENCasualTimeParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9521
8429
|
}
|
|
9522
8430
|
};
|
|
9523
8431
|
|
|
9524
|
-
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENWeekdayParser.js
|
|
9525
|
-
init_define_ADMIN_UIDS();
|
|
9526
|
-
|
|
9527
8432
|
// node_modules/chrono-node/dist/esm/calculation/weekdays.js
|
|
9528
|
-
init_define_ADMIN_UIDS();
|
|
9529
8433
|
function createParsingComponentsAtWeekday(reference, weekday, modifier) {
|
|
9530
8434
|
const refDate = reference.getDateWithAdjustedTimezone();
|
|
9531
8435
|
const daysToWeekday = getDaysToWeekday(refDate, weekday, modifier);
|
|
@@ -9628,7 +8532,6 @@ var ENWeekdayParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
|
9628
8532
|
};
|
|
9629
8533
|
|
|
9630
8534
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENRelativeDateFormatParser.js
|
|
9631
|
-
init_define_ADMIN_UIDS();
|
|
9632
8535
|
var PATTERN12 = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)(?=\\W|$)`, "i");
|
|
9633
8536
|
var MODIFIER_WORD_GROUP = 1;
|
|
9634
8537
|
var RELATIVE_WORD_GROUP = 2;
|
|
@@ -9674,7 +8577,6 @@ var ENRelativeDateFormatParser = class extends AbstractParserWithWordBoundaryChe
|
|
|
9674
8577
|
};
|
|
9675
8578
|
|
|
9676
8579
|
// node_modules/chrono-node/dist/esm/common/parsers/SlashDateFormatParser.js
|
|
9677
|
-
init_define_ADMIN_UIDS();
|
|
9678
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");
|
|
9679
8581
|
var OPENING_GROUP = 1;
|
|
9680
8582
|
var ENDING_GROUP = 5;
|
|
@@ -9743,7 +8645,6 @@ var SlashDateFormatParser = class {
|
|
|
9743
8645
|
};
|
|
9744
8646
|
|
|
9745
8647
|
// node_modules/chrono-node/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js
|
|
9746
|
-
init_define_ADMIN_UIDS();
|
|
9747
8648
|
var PATTERN14 = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
9748
8649
|
var PATTERN_NO_ABBR = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`, "i");
|
|
9749
8650
|
var ENTimeUnitCasualRelativeFormatParser = class extends AbstractParserWithWordBoundaryChecking {
|
|
@@ -9773,7 +8674,6 @@ var ENTimeUnitCasualRelativeFormatParser = class extends AbstractParserWithWordB
|
|
|
9773
8674
|
};
|
|
9774
8675
|
|
|
9775
8676
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeAfterDateRefiner.js
|
|
9776
|
-
init_define_ADMIN_UIDS();
|
|
9777
8677
|
function IsPositiveFollowingReference(result) {
|
|
9778
8678
|
return result.text.match(/^[+-]/i) != null;
|
|
9779
8679
|
}
|
|
@@ -9798,7 +8698,6 @@ var ENMergeRelativeAfterDateRefiner = class extends MergingRefiner {
|
|
|
9798
8698
|
};
|
|
9799
8699
|
|
|
9800
8700
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENMergeRelativeFollowByDateRefiner.js
|
|
9801
|
-
init_define_ADMIN_UIDS();
|
|
9802
8701
|
function hasImpliedEarlierReferenceDate(result) {
|
|
9803
8702
|
return result.text.match(/\s+(before|from)$/i) != null;
|
|
9804
8703
|
}
|
|
@@ -9829,7 +8728,6 @@ var ENMergeRelativeFollowByDateRefiner = class extends MergingRefiner {
|
|
|
9829
8728
|
};
|
|
9830
8729
|
|
|
9831
8730
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENExtractYearSuffixRefiner.js
|
|
9832
|
-
init_define_ADMIN_UIDS();
|
|
9833
8731
|
var YEAR_SUFFIX_PATTERN = new RegExp(`^\\s*(${YEAR_PATTERN})`, "i");
|
|
9834
8732
|
var YEAR_GROUP6 = 1;
|
|
9835
8733
|
var ENExtractYearSuffixRefiner = class {
|
|
@@ -9861,7 +8759,6 @@ var ENExtractYearSuffixRefiner = class {
|
|
|
9861
8759
|
};
|
|
9862
8760
|
|
|
9863
8761
|
// node_modules/chrono-node/dist/esm/locales/en/refiners/ENUnlikelyFormatFilter.js
|
|
9864
|
-
init_define_ADMIN_UIDS();
|
|
9865
8762
|
var ENUnlikelyFormatFilter = class extends Filter {
|
|
9866
8763
|
constructor() {
|
|
9867
8764
|
super();
|
|
@@ -10043,7 +8940,7 @@ var GB = new Chrono(configuration.createCasualConfiguration(true));
|
|
|
10043
8940
|
|
|
10044
8941
|
// node_modules/chrono-node/dist/esm/index.js
|
|
10045
8942
|
var casual2 = casual;
|
|
10046
|
-
function
|
|
8943
|
+
function parseDate(text, ref, option) {
|
|
10047
8944
|
return casual2.parseDate(text, ref, option);
|
|
10048
8945
|
}
|
|
10049
8946
|
|
|
@@ -10052,7 +8949,7 @@ function parseHumanDate(input) {
|
|
|
10052
8949
|
const trimmed = input.trim();
|
|
10053
8950
|
if (!trimmed) return null;
|
|
10054
8951
|
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) return trimmed;
|
|
10055
|
-
const parsed =
|
|
8952
|
+
const parsed = parseDate(trimmed, /* @__PURE__ */ new Date(), { forwardDate: true });
|
|
10056
8953
|
if (!parsed) return null;
|
|
10057
8954
|
const date = parsed.toISOString().slice(0, 10);
|
|
10058
8955
|
const hours = parsed.getHours();
|
|
@@ -10070,10 +8967,9 @@ function parseHumanDateOnly(input) {
|
|
|
10070
8967
|
}
|
|
10071
8968
|
|
|
10072
8969
|
// src/cli/lib/stdin.ts
|
|
10073
|
-
|
|
10074
|
-
var fs4 = __toESM(require("fs"), 1);
|
|
8970
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
10075
8971
|
function readStdinLines() {
|
|
10076
|
-
const input =
|
|
8972
|
+
const input = fs3.readFileSync(0, "utf8");
|
|
10077
8973
|
return input.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
10078
8974
|
}
|
|
10079
8975
|
|
|
@@ -10093,7 +8989,7 @@ async function pickTask(uid, id, actionName) {
|
|
|
10093
8989
|
message: `Select task to ${actionName}`,
|
|
10094
8990
|
options: pending.map((t2) => ({
|
|
10095
8991
|
value: t2.id,
|
|
10096
|
-
label: `${truncate(
|
|
8992
|
+
label: `${truncate(t2.text, 50)} ${import_picocolors8.default.dim(t2.id)}`
|
|
10097
8993
|
}))
|
|
10098
8994
|
});
|
|
10099
8995
|
return selected;
|
|
@@ -10118,13 +9014,12 @@ function extractTime(dueDate) {
|
|
|
10118
9014
|
const time = parts[1];
|
|
10119
9015
|
return time === "00:00" ? "" : time;
|
|
10120
9016
|
}
|
|
10121
|
-
function
|
|
10122
|
-
|
|
10123
|
-
return !!repeat?.type && repeat.type !== "none";
|
|
9017
|
+
function isRepeating(t2) {
|
|
9018
|
+
return !!t2.repeat?.type && t2.repeat.type !== "none";
|
|
10124
9019
|
}
|
|
10125
9020
|
function getCheckIndicator(t2) {
|
|
10126
9021
|
if (t2.completed) return import_picocolors8.default.green(SYM.check);
|
|
10127
|
-
if (
|
|
9022
|
+
if (isRepeating(t2)) return import_picocolors8.default.blue(SYM.repeat);
|
|
10128
9023
|
return import_picocolors8.default.dim(SYM.circle);
|
|
10129
9024
|
}
|
|
10130
9025
|
function sortTasksForDisplay(tasks) {
|
|
@@ -10132,16 +9027,16 @@ function sortTasksForDisplay(tasks) {
|
|
|
10132
9027
|
return [...tasks].sort((a, b) => {
|
|
10133
9028
|
const timeA = extractTime(a.dueDate);
|
|
10134
9029
|
const timeB = extractTime(b.dueDate);
|
|
10135
|
-
const repA =
|
|
10136
|
-
const repB =
|
|
9030
|
+
const repA = isRepeating(a);
|
|
9031
|
+
const repB = isRepeating(b);
|
|
10137
9032
|
if (timeA && !timeB) return -1;
|
|
10138
9033
|
if (!timeA && timeB) return 1;
|
|
10139
9034
|
if (timeA && timeB) return timeA.localeCompare(timeB);
|
|
10140
9035
|
if (repA && !repB) return -1;
|
|
10141
9036
|
if (!repA && repB) return 1;
|
|
10142
9037
|
if (repA && repB) {
|
|
10143
|
-
const ra = a.repeat
|
|
10144
|
-
const rb = b.repeat
|
|
9038
|
+
const ra = a.repeat.type ?? "";
|
|
9039
|
+
const rb = b.repeat.type ?? "";
|
|
10145
9040
|
return (repeatOrder[ra] ?? 99) - (repeatOrder[rb] ?? 99);
|
|
10146
9041
|
}
|
|
10147
9042
|
return 0;
|
|
@@ -10153,7 +9048,7 @@ function printTaskDetail(t2) {
|
|
|
10153
9048
|
printRecord([
|
|
10154
9049
|
["ID", dim(t2.id)],
|
|
10155
9050
|
["Text", t2.text],
|
|
10156
|
-
["Due",
|
|
9051
|
+
["Due", formatDate(t2.dueDate) || dim("none (backlog)")],
|
|
10157
9052
|
["Status", t2.completed ? import_picocolors8.default.green("completed") : import_picocolors8.default.yellow("pending")],
|
|
10158
9053
|
["Tags", formatTags(t2.tags) || dim("none")],
|
|
10159
9054
|
["Difficulty", formatDifficulty(t2.difficulty) || dim("not set")],
|
|
@@ -10162,18 +9057,18 @@ function printTaskDetail(t2) {
|
|
|
10162
9057
|
["Note", t2.note || dim("none")],
|
|
10163
9058
|
["Public", t2.isPublic ? import_picocolors8.default.green("yes") : import_picocolors8.default.yellow("no")],
|
|
10164
9059
|
["Completions", String(t2.completions ?? 0)],
|
|
10165
|
-
["Created",
|
|
9060
|
+
["Created", formatDate(t2.createdAt)]
|
|
10166
9061
|
]);
|
|
10167
9062
|
console.log("");
|
|
10168
9063
|
}
|
|
10169
9064
|
function printTaskLine(t2) {
|
|
10170
9065
|
const check = getCheckIndicator(t2);
|
|
10171
|
-
const rawText = truncate(
|
|
9066
|
+
const rawText = truncate(t2.text, 50);
|
|
10172
9067
|
const text = t2.completed ? import_picocolors8.default.strikethrough(import_picocolors8.default.dim(rawText)) : rawText;
|
|
10173
9068
|
const time = extractTime(t2.dueDate);
|
|
10174
9069
|
const tags = formatTags(t2.tags);
|
|
10175
9070
|
const difficulty = formatDifficulty(t2.difficulty);
|
|
10176
|
-
const id = import_picocolors8.default.dim(
|
|
9071
|
+
const id = import_picocolors8.default.dim(t2.id);
|
|
10177
9072
|
const parts = [check, text];
|
|
10178
9073
|
if (time) parts.push(import_picocolors8.default.cyan(time));
|
|
10179
9074
|
if (tags) parts.push(tags);
|
|
@@ -10202,8 +9097,7 @@ function registerTasksCommands(program3) {
|
|
|
10202
9097
|
console.log(` ${import_picocolors8.default.bold("Backlog")} ${import_picocolors8.default.dim(`(${items.length})`)}`);
|
|
10203
9098
|
} else {
|
|
10204
9099
|
const viewDate = date ? /* @__PURE__ */ new Date(date + "T00:00:00") : /* @__PURE__ */ new Date();
|
|
10205
|
-
|
|
10206
|
-
console.log(formatWeekdayHeader(viewDate, streakCount));
|
|
9100
|
+
console.log(formatWeekdayHeader(viewDate, completed.length));
|
|
10207
9101
|
}
|
|
10208
9102
|
const tagLine = formatTagsSummary(items);
|
|
10209
9103
|
if (tagLine) console.log(`
|
|
@@ -10268,7 +9162,7 @@ Examples:
|
|
|
10268
9162
|
Examples:
|
|
10269
9163
|
$ numo tasks get abc123
|
|
10270
9164
|
$ numo tasks get abc123 --json | jq '.text'`);
|
|
10271
|
-
tasks.command("create").description("Create a new task").option("--text <text>", "Task text").option("--due <date>", "Due date YYYY-MM-DD").option("--tags <tags>", "Comma-separated tags").option("--private", "Make task private").option("--note <note>", "Task note").option("--priority <n>", "Priority 0.1\u20131.0").option("--difficulty <n>", "Difficulty 0\u20133").option("--duration <n>", "Duration in minutes").action(async function() {
|
|
9165
|
+
tasks.command("create").description("Create a new task").option("--text <text>", "Task text").option("--due <date>", "Due date YYYY-MM-DD").option("--tags <tags>", "Comma-separated tags").option("--public", "Make task public (default)").option("--private", "Make task private").option("--note <note>", "Task note").option("--priority <n>", "Priority 0.1\u20131.0").option("--difficulty <n>", "Difficulty 0\u20133").option("--duration <n>", "Duration in minutes").action(async function() {
|
|
10272
9166
|
const opts = this.optsWithGlobals();
|
|
10273
9167
|
const uid = requireUid();
|
|
10274
9168
|
const text = await promptForMissing({ value: opts.text, message: "Task text", placeholder: "What do you need to do?" });
|
|
@@ -10331,6 +9225,16 @@ Examples:
|
|
|
10331
9225
|
body.dueDate = fmt(today2);
|
|
10332
9226
|
}
|
|
10333
9227
|
}
|
|
9228
|
+
if (!opts.private && !opts.public) {
|
|
9229
|
+
const visibility = await promptSelect({
|
|
9230
|
+
message: "Visibility",
|
|
9231
|
+
options: [
|
|
9232
|
+
{ value: "public", label: "Public \u2014 visible to your squad" },
|
|
9233
|
+
{ value: "private", label: "Private \u2014 only you can see it" }
|
|
9234
|
+
]
|
|
9235
|
+
});
|
|
9236
|
+
if (visibility === "private") body.isPublic = false;
|
|
9237
|
+
}
|
|
10334
9238
|
const addDetails = await promptConfirm({
|
|
10335
9239
|
message: "Add details? (tags, effort, time, note)",
|
|
10336
9240
|
initialValue: false
|
|
@@ -10376,6 +9280,7 @@ Examples:
|
|
|
10376
9280
|
if (note) body.note = note;
|
|
10377
9281
|
}
|
|
10378
9282
|
}
|
|
9283
|
+
if (opts.public) body.isPublic = true;
|
|
10379
9284
|
if (opts.private) body.isPublic = false;
|
|
10380
9285
|
if (opts.tags && !body.tags) body.tags = opts.tags.split(",");
|
|
10381
9286
|
if (opts.note && !body.note) body.note = opts.note;
|
|
@@ -10390,6 +9295,7 @@ Examples:
|
|
|
10390
9295
|
if (opts.priority) body.priority = parseFloat(opts.priority);
|
|
10391
9296
|
if (opts.difficulty !== void 0) body.difficulty = parseInt(opts.difficulty);
|
|
10392
9297
|
if (opts.duration) body.duration = parseInt(opts.duration);
|
|
9298
|
+
if (opts.public) body.isPublic = true;
|
|
10393
9299
|
if (opts.private) body.isPublic = false;
|
|
10394
9300
|
}
|
|
10395
9301
|
await runCreate({
|
|
@@ -10398,11 +9304,10 @@ Examples:
|
|
|
10398
9304
|
dataKey: "task",
|
|
10399
9305
|
spinnerMessage: "Creating task...",
|
|
10400
9306
|
onInteractive: (_task, payload) => {
|
|
10401
|
-
const task = payload
|
|
9307
|
+
const { task, karma } = payload;
|
|
10402
9308
|
const check = import_picocolors8.default.green(SYM.check);
|
|
10403
9309
|
console.log(`
|
|
10404
9310
|
${check} Created ${task.text}`);
|
|
10405
|
-
const karma = payload.karma;
|
|
10406
9311
|
if (karma) {
|
|
10407
9312
|
console.log(` ${formatKarmaGain(karma)}${" ".repeat(20)}${import_picocolors8.default.dim(task.id)}`);
|
|
10408
9313
|
}
|
|
@@ -10457,9 +9362,9 @@ Examples:
|
|
|
10457
9362
|
fn: () => updateTask(uid, taskId, body),
|
|
10458
9363
|
dataKey: "task",
|
|
10459
9364
|
spinnerMessage: "Updating task...",
|
|
10460
|
-
onInteractive: (
|
|
9365
|
+
onInteractive: (payload) => {
|
|
10461
9366
|
console.log(`
|
|
10462
|
-
${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)}
|
|
10463
9368
|
`);
|
|
10464
9369
|
}
|
|
10465
9370
|
});
|
|
@@ -10489,7 +9394,7 @@ Examples:
|
|
|
10489
9394
|
let taskText = taskId;
|
|
10490
9395
|
try {
|
|
10491
9396
|
const task = await getTask(uid, taskId);
|
|
10492
|
-
taskText =
|
|
9397
|
+
taskText = task.text ?? taskId;
|
|
10493
9398
|
} catch {
|
|
10494
9399
|
}
|
|
10495
9400
|
const confirmed = await promptConfirm({
|
|
@@ -10507,9 +9412,8 @@ Examples:
|
|
|
10507
9412
|
spinnerMessage: "Deleting task...",
|
|
10508
9413
|
onInteractive: (data) => {
|
|
10509
9414
|
const cross = import_picocolors8.default.red(SYM.cross);
|
|
10510
|
-
const text = data.taskText || taskId;
|
|
10511
9415
|
console.log(`
|
|
10512
|
-
${cross} Deleted ${
|
|
9416
|
+
${cross} Deleted ${data.taskText || taskId}`);
|
|
10513
9417
|
if (data.archived) console.log(` ${import_picocolors8.default.dim("Archived")}`);
|
|
10514
9418
|
console.log("");
|
|
10515
9419
|
}
|
|
@@ -10541,13 +9445,10 @@ Examples:
|
|
|
10541
9445
|
spinnerMessage: "Completing task...",
|
|
10542
9446
|
onInteractive: (data) => {
|
|
10543
9447
|
const check = import_picocolors8.default.green(SYM.check);
|
|
10544
|
-
const text = data.taskText ?? taskId;
|
|
10545
9448
|
console.log(`
|
|
10546
|
-
${check} Done! ${
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
if (karma) {
|
|
10550
|
-
console.log(` ${formatKarmaGain(karma, checksInRow)}`);
|
|
9449
|
+
${check} Done! ${data.taskText ?? taskId}`);
|
|
9450
|
+
if (data.karma) {
|
|
9451
|
+
console.log(` ${formatKarmaGain(data.karma, data.checksInRow)}`);
|
|
10551
9452
|
}
|
|
10552
9453
|
console.log("");
|
|
10553
9454
|
}
|
|
@@ -10579,9 +9480,8 @@ Examples:
|
|
|
10579
9480
|
spinnerMessage: "Uncompleting task...",
|
|
10580
9481
|
onInteractive: (data) => {
|
|
10581
9482
|
const arrow = SYM.undo;
|
|
10582
|
-
const text = data.text ?? taskId;
|
|
10583
9483
|
console.log(`
|
|
10584
|
-
${import_picocolors8.default.yellow(arrow)} Reverted ${text}`);
|
|
9484
|
+
${import_picocolors8.default.yellow(arrow)} Reverted ${data.task.text ?? taskId}`);
|
|
10585
9485
|
console.log(` ${import_picocolors8.default.dim("Karma adjustment applied")}`);
|
|
10586
9486
|
console.log("");
|
|
10587
9487
|
}
|
|
@@ -10592,13 +9492,10 @@ Examples:
|
|
|
10592
9492
|
}
|
|
10593
9493
|
|
|
10594
9494
|
// src/cli/commands/posts.ts
|
|
10595
|
-
init_define_ADMIN_UIDS();
|
|
10596
9495
|
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
10597
9496
|
|
|
10598
9497
|
// src/cli/lib/pagination.ts
|
|
10599
|
-
init_define_ADMIN_UIDS();
|
|
10600
9498
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
10601
|
-
init_errors();
|
|
10602
9499
|
function printPaginationHint(opts) {
|
|
10603
9500
|
if (!opts.nextCursor) return;
|
|
10604
9501
|
const parts = [opts.command, `--cursor ${opts.nextCursor}`];
|
|
@@ -10608,223 +9505,56 @@ ${import_picocolors9.default.dim("Next page:")} ${import_picocolors9.default.dim
|
|
|
10608
9505
|
}
|
|
10609
9506
|
|
|
10610
9507
|
// src/cli/services/posts.ts
|
|
10611
|
-
init_define_ADMIN_UIDS();
|
|
10612
|
-
function generateSlug(title) {
|
|
10613
|
-
return title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 80) + "-" + Date.now().toString(36);
|
|
10614
|
-
}
|
|
10615
9508
|
async function listPosts(opts) {
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
};
|
|
10621
|
-
if (opts.cursor) {
|
|
10622
|
-
const cursorDoc = await getDoc(`posts/${opts.cursor}`);
|
|
10623
|
-
if (cursorDoc.createdAt != null) {
|
|
10624
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10625
|
-
}
|
|
10626
|
-
}
|
|
10627
|
-
const docs = await runQuery("", "posts", queryOpts);
|
|
10628
|
-
const hasMore = docs.length > lim;
|
|
10629
|
-
const posts = docs.slice(0, lim);
|
|
10630
|
-
return {
|
|
10631
|
-
posts,
|
|
10632
|
-
nextCursor: hasMore ? posts[posts.length - 1].id : void 0
|
|
10633
|
-
};
|
|
9509
|
+
return api.get("/api/posts", {
|
|
9510
|
+
cursor: opts.cursor,
|
|
9511
|
+
limit: opts.limit?.toString()
|
|
9512
|
+
});
|
|
10634
9513
|
}
|
|
10635
9514
|
async function getPost(id) {
|
|
10636
|
-
|
|
10637
|
-
const post = await getDoc(`posts/${id}`);
|
|
10638
|
-
if (post.authorId) {
|
|
10639
|
-
try {
|
|
10640
|
-
const author = await getDoc(`users/${post.authorId}`);
|
|
10641
|
-
post.authorName = author.username ?? null;
|
|
10642
|
-
} catch {
|
|
10643
|
-
}
|
|
10644
|
-
}
|
|
10645
|
-
return post;
|
|
9515
|
+
return api.get(`/api/posts/${encodeURIComponent(id)}`);
|
|
10646
9516
|
}
|
|
10647
9517
|
async function createPost(uid, body) {
|
|
10648
|
-
|
|
10649
|
-
const postData = {
|
|
10650
|
-
title: body.title,
|
|
10651
|
-
body: body.body,
|
|
10652
|
-
tag: body.tag,
|
|
10653
|
-
authorId: uid,
|
|
10654
|
-
slug: generateSlug(body.title),
|
|
10655
|
-
isPublic: body.isPublic !== false,
|
|
10656
|
-
createdAt: now2,
|
|
10657
|
-
updatedAt: now2,
|
|
10658
|
-
commentsCount: 0,
|
|
10659
|
-
likesCount: 0
|
|
10660
|
-
};
|
|
10661
|
-
const doc = await createDoc("posts", postData);
|
|
10662
|
-
const postId = doc.id;
|
|
10663
|
-
try {
|
|
10664
|
-
await giveKarma(uid, "createPost", postId, KARMA_POINTS.createPost, String(body.title));
|
|
10665
|
-
} catch {
|
|
10666
|
-
}
|
|
10667
|
-
try {
|
|
10668
|
-
await incrementField(`users/${uid}/activity/totals`, "posts.written", 1);
|
|
10669
|
-
} catch {
|
|
10670
|
-
}
|
|
10671
|
-
return { post: doc, karma: KARMA_POINTS.createPost };
|
|
9518
|
+
return api.post("/api/posts", body);
|
|
10672
9519
|
}
|
|
10673
9520
|
async function updatePost(uid, id, body) {
|
|
10674
|
-
|
|
10675
|
-
const post = await getDoc(`posts/${id}`);
|
|
10676
|
-
checkOwnership(post, uid, "update");
|
|
10677
|
-
const allowed = ["title", "body", "tag", "isPublic"];
|
|
10678
|
-
const update = { updatedAt: Date.now() };
|
|
10679
|
-
const fieldMask = ["updatedAt"];
|
|
10680
|
-
for (const key of allowed) {
|
|
10681
|
-
if (key in body) {
|
|
10682
|
-
update[key] = body[key];
|
|
10683
|
-
fieldMask.push(key);
|
|
10684
|
-
}
|
|
10685
|
-
}
|
|
10686
|
-
await updateDoc(`posts/${id}`, update, fieldMask);
|
|
10687
|
-
const updated = await getDoc(`posts/${id}`);
|
|
10688
|
-
return { post: updated };
|
|
9521
|
+
return api.patch(`/api/posts/${encodeURIComponent(id)}`, body);
|
|
10689
9522
|
}
|
|
10690
9523
|
async function deletePost(uid, id) {
|
|
10691
|
-
|
|
10692
|
-
const post = await getDoc(`posts/${id}`);
|
|
10693
|
-
checkOwnership(post, uid, "delete");
|
|
10694
|
-
await deleteDoc(`posts/${id}`);
|
|
9524
|
+
await api.del(`/api/posts/${encodeURIComponent(id)}`);
|
|
10695
9525
|
}
|
|
10696
9526
|
|
|
10697
9527
|
// src/cli/services/comments.ts
|
|
10698
|
-
init_define_ADMIN_UIDS();
|
|
10699
9528
|
async function listComments(postId, opts) {
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
limit: lim + 1
|
|
10705
|
-
};
|
|
10706
|
-
if (opts.cursor) {
|
|
10707
|
-
const cursorDoc = await getDoc(`posts/${postId}/comments/${opts.cursor}`);
|
|
10708
|
-
if (cursorDoc.createdAt != null) {
|
|
10709
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10710
|
-
}
|
|
10711
|
-
}
|
|
10712
|
-
const docs = await runQuery(`posts/${postId}`, "comments", queryOpts);
|
|
10713
|
-
const hasMore = docs.length > lim;
|
|
10714
|
-
const comments = docs.slice(0, lim);
|
|
10715
|
-
const uids = [...new Set(comments.map((c) => c.userId).filter(Boolean))];
|
|
10716
|
-
const userMap = {};
|
|
10717
|
-
await Promise.all(uids.map(async (uid) => {
|
|
10718
|
-
try {
|
|
10719
|
-
const user = await getDoc(`users/${uid}`);
|
|
10720
|
-
if (user.username) userMap[uid] = user.username;
|
|
10721
|
-
} catch {
|
|
10722
|
-
}
|
|
10723
|
-
}));
|
|
10724
|
-
for (const c of comments) {
|
|
10725
|
-
c.authorName = userMap[c.userId] ?? null;
|
|
10726
|
-
}
|
|
10727
|
-
return {
|
|
10728
|
-
comments,
|
|
10729
|
-
nextCursor: hasMore ? comments[comments.length - 1].id : void 0
|
|
10730
|
-
};
|
|
9529
|
+
return api.get(`/api/posts/${encodeURIComponent(postId)}/comments`, {
|
|
9530
|
+
cursor: opts.cursor,
|
|
9531
|
+
limit: opts.limit?.toString()
|
|
9532
|
+
});
|
|
10731
9533
|
}
|
|
10732
9534
|
async function createComment(uid, postId, text) {
|
|
10733
|
-
|
|
10734
|
-
const now2 = Date.now();
|
|
10735
|
-
const commentData = {
|
|
10736
|
-
postId,
|
|
10737
|
-
userId: uid,
|
|
10738
|
-
text,
|
|
10739
|
-
createdAt: now2,
|
|
10740
|
-
updatedAt: now2,
|
|
10741
|
-
textLength: text.length,
|
|
10742
|
-
likes: [],
|
|
10743
|
-
repliesCount: 0
|
|
10744
|
-
};
|
|
10745
|
-
const doc = await createDoc(`posts/${postId}/comments`, commentData);
|
|
10746
|
-
const commentId = doc.id;
|
|
10747
|
-
await incrementField(`posts/${postId}`, "commentsCount", 1);
|
|
10748
|
-
try {
|
|
10749
|
-
await giveKarma(uid, "addComment", `${postId}_${commentId}`, KARMA_POINTS.addComment, text);
|
|
10750
|
-
} catch {
|
|
10751
|
-
}
|
|
10752
|
-
try {
|
|
10753
|
-
await incrementField(`users/${uid}/activity/totals`, "comments.written", 1);
|
|
10754
|
-
} catch {
|
|
10755
|
-
}
|
|
10756
|
-
return { comment: doc, karma: KARMA_POINTS.addComment };
|
|
9535
|
+
return api.post(`/api/posts/${encodeURIComponent(postId)}/comments`, { text });
|
|
10757
9536
|
}
|
|
10758
9537
|
async function deleteComment(uid, postId, commentId) {
|
|
10759
|
-
|
|
10760
|
-
validateDocId(commentId, "Comment ID");
|
|
10761
|
-
const comment = await getDoc(`posts/${postId}/comments/${commentId}`);
|
|
10762
|
-
checkOwnership(comment, uid, "delete");
|
|
10763
|
-
await deleteDoc(`posts/${postId}/comments/${commentId}`);
|
|
10764
|
-
await incrementField(`posts/${postId}`, "commentsCount", -1);
|
|
9538
|
+
await api.del(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}`);
|
|
10765
9539
|
}
|
|
10766
9540
|
|
|
10767
9541
|
// src/cli/services/replies.ts
|
|
10768
|
-
init_define_ADMIN_UIDS();
|
|
10769
9542
|
async function listReplies(postId, commentId, opts) {
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
orderBy: [{ field: "createdAt", direction: "ASCENDING" }],
|
|
10775
|
-
limit: lim + 1
|
|
10776
|
-
};
|
|
10777
|
-
if (opts.cursor) {
|
|
10778
|
-
const cursorDoc = await getDoc(`posts/${postId}/comments/${commentId}/replies/${opts.cursor}`);
|
|
10779
|
-
if (cursorDoc.createdAt != null) {
|
|
10780
|
-
queryOpts.startAfter = [cursorDoc.createdAt];
|
|
10781
|
-
}
|
|
10782
|
-
}
|
|
10783
|
-
const docs = await runQuery(`posts/${postId}/comments/${commentId}`, "replies", queryOpts);
|
|
10784
|
-
const hasMore = docs.length > lim;
|
|
10785
|
-
const replies = docs.slice(0, lim);
|
|
10786
|
-
return {
|
|
10787
|
-
replies,
|
|
10788
|
-
nextCursor: hasMore ? replies[replies.length - 1].id : void 0
|
|
10789
|
-
};
|
|
9543
|
+
return api.get(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies`, {
|
|
9544
|
+
cursor: opts.cursor,
|
|
9545
|
+
limit: opts.limit?.toString()
|
|
9546
|
+
});
|
|
10790
9547
|
}
|
|
10791
9548
|
async function createReply(uid, postId, commentId, text) {
|
|
10792
|
-
|
|
10793
|
-
validateDocId(commentId, "Comment ID");
|
|
10794
|
-
const now2 = Date.now();
|
|
10795
|
-
const replyData = {
|
|
10796
|
-
postId,
|
|
10797
|
-
userId: uid,
|
|
10798
|
-
text,
|
|
10799
|
-
createdAt: now2,
|
|
10800
|
-
updatedAt: now2,
|
|
10801
|
-
textLength: text.length,
|
|
10802
|
-
likes: [],
|
|
10803
|
-
parentCommentId: commentId
|
|
10804
|
-
};
|
|
10805
|
-
const doc = await createDoc(`posts/${postId}/comments/${commentId}/replies`, replyData);
|
|
10806
|
-
const replyId = doc.id;
|
|
10807
|
-
await incrementField(`posts/${postId}/comments/${commentId}`, "repliesCount", 1);
|
|
10808
|
-
try {
|
|
10809
|
-
await giveKarma(uid, "addComment", `${postId}_${replyId}`, KARMA_POINTS.addComment, text);
|
|
10810
|
-
} catch {
|
|
10811
|
-
}
|
|
10812
|
-
try {
|
|
10813
|
-
await incrementField(`users/${uid}/activity/totals`, "replies.written", 1);
|
|
10814
|
-
} catch {
|
|
10815
|
-
}
|
|
10816
|
-
return { reply: doc, karma: KARMA_POINTS.addComment };
|
|
9549
|
+
return api.post(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies`, { text });
|
|
10817
9550
|
}
|
|
10818
9551
|
async function deleteReply(uid, postId, commentId, replyId) {
|
|
10819
|
-
|
|
10820
|
-
validateDocId(commentId, "Comment ID");
|
|
10821
|
-
validateDocId(replyId, "Reply ID");
|
|
10822
|
-
const reply = await getDoc(`posts/${postId}/comments/${commentId}/replies/${replyId}`);
|
|
10823
|
-
checkOwnership(reply, uid, "delete");
|
|
10824
|
-
await deleteDoc(`posts/${postId}/comments/${commentId}/replies/${replyId}`);
|
|
10825
|
-
await incrementField(`posts/${postId}/comments/${commentId}`, "repliesCount", -1);
|
|
9552
|
+
await api.del(`/api/posts/${encodeURIComponent(postId)}/comments/${encodeURIComponent(commentId)}/replies/${encodeURIComponent(replyId)}`);
|
|
10826
9553
|
}
|
|
10827
9554
|
|
|
9555
|
+
// src/cli/types/api.ts
|
|
9556
|
+
var POST_TAGS = ["general", "hack", "story", "meme", "other", "question", "hack-tip", "activity"];
|
|
9557
|
+
|
|
10828
9558
|
// src/cli/commands/posts.ts
|
|
10829
9559
|
init_prompts();
|
|
10830
9560
|
init_tty();
|
|
@@ -10837,7 +9567,7 @@ async function pickComment(postId, commentId) {
|
|
|
10837
9567
|
message: "Select comment",
|
|
10838
9568
|
options: comments.map((c) => ({
|
|
10839
9569
|
value: c.id,
|
|
10840
|
-
label: `${truncate(
|
|
9570
|
+
label: `${truncate(c.text ?? "", 60)} ${import_picocolors10.default.dim(c.authorName ?? c.userId ?? "")}`
|
|
10841
9571
|
}))
|
|
10842
9572
|
});
|
|
10843
9573
|
}
|
|
@@ -10850,16 +9580,16 @@ async function pickReply(postId, commentId, replyId) {
|
|
|
10850
9580
|
message: "Select reply",
|
|
10851
9581
|
options: replies.map((r) => ({
|
|
10852
9582
|
value: r.id,
|
|
10853
|
-
label: `${truncate(
|
|
9583
|
+
label: `${truncate(r.text ?? "", 60)} ${import_picocolors10.default.dim(r.userId ?? "")}`
|
|
10854
9584
|
}))
|
|
10855
9585
|
});
|
|
10856
9586
|
}
|
|
10857
9587
|
function printPostLine(p) {
|
|
10858
|
-
const tag = import_picocolors10.default.cyan(
|
|
10859
|
-
const title = truncate(
|
|
9588
|
+
const tag = import_picocolors10.default.cyan(p.tag ?? "");
|
|
9589
|
+
const title = truncate(p.title, 55);
|
|
10860
9590
|
const comments = p.commentsCount ? import_picocolors10.default.dim(`${p.commentsCount} comments`) : "";
|
|
10861
9591
|
const time = formatRelativeDate(p.createdAt);
|
|
10862
|
-
const id = import_picocolors10.default.dim(
|
|
9592
|
+
const id = import_picocolors10.default.dim(p.id);
|
|
10863
9593
|
const parts = [tag, import_picocolors10.default.bold(title)];
|
|
10864
9594
|
if (comments) parts.push(comments);
|
|
10865
9595
|
parts.push(import_picocolors10.default.dim(time));
|
|
@@ -10876,7 +9606,7 @@ function printPostDetail(p) {
|
|
|
10876
9606
|
console.log(` ${import_picocolors10.default.dim(SYM.dash.repeat(40))}`);
|
|
10877
9607
|
printRecord([
|
|
10878
9608
|
["ID", import_picocolors10.default.dim(p.id)],
|
|
10879
|
-
["Tag", import_picocolors10.default.cyan(
|
|
9609
|
+
["Tag", import_picocolors10.default.cyan(p.tag ?? "")],
|
|
10880
9610
|
["Author", p.authorName ?? p.authorId],
|
|
10881
9611
|
["Comments", p.commentsCount != null ? String(p.commentsCount) : null],
|
|
10882
9612
|
["Likes", p.likesCount != null ? String(p.likesCount) : null],
|
|
@@ -10885,18 +9615,18 @@ function printPostDetail(p) {
|
|
|
10885
9615
|
console.log("");
|
|
10886
9616
|
}
|
|
10887
9617
|
function printCommentLine(c) {
|
|
10888
|
-
const author = import_picocolors10.default.bold(
|
|
9618
|
+
const author = import_picocolors10.default.bold(c.authorName ?? c.userId ?? "");
|
|
10889
9619
|
const time = import_picocolors10.default.dim(formatRelativeDate(c.createdAt));
|
|
10890
9620
|
const replies = c.repliesCount ? import_picocolors10.default.dim(`\xB7 ${c.repliesCount} replies`) : "";
|
|
10891
|
-
const text =
|
|
9621
|
+
const text = c.text ?? "";
|
|
10892
9622
|
console.log(` ${author} ${time}${replies}`);
|
|
10893
9623
|
console.log(` ${text}`);
|
|
10894
9624
|
console.log("");
|
|
10895
9625
|
}
|
|
10896
9626
|
function printReplyLine(r) {
|
|
10897
|
-
const text = truncate(
|
|
9627
|
+
const text = truncate(r.text ?? "", 60);
|
|
10898
9628
|
const time = formatRelativeDate(r.createdAt);
|
|
10899
|
-
const id = import_picocolors10.default.dim(
|
|
9629
|
+
const id = import_picocolors10.default.dim(r.id);
|
|
10900
9630
|
console.log(` ${text} ${import_picocolors10.default.dim(time)} ${id}`);
|
|
10901
9631
|
}
|
|
10902
9632
|
function registerPostsCommands(program3) {
|
|
@@ -10998,148 +9728,134 @@ Examples:
|
|
|
10998
9728
|
}
|
|
10999
9729
|
});
|
|
11000
9730
|
});
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
options: POST_TAGS.map((t2) => ({ value: t2, label: t2 }))
|
|
11012
|
-
});
|
|
11013
|
-
}
|
|
11014
|
-
const body = { title, body: postBody, tag };
|
|
11015
|
-
await runCreate({
|
|
11016
|
-
global: opts,
|
|
11017
|
-
fn: () => createPost(uid, body),
|
|
11018
|
-
dataKey: "post",
|
|
11019
|
-
spinnerMessage: "Creating post...",
|
|
11020
|
-
onInteractive: (post) => {
|
|
11021
|
-
console.log(`
|
|
11022
|
-
${import_picocolors10.default.green("Posted!")} ${post.title} ${import_picocolors10.default.dim(post.id)}
|
|
11023
|
-
`);
|
|
11024
|
-
}
|
|
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 }))
|
|
11025
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
|
+
}
|
|
11026
9754
|
});
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
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)}
|
|
11053
9782
|
`);
|
|
11054
|
-
|
|
11055
|
-
});
|
|
9783
|
+
}
|
|
11056
9784
|
});
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
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..."
|
|
11066
9794
|
});
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
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)}
|
|
11080
9809
|
`);
|
|
11081
|
-
|
|
11082
|
-
});
|
|
9810
|
+
}
|
|
11083
9811
|
});
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
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..."
|
|
11094
9822
|
});
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
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)}
|
|
11109
9838
|
`);
|
|
11110
|
-
|
|
11111
|
-
});
|
|
9839
|
+
}
|
|
11112
9840
|
});
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
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..."
|
|
11124
9852
|
});
|
|
11125
|
-
}
|
|
9853
|
+
});
|
|
11126
9854
|
}
|
|
11127
9855
|
|
|
11128
|
-
// src/cli/commands/profile.ts
|
|
11129
|
-
init_define_ADMIN_UIDS();
|
|
11130
|
-
|
|
11131
9856
|
// src/cli/services/profile.ts
|
|
11132
|
-
init_define_ADMIN_UIDS();
|
|
11133
9857
|
async function getProfile() {
|
|
11134
|
-
|
|
11135
|
-
if (!creds) throw new Error("Not logged in. Run: numo login");
|
|
11136
|
-
const doc = await getDoc(`users/${creds.uid}`);
|
|
11137
|
-
return {
|
|
11138
|
-
uid: creds.uid,
|
|
11139
|
-
email: creds.email ?? null,
|
|
11140
|
-
username: doc.username ?? null,
|
|
11141
|
-
photoURL: doc.photoURL ?? null
|
|
11142
|
-
};
|
|
9858
|
+
return api.get("/api/profile");
|
|
11143
9859
|
}
|
|
11144
9860
|
|
|
11145
9861
|
// src/cli/commands/profile.ts
|
|
@@ -11163,11 +9879,9 @@ function registerProfileCommands(program3) {
|
|
|
11163
9879
|
}
|
|
11164
9880
|
|
|
11165
9881
|
// src/cli/commands/doctor.ts
|
|
11166
|
-
init_define_ADMIN_UIDS();
|
|
11167
9882
|
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
11168
|
-
init_config();
|
|
11169
|
-
init_config();
|
|
11170
9883
|
init_tty();
|
|
9884
|
+
var API_BASE5 = process.env.NUMO_API_URL ?? "http://localhost:3000";
|
|
11171
9885
|
async function runChecks() {
|
|
11172
9886
|
const checks = [];
|
|
11173
9887
|
const nodeVersion = process.version;
|
|
@@ -11177,6 +9891,11 @@ async function runChecks() {
|
|
|
11177
9891
|
status: major >= 18 ? "ok" : "fail",
|
|
11178
9892
|
message: major >= 18 ? `Node ${nodeVersion}` : `Node ${nodeVersion} \u2014 requires >= 18`
|
|
11179
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
|
+
});
|
|
11180
9899
|
const creds = loadCredentials();
|
|
11181
9900
|
checks.push({
|
|
11182
9901
|
name: "credentials",
|
|
@@ -11193,18 +9912,11 @@ async function runChecks() {
|
|
|
11193
9912
|
} else {
|
|
11194
9913
|
checks.push({ name: "token", status: "fail", message: "Skipped (no credentials)" });
|
|
11195
9914
|
}
|
|
11196
|
-
const apiKey = getFirebaseApiKey();
|
|
11197
|
-
checks.push({
|
|
11198
|
-
name: "api_key",
|
|
11199
|
-
status: apiKey ? "ok" : "fail",
|
|
11200
|
-
message: apiKey ? "Firebase API key configured" : "NUMO_FIREBASE_API_KEY not set"
|
|
11201
|
-
});
|
|
11202
9915
|
try {
|
|
11203
|
-
const
|
|
11204
|
-
|
|
11205
|
-
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})` });
|
|
11206
9918
|
} catch (err) {
|
|
11207
|
-
checks.push({ name: "
|
|
9919
|
+
checks.push({ name: "api_reachable", status: "fail", message: `API server unreachable: ${err.message}` });
|
|
11208
9920
|
}
|
|
11209
9921
|
return checks;
|
|
11210
9922
|
}
|
|
@@ -11235,19 +9947,18 @@ function registerDoctorCommand(program3) {
|
|
|
11235
9947
|
}
|
|
11236
9948
|
|
|
11237
9949
|
// src/cli/lib/update-check.ts
|
|
11238
|
-
|
|
11239
|
-
var
|
|
11240
|
-
var path3 = __toESM(require("path"), 1);
|
|
9950
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
9951
|
+
var path2 = __toESM(require("path"), 1);
|
|
11241
9952
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
11242
9953
|
init_tty();
|
|
11243
9954
|
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
11244
9955
|
var PACKAGE_NAME = "numo-cli";
|
|
11245
9956
|
function getStatePath() {
|
|
11246
|
-
return
|
|
9957
|
+
return path2.join(getConfigDir(), "update-check.json");
|
|
11247
9958
|
}
|
|
11248
9959
|
function loadState() {
|
|
11249
9960
|
try {
|
|
11250
|
-
return JSON.parse(
|
|
9961
|
+
return JSON.parse(fs4.readFileSync(getStatePath(), "utf8"));
|
|
11251
9962
|
} catch {
|
|
11252
9963
|
return { lastCheck: 0 };
|
|
11253
9964
|
}
|
|
@@ -11255,7 +9966,7 @@ function loadState() {
|
|
|
11255
9966
|
function saveState(state) {
|
|
11256
9967
|
try {
|
|
11257
9968
|
ensureConfigDir();
|
|
11258
|
-
|
|
9969
|
+
fs4.writeFileSync(getStatePath(), JSON.stringify(state), { mode: 384 });
|
|
11259
9970
|
} catch {
|
|
11260
9971
|
}
|
|
11261
9972
|
}
|
|
@@ -11304,7 +10015,7 @@ function fetchLatestVersion(state) {
|
|
|
11304
10015
|
// src/cli/cli.ts
|
|
11305
10016
|
init_tty();
|
|
11306
10017
|
init_errors();
|
|
11307
|
-
var CLI_VERSION = true ? "1.
|
|
10018
|
+
var CLI_VERSION = true ? "1.3.0" : "0.0.0-dev";
|
|
11308
10019
|
var program2 = new Command();
|
|
11309
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) => {
|
|
11310
10021
|
const opts = thisCommand.optsWithGlobals();
|
|
@@ -11326,7 +10037,7 @@ program2.command("login").description("Login with your Numo account").option("--
|
|
|
11326
10037
|
Examples:
|
|
11327
10038
|
$ numo login # Interactive (email/password)
|
|
11328
10039
|
$ numo login --phone # SMS OTP flow`);
|
|
11329
|
-
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) => {
|
|
11330
10041
|
await register(opts);
|
|
11331
10042
|
}).addHelpText("after", `
|
|
11332
10043
|
Examples:
|
|
@@ -11334,7 +10045,6 @@ Examples:
|
|
|
11334
10045
|
$ numo register --email user@example.com --password s3cret # Non-interactive`);
|
|
11335
10046
|
program2.command("logout").description("Clear stored credentials").action(() => {
|
|
11336
10047
|
clearCredentials();
|
|
11337
|
-
clearStreaks();
|
|
11338
10048
|
console.log(import_picocolors13.default.green("Logged out."));
|
|
11339
10049
|
});
|
|
11340
10050
|
program2.command("whoami").description("Show current auth status (no API call)").action(function() {
|
|
@@ -11366,7 +10076,7 @@ program2.command("whoami").description("Show current auth status (no API call)")
|
|
|
11366
10076
|
}
|
|
11367
10077
|
});
|
|
11368
10078
|
registerTasksCommands(program2);
|
|
11369
|
-
program2.command("add [text...]").description("Quick-add a task (today, public, no wizard)").option("--due <date>", "Due date YYYY-MM-DD (default: today)").option("--tags <tags>", "Comma-separated tags").option("--private", "Make task private").action(async function(textParts) {
|
|
10079
|
+
program2.command("add [text...]").description("Quick-add a task (today, public, no wizard)").option("--due <date>", "Due date YYYY-MM-DD (default: today)").option("--tags <tags>", "Comma-separated tags").option("--public", "Make task public (default)").option("--private", "Make task private").action(async function(textParts) {
|
|
11370
10080
|
const opts = this.optsWithGlobals();
|
|
11371
10081
|
const uid = requireUid();
|
|
11372
10082
|
const text = textParts?.join(" ");
|
|
@@ -11379,6 +10089,7 @@ program2.command("add [text...]").description("Quick-add a task (today, public,
|
|
|
11379
10089
|
dueDate: opts.due ? parseHumanDate(opts.due) ?? opts.due : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
11380
10090
|
};
|
|
11381
10091
|
if (opts.tags) body.tags = opts.tags.split(",");
|
|
10092
|
+
if (opts.public) body.isPublic = true;
|
|
11382
10093
|
if (opts.private) body.isPublic = false;
|
|
11383
10094
|
await runCreate({
|
|
11384
10095
|
global: opts,
|
|
@@ -11386,17 +10097,16 @@ program2.command("add [text...]").description("Quick-add a task (today, public,
|
|
|
11386
10097
|
dataKey: "task",
|
|
11387
10098
|
spinnerMessage: "Creating task...",
|
|
11388
10099
|
onInteractive: (_task, payload) => {
|
|
11389
|
-
const task = payload
|
|
10100
|
+
const { task, karma } = payload;
|
|
11390
10101
|
const check = import_picocolors13.default.green(SYM.check);
|
|
11391
10102
|
console.log(`
|
|
11392
10103
|
${check} Created ${task.text} ${import_picocolors13.default.dim(task.id)}`);
|
|
11393
|
-
const karma = payload.karma;
|
|
11394
10104
|
if (karma) console.log(` ${formatKarmaGain(karma)}`);
|
|
11395
10105
|
console.log("");
|
|
11396
10106
|
}
|
|
11397
10107
|
});
|
|
11398
10108
|
});
|
|
11399
|
-
|
|
10109
|
+
registerPostsCommands(program2);
|
|
11400
10110
|
registerProfileCommands(program2);
|
|
11401
10111
|
registerDoctorCommand(program2);
|
|
11402
10112
|
program2.command("commands").description("List all available commands").action(function() {
|