sec-ry 1.1.5
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.bundle.js +7211 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +37 -0
- package/dist/cmd/changelog.d.ts +2 -0
- package/dist/cmd/changelog.js +726 -0
- package/dist/cmd/denc.d.ts +2 -0
- package/dist/cmd/denc.js +74 -0
- package/dist/cmd/enc.d.ts +2 -0
- package/dist/cmd/enc.js +225 -0
- package/dist/cmd/example.d.ts +1 -0
- package/dist/cmd/example.js +92 -0
- package/dist/cmd/fp.d.ts +2 -0
- package/dist/cmd/fp.js +29 -0
- package/dist/cmd/gen.d.ts +2 -0
- package/dist/cmd/gen.js +76 -0
- package/dist/cmd/history.d.ts +2 -0
- package/dist/cmd/history.js +25 -0
- package/dist/cmd/info.d.ts +2 -0
- package/dist/cmd/info.js +28 -0
- package/dist/cmd/root.d.ts +1 -0
- package/dist/cmd/root.js +191 -0
- package/dist/cmd/seal.d.ts +3 -0
- package/dist/cmd/seal.js +81 -0
- package/dist/cmd/upgrade.d.ts +1 -0
- package/dist/cmd/upgrade.js +44 -0
- package/dist/cmd/verify.d.ts +2 -0
- package/dist/cmd/verify.js +30 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +27 -0
- package/dist/lib/args.d.ts +8 -0
- package/dist/lib/args.js +43 -0
- package/dist/lib/clip.d.ts +2 -0
- package/dist/lib/clip.js +39 -0
- package/dist/lib/crypto.d.ts +29 -0
- package/dist/lib/crypto.js +312 -0
- package/dist/lib/env.d.ts +13 -0
- package/dist/lib/env.js +42 -0
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.js +61 -0
- package/dist/lib/history.d.ts +9 -0
- package/dist/lib/history.js +35 -0
- package/dist/lib/password.d.ts +2 -0
- package/dist/lib/password.js +31 -0
- package/dist/lib/rc.d.ts +7 -0
- package/dist/lib/rc.js +45 -0
- package/dist/lib/ui.d.ts +21 -0
- package/dist/lib/ui.js +137 -0
- package/package.json +44 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.adminPanel = adminPanel;
|
|
37
|
+
exports.cmdChangelog = cmdChangelog;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const readline = __importStar(require("readline"));
|
|
42
|
+
const crypto = __importStar(require("crypto"));
|
|
43
|
+
const _D = path.join(os.homedir(), ".secry");
|
|
44
|
+
const _F = path.join(_D, "changelog.json");
|
|
45
|
+
const _FP = path.join(_D, "changelog.pub.json");
|
|
46
|
+
const _PF = path.join(_D, ".adm");
|
|
47
|
+
const _LF = path.join(_D, "changelog.audit.log");
|
|
48
|
+
const _BF = path.join(_D, ".blk");
|
|
49
|
+
const _SN = 16384;
|
|
50
|
+
const _SR = 8;
|
|
51
|
+
const _SP = 2;
|
|
52
|
+
const _SL = 64;
|
|
53
|
+
const _TMS = 2 * 60 * 1000;
|
|
54
|
+
const _PS = 5;
|
|
55
|
+
const _VT = ["feature", "fix", "breaking", "security", "other"];
|
|
56
|
+
const _VP = ["nodejs", "rust"];
|
|
57
|
+
const _AD = Buffer.from("secry-changelog-v1");
|
|
58
|
+
const $ = {
|
|
59
|
+
r: "\x1b[0m", d: "\x1b[2m", b: "\x1b[1m",
|
|
60
|
+
w: "\x1b[97m", c: "\x1b[96m", g: "\x1b[92m",
|
|
61
|
+
e: "\x1b[91m", y: "\x1b[93m",
|
|
62
|
+
};
|
|
63
|
+
const _TC = {
|
|
64
|
+
feature: "\x1b[97m",
|
|
65
|
+
fix: "\x1b[38;5;250m",
|
|
66
|
+
breaking: "\x1b[38;5;245m",
|
|
67
|
+
security: "\x1b[38;5;240m",
|
|
68
|
+
other: "\x1b[2m",
|
|
69
|
+
};
|
|
70
|
+
const _PC = {
|
|
71
|
+
nodejs: "\x1b[38;5;114m",
|
|
72
|
+
rust: "\x1b[38;5;174m",
|
|
73
|
+
};
|
|
74
|
+
const _w = (s) => process.stderr.write(s);
|
|
75
|
+
function _box(t) {
|
|
76
|
+
const l = "─".repeat(48);
|
|
77
|
+
_w(`\n ${$.d}${l}${$.r}\n ${$.b}${$.w} ${t}${$.r}\n ${$.d}${l}${$.r}\n\n`);
|
|
78
|
+
}
|
|
79
|
+
let _tmr = null;
|
|
80
|
+
function _rt() {
|
|
81
|
+
if (_tmr)
|
|
82
|
+
clearTimeout(_tmr);
|
|
83
|
+
_tmr = setTimeout(() => {
|
|
84
|
+
_w(`\n\n ${$.y}session timed out.${$.r}\n\n`);
|
|
85
|
+
_al("timeout", "expired");
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}, _TMS);
|
|
88
|
+
_tmr.ref?.();
|
|
89
|
+
}
|
|
90
|
+
function _st() { if (_tmr) {
|
|
91
|
+
clearTimeout(_tmr);
|
|
92
|
+
_tmr = null;
|
|
93
|
+
} }
|
|
94
|
+
function _lb() {
|
|
95
|
+
try {
|
|
96
|
+
if (fs.existsSync(_BF))
|
|
97
|
+
return JSON.parse(fs.readFileSync(_BF, "utf8"));
|
|
98
|
+
}
|
|
99
|
+
catch { }
|
|
100
|
+
return { attempts: 0, until: 0 };
|
|
101
|
+
}
|
|
102
|
+
function _sb(d) {
|
|
103
|
+
try {
|
|
104
|
+
fs.writeFileSync(_BF, JSON.stringify(d), "utf8");
|
|
105
|
+
}
|
|
106
|
+
catch { }
|
|
107
|
+
}
|
|
108
|
+
function _cb() {
|
|
109
|
+
const d = _lb();
|
|
110
|
+
if (d.until > Date.now()) {
|
|
111
|
+
_w(`\n ${$.e}too many attempts. wait ${Math.ceil((d.until - Date.now()) / 1000)}s.${$.r}\n\n`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function _fb() {
|
|
116
|
+
const d = _lb();
|
|
117
|
+
d.attempts++;
|
|
118
|
+
const dl = Math.min(30, Math.pow(2, d.attempts - 1)) * 1000;
|
|
119
|
+
d.until = Date.now() + dl;
|
|
120
|
+
_sb(d);
|
|
121
|
+
const rm = 3 - d.attempts;
|
|
122
|
+
if (rm > 0)
|
|
123
|
+
_w(`\n ${$.e}wrong password. ${rm} attempt(s) left.${$.r}\n\n`);
|
|
124
|
+
else
|
|
125
|
+
_w(`\n ${$.e}too many attempts. retry in ${dl / 1000}s.${$.r}\n\n`);
|
|
126
|
+
}
|
|
127
|
+
function _xb() { try {
|
|
128
|
+
if (fs.existsSync(_BF))
|
|
129
|
+
fs.unlinkSync(_BF);
|
|
130
|
+
}
|
|
131
|
+
catch { } }
|
|
132
|
+
function _nv(p) {
|
|
133
|
+
try {
|
|
134
|
+
if (p === "nodejs") {
|
|
135
|
+
const f = path.join(process.cwd(), "package.json");
|
|
136
|
+
if (fs.existsSync(f)) {
|
|
137
|
+
const pts = String(JSON.parse(fs.readFileSync(f, "utf8")).version).split(".").map(Number);
|
|
138
|
+
if (!pts.some(isNaN)) {
|
|
139
|
+
pts[2] = (pts[2] ?? 0) + 1;
|
|
140
|
+
return pts.join(".");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (p === "rust") {
|
|
145
|
+
const f = path.join(os.homedir(), "secry-rs", "Cargo.toml");
|
|
146
|
+
if (fs.existsSync(f)) {
|
|
147
|
+
const m = fs.readFileSync(f, "utf8").match(/^version\s*=\s*"(.+?)"/m);
|
|
148
|
+
if (m?.[1]) {
|
|
149
|
+
const pts = m[1].split(".").map(Number);
|
|
150
|
+
if (!pts.some(isNaN)) {
|
|
151
|
+
pts[2] = (pts[2] ?? 0) + 1;
|
|
152
|
+
return pts.join(".");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch { }
|
|
159
|
+
return "1.0.0";
|
|
160
|
+
}
|
|
161
|
+
function _vv(v) { return /^\d+(\.\d+){1,2}$/.test(v); }
|
|
162
|
+
function _dk(pw, salt) {
|
|
163
|
+
return crypto.scryptSync(Buffer.from(pw, "utf8"), salt, _SL, { N: _SN, r: _SR, p: _SP });
|
|
164
|
+
}
|
|
165
|
+
function _hp(pw) {
|
|
166
|
+
const salt = crypto.randomBytes(32);
|
|
167
|
+
const key = _dk(pw, salt);
|
|
168
|
+
const hp = key.subarray(0, 32);
|
|
169
|
+
const hk = key.subarray(32);
|
|
170
|
+
const hm = crypto.createHmac("sha256", hk).update(hp).digest();
|
|
171
|
+
return Buffer.from(JSON.stringify({ v: 2, salt: salt.toString("hex"), hash: hp.toString("hex"), hmac: hm.toString("hex") })).toString("base64");
|
|
172
|
+
}
|
|
173
|
+
function _vp(pw, stored) {
|
|
174
|
+
try {
|
|
175
|
+
const o = JSON.parse(Buffer.from(stored, "base64").toString("utf8"));
|
|
176
|
+
const key = _dk(pw, Buffer.from(o.salt, "hex"));
|
|
177
|
+
const hp = key.subarray(0, 32);
|
|
178
|
+
const hk = key.subarray(32);
|
|
179
|
+
const exp = crypto.createHmac("sha256", hk).update(hp).digest();
|
|
180
|
+
const got = Buffer.from(o.hmac, "hex");
|
|
181
|
+
return exp.length === got.length && crypto.timingSafeEqual(exp, got);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
let _sk = null;
|
|
188
|
+
function _gk(pw) {
|
|
189
|
+
if (_sk)
|
|
190
|
+
return _sk;
|
|
191
|
+
const o = JSON.parse(Buffer.from(fs.readFileSync(_PF, "utf8").trim(), "base64").toString("utf8"));
|
|
192
|
+
_sk = crypto.scryptSync(Buffer.from(pw + ":enc", "utf8"), Buffer.from(o.salt, "hex"), 32, { N: _SN, r: _SR, p: _SP });
|
|
193
|
+
return _sk;
|
|
194
|
+
}
|
|
195
|
+
function _en(data, key) {
|
|
196
|
+
const iv = crypto.randomBytes(12);
|
|
197
|
+
const c = crypto.createCipheriv("chacha20-poly1305", key, iv, { authTagLength: 16 });
|
|
198
|
+
c.setAAD(_AD, { plaintextLength: Buffer.byteLength(data) });
|
|
199
|
+
const enc = Buffer.concat([c.update(data, "utf8"), c.final()]);
|
|
200
|
+
const tag = c.getAuthTag();
|
|
201
|
+
return Buffer.concat([iv, tag, enc]).toString("base64");
|
|
202
|
+
}
|
|
203
|
+
function _de(raw, key) {
|
|
204
|
+
const b = Buffer.from(raw, "base64");
|
|
205
|
+
const iv = b.subarray(0, 12), tag = b.subarray(12, 28), enc = b.subarray(28);
|
|
206
|
+
const d = crypto.createDecipheriv("chacha20-poly1305", key, iv, { authTagLength: 16 });
|
|
207
|
+
d.setAAD(_AD, { plaintextLength: enc.length });
|
|
208
|
+
d.setAuthTag(tag);
|
|
209
|
+
return Buffer.concat([d.update(enc), d.final()]).toString("utf8");
|
|
210
|
+
}
|
|
211
|
+
function _lo(pw) {
|
|
212
|
+
if (pw) {
|
|
213
|
+
if (!fs.existsSync(_F))
|
|
214
|
+
return [];
|
|
215
|
+
try {
|
|
216
|
+
return JSON.parse(_de(fs.readFileSync(_F, "utf8").trim(), _gk(pw))).entries ?? [];
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!fs.existsSync(_FP))
|
|
223
|
+
return [];
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(Buffer.from(fs.readFileSync(_FP, "utf8").trim(), "base64").toString("utf8")).entries ?? [];
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function _sv(entries, pw) {
|
|
232
|
+
if (!fs.existsSync(_D))
|
|
233
|
+
fs.mkdirSync(_D, { recursive: true });
|
|
234
|
+
const json = JSON.stringify({ entries }, null, 2);
|
|
235
|
+
const tmp = _F + ".tmp";
|
|
236
|
+
fs.writeFileSync(tmp, _en(json, _gk(pw)), "utf8");
|
|
237
|
+
fs.renameSync(tmp, _F);
|
|
238
|
+
fs.copyFileSync(_F, _F + ".bak");
|
|
239
|
+
fs.writeFileSync(_FP, Buffer.from(json).toString("base64"), "utf8");
|
|
240
|
+
}
|
|
241
|
+
function _al(action, detail) {
|
|
242
|
+
try {
|
|
243
|
+
fs.appendFileSync(_LF, `${new Date().toISOString()} ${action.padEnd(12)} ${detail}\n`, "utf8");
|
|
244
|
+
}
|
|
245
|
+
catch { }
|
|
246
|
+
}
|
|
247
|
+
function _id() { return crypto.randomBytes(6).toString("hex"); }
|
|
248
|
+
function _pr(q, tick) {
|
|
249
|
+
return new Promise(r => {
|
|
250
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
251
|
+
rl.question(q, a => { rl.close(); tick?.(); r(a.trim()); });
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function _ph(q) {
|
|
255
|
+
return new Promise(r => {
|
|
256
|
+
process.stdout.write(q);
|
|
257
|
+
process.stdin.resume();
|
|
258
|
+
process.stdin.setRawMode?.(true);
|
|
259
|
+
process.stdin.setEncoding("utf8");
|
|
260
|
+
let v = "";
|
|
261
|
+
process.stdin.on("data", function h(ch) {
|
|
262
|
+
if (ch === "\r" || ch === "\n") {
|
|
263
|
+
process.stdin.setRawMode?.(false);
|
|
264
|
+
process.stdin.pause();
|
|
265
|
+
process.stdin.removeListener("data", h);
|
|
266
|
+
process.stdout.write("\n");
|
|
267
|
+
r(v);
|
|
268
|
+
}
|
|
269
|
+
else if (ch === "\u0003") {
|
|
270
|
+
process.exit(0);
|
|
271
|
+
}
|
|
272
|
+
else if (ch === "\u007f") {
|
|
273
|
+
if (v.length) {
|
|
274
|
+
v = v.slice(0, -1);
|
|
275
|
+
process.stdout.write("\b \b");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
v += ch;
|
|
280
|
+
process.stdout.write("*");
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
let _mr = false;
|
|
286
|
+
function _mn(items, tick) {
|
|
287
|
+
return new Promise(resolve => {
|
|
288
|
+
let idx = 0;
|
|
289
|
+
while (items[idx]?.dim)
|
|
290
|
+
idx = (idx + 1) % items.length;
|
|
291
|
+
function render() {
|
|
292
|
+
if (_mr)
|
|
293
|
+
process.stderr.write(`\x1b[${items.length}A`);
|
|
294
|
+
_mr = true;
|
|
295
|
+
for (let i = 0; i < items.length; i++) {
|
|
296
|
+
const it = items[i];
|
|
297
|
+
if (it.dim) {
|
|
298
|
+
_w(` ${$.d} ${it.label}${$.r}\n`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const s = i === idx;
|
|
302
|
+
_w(` ${s ? `${$.b}${$.c}›${$.r} ` : `${$.d} `}${s ? $.b : ""}${it.label}${$.r}\n`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
process.stdin.setRawMode?.(true);
|
|
306
|
+
process.stdin.resume();
|
|
307
|
+
process.stdin.setEncoding("utf8");
|
|
308
|
+
render();
|
|
309
|
+
process.stdin.on("data", function h(ch) {
|
|
310
|
+
tick?.();
|
|
311
|
+
if (ch === "\u0003")
|
|
312
|
+
process.exit(0);
|
|
313
|
+
if (ch === "\u001b[A") {
|
|
314
|
+
do {
|
|
315
|
+
idx = (idx - 1 + items.length) % items.length;
|
|
316
|
+
} while (items[idx]?.dim);
|
|
317
|
+
render();
|
|
318
|
+
}
|
|
319
|
+
else if (ch === "\u001b[B") {
|
|
320
|
+
do {
|
|
321
|
+
idx = (idx + 1) % items.length;
|
|
322
|
+
} while (items[idx]?.dim);
|
|
323
|
+
render();
|
|
324
|
+
}
|
|
325
|
+
else if (ch === "\r" || ch === "\n") {
|
|
326
|
+
process.stdin.setRawMode?.(false);
|
|
327
|
+
process.stdin.pause();
|
|
328
|
+
process.stdin.removeListener("data", h);
|
|
329
|
+
_mr = false;
|
|
330
|
+
_w("\n");
|
|
331
|
+
resolve(items[idx].key);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function _rt2(tags) {
|
|
337
|
+
if (!tags?.length)
|
|
338
|
+
return "";
|
|
339
|
+
return tags.map(t => `${_TC[t]}[${t}]${$.r}`).join(" ");
|
|
340
|
+
}
|
|
341
|
+
function _rp(p) {
|
|
342
|
+
return `${_PC[p]}[${p}]${$.r}`;
|
|
343
|
+
}
|
|
344
|
+
function _pv(e) {
|
|
345
|
+
_w(`\n ${$.d}── preview ${"─".repeat(37)}${$.r}\n\n`);
|
|
346
|
+
_w(` ${$.c}${$.b}v${e.version}${$.r} ${$.d}${e.date}${$.r} ${_rp(e.platform)}`);
|
|
347
|
+
if (e.tags?.length)
|
|
348
|
+
_w(` ${_rt2(e.tags)}`);
|
|
349
|
+
_w(`\n ${$.w}${e.title}${$.r}\n\n`);
|
|
350
|
+
for (const i of e.items)
|
|
351
|
+
_w(` ${$.d}—${$.r} ${i}\n`);
|
|
352
|
+
_w(`\n ${$.d}${"─".repeat(48)}${$.r}\n\n`);
|
|
353
|
+
}
|
|
354
|
+
async function _pg(entries, tick) {
|
|
355
|
+
if (!entries.length) {
|
|
356
|
+
_w(` ${$.d}no entries.${$.r}\n\n`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
let page = 0;
|
|
360
|
+
const total = Math.ceil(entries.length / _PS);
|
|
361
|
+
while (true) {
|
|
362
|
+
for (const e of entries.slice(page * _PS, (page + 1) * _PS)) {
|
|
363
|
+
_w(` ${$.d}${"─".repeat(44)}${$.r}\n`);
|
|
364
|
+
_w(` ${$.c}${$.b}v${e.version}${$.r} ${$.d}${e.date}${$.r} ${_rp(e.platform)}`);
|
|
365
|
+
if (e.tags?.length)
|
|
366
|
+
_w(` ${_rt2(e.tags)}`);
|
|
367
|
+
_w(`\n ${$.w}${e.title}${$.r}\n\n`);
|
|
368
|
+
for (const i of e.items)
|
|
369
|
+
_w(` ${$.d}—${$.r} ${i}\n`);
|
|
370
|
+
_w("\n");
|
|
371
|
+
}
|
|
372
|
+
if (total <= 1)
|
|
373
|
+
break;
|
|
374
|
+
_w(` ${$.d}page ${page + 1}/${total}${$.r}\n\n`);
|
|
375
|
+
const nav = [];
|
|
376
|
+
if (page > 0)
|
|
377
|
+
nav.push({ label: "← previous", key: "p" });
|
|
378
|
+
if (page < total - 1)
|
|
379
|
+
nav.push({ label: "next →", key: "n" });
|
|
380
|
+
nav.push({ label: "back", key: "x" });
|
|
381
|
+
const c = await _mn(nav, tick);
|
|
382
|
+
if (c === "n")
|
|
383
|
+
page++;
|
|
384
|
+
else if (c === "p")
|
|
385
|
+
page--;
|
|
386
|
+
else
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function _vi(filterTag, filterPlat, search) {
|
|
391
|
+
let entries = _lo();
|
|
392
|
+
if (filterPlat)
|
|
393
|
+
entries = entries.filter(e => e.platform === filterPlat);
|
|
394
|
+
if (filterTag)
|
|
395
|
+
entries = entries.filter(e => e.tags?.includes(filterTag));
|
|
396
|
+
if (search) {
|
|
397
|
+
const q = search.toLowerCase();
|
|
398
|
+
entries = entries.filter(e => e.version.includes(q) ||
|
|
399
|
+
e.title.toLowerCase().includes(q) ||
|
|
400
|
+
e.items.some(i => i.toLowerCase().includes(q)));
|
|
401
|
+
}
|
|
402
|
+
_box("changelog");
|
|
403
|
+
if (!entries.length) {
|
|
404
|
+
_w(` ${$.d}no releases yet.${$.r}\n\n`);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
for (const e of entries) {
|
|
408
|
+
_w(` ${$.d}${"─".repeat(44)}${$.r}\n`);
|
|
409
|
+
_w(` ${$.c}${$.b}v${e.version}${$.r} ${$.d}${e.date}${$.r} ${_rp(e.platform)}`);
|
|
410
|
+
if (e.tags?.length)
|
|
411
|
+
_w(` ${_rt2(e.tags)}`);
|
|
412
|
+
_w(`\n ${$.w}${e.title}${$.r}\n\n`);
|
|
413
|
+
for (const i of e.items)
|
|
414
|
+
_w(` ${$.d}—${$.r} ${i}\n`);
|
|
415
|
+
_w("\n");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function _ex(out) {
|
|
419
|
+
const entries = _lo();
|
|
420
|
+
if (!entries.length) {
|
|
421
|
+
_w(` ${$.d}nothing to export.${$.r}\n\n`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const lines = ["# changelog\n"];
|
|
425
|
+
for (const e of entries) {
|
|
426
|
+
const t = e.tags?.length ? " `" + e.tags.join("` `") + "`" : "";
|
|
427
|
+
lines.push(`## [${e.platform}] v${e.version} — ${e.date}${t}`);
|
|
428
|
+
lines.push(`**${e.title}**\n`);
|
|
429
|
+
for (const i of e.items)
|
|
430
|
+
lines.push(`- ${i}`);
|
|
431
|
+
lines.push("");
|
|
432
|
+
}
|
|
433
|
+
const dest = out || path.join(os.homedir(), "secry-changelog.md");
|
|
434
|
+
fs.writeFileSync(dest, lines.join("\n"), "utf8");
|
|
435
|
+
_w(`\n ${$.g}✔${$.r} exported to ${dest}\n\n`);
|
|
436
|
+
_al("export", dest);
|
|
437
|
+
}
|
|
438
|
+
async function _tr(text) {
|
|
439
|
+
try {
|
|
440
|
+
const { translate } = await Promise.resolve().then(() => __importStar(require("@vitalets/google-translate-api")));
|
|
441
|
+
const r = await Promise.race([
|
|
442
|
+
translate(text, { from: "pt", to: "en" }),
|
|
443
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error("t")), 5000)),
|
|
444
|
+
]);
|
|
445
|
+
return r?.text ?? null;
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async function _aa(pw, tick) {
|
|
452
|
+
_w(`\n ${$.c}new entry${$.r}\n\n`);
|
|
453
|
+
const existing = _lo(pw);
|
|
454
|
+
const suggested = _nv("nodejs");
|
|
455
|
+
_w(` ${$.d}suggested: ${suggested}${$.r}\n`);
|
|
456
|
+
let version = "";
|
|
457
|
+
while (true) {
|
|
458
|
+
version = await _pr(` version [${suggested}]: `, tick);
|
|
459
|
+
if (!version) {
|
|
460
|
+
version = suggested;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
if (!_vv(version)) {
|
|
464
|
+
_w(` ${$.e}numbers and dots only (e.g. 1.2.0)${$.r}\n`);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (existing.some(e => e.version === version)) {
|
|
468
|
+
_w(` ${$.y}v${version} already exists.${$.r}\n`);
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
const title = await _pr(" title: ", tick);
|
|
474
|
+
if (!title) {
|
|
475
|
+
_w(`\n ${$.e}cancelled.${$.r}\n\n`);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
_w(" items (one per line, empty to finish):\n");
|
|
479
|
+
const items = [];
|
|
480
|
+
while (true) {
|
|
481
|
+
const item = await _pr(" — ", tick);
|
|
482
|
+
if (!item)
|
|
483
|
+
break;
|
|
484
|
+
items.push(item);
|
|
485
|
+
}
|
|
486
|
+
if (!items.length) {
|
|
487
|
+
_w(`\n ${$.e}cancelled — no items.${$.r}\n\n`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
_w(`\n tags:\n\n`);
|
|
491
|
+
const tagChoice = await _mn([
|
|
492
|
+
{ label: `${_TC.feature}feature${$.r}`, key: "feature" },
|
|
493
|
+
{ label: `${_TC.fix}fix${$.r}`, key: "fix" },
|
|
494
|
+
{ label: `${_TC.breaking}breaking${$.r}`, key: "breaking" },
|
|
495
|
+
{ label: `${_TC.security}security${$.r}`, key: "security" },
|
|
496
|
+
{ label: `${_TC.other}other${$.r}`, key: "other" },
|
|
497
|
+
], tick);
|
|
498
|
+
_w(`\n platform:\n\n`);
|
|
499
|
+
const platChoice = await _mn([
|
|
500
|
+
{ label: `${_PC.nodejs}nodejs${$.r}`, key: "nodejs" },
|
|
501
|
+
{ label: `${_PC.rust}rust${$.r}`, key: "rust" },
|
|
502
|
+
], tick);
|
|
503
|
+
let finalTitle = title;
|
|
504
|
+
let finalItems = items;
|
|
505
|
+
_w(`\n ${$.d}translating PT→EN...${$.r}`);
|
|
506
|
+
const tTitle = await _tr(title);
|
|
507
|
+
if (tTitle) {
|
|
508
|
+
finalTitle = tTitle;
|
|
509
|
+
const tItems = await Promise.all(items.map(i => _tr(i)));
|
|
510
|
+
finalItems = tItems.map((t, i) => t ?? items[i]);
|
|
511
|
+
_w(`\r ${$.g}✔${$.r} translated. \n`);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
_w(`\r ${$.d}translation unavailable.${$.r}\n`);
|
|
515
|
+
}
|
|
516
|
+
const entry = {
|
|
517
|
+
id: _id(), date: new Date().toISOString().slice(0, 10),
|
|
518
|
+
version, title: finalTitle, items: finalItems,
|
|
519
|
+
tags: [tagChoice], platform: platChoice,
|
|
520
|
+
};
|
|
521
|
+
_pv(entry);
|
|
522
|
+
const ok = await _pr(" save? [Y/n]: ", tick);
|
|
523
|
+
if (ok.toLowerCase() === "n") {
|
|
524
|
+
_w(` ${$.d}cancelled.${$.r}\n\n`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
existing.unshift(entry);
|
|
528
|
+
_sv(existing, pw);
|
|
529
|
+
_al("add", `${platChoice} v${entry.version}`);
|
|
530
|
+
_w(`\n ${$.g}✔${$.r} v${entry.version} saved.\n\n`);
|
|
531
|
+
}
|
|
532
|
+
async function _ae(pw, tick) {
|
|
533
|
+
const entries = _lo(pw);
|
|
534
|
+
if (!entries.length) {
|
|
535
|
+
_w(`\n ${$.d}nothing to edit.${$.r}\n\n`);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
_w(`\n ${$.c}select entry:${$.r}\n\n`);
|
|
539
|
+
const key = await _mn(entries.map((e, i) => ({
|
|
540
|
+
label: `${_rp(e.platform)} v${e.version} ${$.d}${e.date}${$.r} ${e.title}`,
|
|
541
|
+
key: String(i),
|
|
542
|
+
})).concat([{ label: "cancel", key: "x" }]), tick);
|
|
543
|
+
if (key === "x")
|
|
544
|
+
return;
|
|
545
|
+
const idx = parseInt(key);
|
|
546
|
+
const e = { ...entries[idx] };
|
|
547
|
+
_w(`\n ${$.d}blank = keep current${$.r}\n\n`);
|
|
548
|
+
let version = "";
|
|
549
|
+
while (true) {
|
|
550
|
+
version = await _pr(` version [${e.version}]: `, tick);
|
|
551
|
+
if (!version) {
|
|
552
|
+
version = e.version;
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
if (!_vv(version)) {
|
|
556
|
+
_w(` ${$.e}numbers and dots only${$.r}\n`);
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (version !== e.version && entries.some(x => x.version === version && x.platform === e.platform)) {
|
|
560
|
+
_w(` ${$.y}v${version} already exists.${$.r}\n`);
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
const title = await _pr(` title [${e.title}]: `, tick);
|
|
566
|
+
_w(` ${$.d}current items: ${e.items.join(" / ")}${$.r}\n`);
|
|
567
|
+
const fi = await _pr(" — ", tick);
|
|
568
|
+
let items = e.items;
|
|
569
|
+
if (fi) {
|
|
570
|
+
items = [fi];
|
|
571
|
+
while (true) {
|
|
572
|
+
const i = await _pr(" — ", tick);
|
|
573
|
+
if (!i)
|
|
574
|
+
break;
|
|
575
|
+
items.push(i);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
_w(`\n tag:\n\n`);
|
|
579
|
+
const tagChoice = await _mn([
|
|
580
|
+
{ label: `${_TC.feature}feature${$.r}`, key: "feature" },
|
|
581
|
+
{ label: `${_TC.fix}fix${$.r}`, key: "fix" },
|
|
582
|
+
{ label: `${_TC.breaking}breaking${$.r}`, key: "breaking" },
|
|
583
|
+
{ label: `${_TC.security}security${$.r}`, key: "security" },
|
|
584
|
+
{ label: `${_TC.other}other${$.r}`, key: "other" },
|
|
585
|
+
], tick);
|
|
586
|
+
_w(`\n platform:\n\n`);
|
|
587
|
+
const platChoice = await _mn([
|
|
588
|
+
{ label: `${_PC.nodejs}nodejs${$.r}`, key: "nodejs" },
|
|
589
|
+
{ label: `${_PC.rust}rust${$.r}`, key: "rust" },
|
|
590
|
+
], tick);
|
|
591
|
+
e.version = version;
|
|
592
|
+
if (title)
|
|
593
|
+
e.title = title;
|
|
594
|
+
e.items = items;
|
|
595
|
+
e.tags = [tagChoice];
|
|
596
|
+
e.platform = platChoice;
|
|
597
|
+
_pv(e);
|
|
598
|
+
const ok = await _pr(" save? [Y/n]: ", tick);
|
|
599
|
+
if (ok.toLowerCase() === "n") {
|
|
600
|
+
_w(` ${$.d}cancelled.${$.r}\n\n`);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
entries[idx] = e;
|
|
604
|
+
_sv(entries, pw);
|
|
605
|
+
_al("edit", `${e.platform} v${e.version}`);
|
|
606
|
+
_w(`\n ${$.g}✔${$.r} updated.\n\n`);
|
|
607
|
+
}
|
|
608
|
+
async function _ad(pw, tick) {
|
|
609
|
+
const entries = _lo(pw);
|
|
610
|
+
if (!entries.length) {
|
|
611
|
+
_w(`\n ${$.d}nothing to delete.${$.r}\n\n`);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
_w(`\n ${$.c}select entry:${$.r}\n\n`);
|
|
615
|
+
const key = await _mn(entries.map((e, i) => ({
|
|
616
|
+
label: `${_rp(e.platform)} v${e.version} ${$.d}${e.date}${$.r} ${e.title}`,
|
|
617
|
+
key: String(i),
|
|
618
|
+
})).concat([{ label: "cancel", key: "x" }]), tick);
|
|
619
|
+
if (key === "x")
|
|
620
|
+
return;
|
|
621
|
+
const idx = parseInt(key);
|
|
622
|
+
const e = entries[idx];
|
|
623
|
+
const ok = await _pr(`\n delete ${e.platform} v${e.version}? [y/N]: `, tick);
|
|
624
|
+
if (ok.toLowerCase() !== "y") {
|
|
625
|
+
_w(` ${$.d}cancelled.${$.r}\n\n`);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
entries.splice(idx, 1);
|
|
629
|
+
_sv(entries, pw);
|
|
630
|
+
_al("delete", `${e.platform} v${e.version}`);
|
|
631
|
+
_w(`\n ${$.g}✔${$.r} deleted.\n\n`);
|
|
632
|
+
}
|
|
633
|
+
async function _su() {
|
|
634
|
+
_w(`\n ${$.y}first run — set admin password${$.r}\n\n`);
|
|
635
|
+
const p1 = await _ph(" new password: ");
|
|
636
|
+
if (p1.length < 8) {
|
|
637
|
+
_w(`\n ${$.e}min 8 chars.${$.r}\n\n`);
|
|
638
|
+
process.exit(1);
|
|
639
|
+
}
|
|
640
|
+
const p2 = await _ph(" confirm: ");
|
|
641
|
+
if (p1 !== p2) {
|
|
642
|
+
_w(`\n ${$.e}passwords don't match.${$.r}\n\n`);
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
if (!fs.existsSync(_D))
|
|
646
|
+
fs.mkdirSync(_D, { recursive: true });
|
|
647
|
+
fs.writeFileSync(_PF, _hp(p1), "utf8");
|
|
648
|
+
try {
|
|
649
|
+
fs.chmodSync(_PF, 0o600);
|
|
650
|
+
}
|
|
651
|
+
catch { }
|
|
652
|
+
_al("setup", "initialized");
|
|
653
|
+
_w(`\n ${$.g}✔${$.r} password set.\n\n`);
|
|
654
|
+
return p1;
|
|
655
|
+
}
|
|
656
|
+
async function _au() {
|
|
657
|
+
if (!fs.existsSync(_PF))
|
|
658
|
+
return _su();
|
|
659
|
+
_cb();
|
|
660
|
+
const stored = fs.readFileSync(_PF, "utf8").trim();
|
|
661
|
+
const pw = await _ph("\n admin password: ");
|
|
662
|
+
_w(` ${$.d}verifying...${$.r}`);
|
|
663
|
+
const ok = _vp(pw, stored);
|
|
664
|
+
_w("\r \r");
|
|
665
|
+
if (!ok) {
|
|
666
|
+
_fb();
|
|
667
|
+
_al("fail", "wrong password");
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
_xb();
|
|
671
|
+
_al("login", "ok");
|
|
672
|
+
return pw;
|
|
673
|
+
}
|
|
674
|
+
async function adminPanel(existingPw) {
|
|
675
|
+
const pw = existingPw ?? await _au();
|
|
676
|
+
const tick = () => _rt();
|
|
677
|
+
tick();
|
|
678
|
+
while (true) {
|
|
679
|
+
const entries = _lo(pw);
|
|
680
|
+
_box("changelog");
|
|
681
|
+
_w(` ${$.d}timeout: 2 min • entries: ${entries.length}${$.r}\n\n`);
|
|
682
|
+
const choice = await _mn([
|
|
683
|
+
{ label: "add entry", key: "1" },
|
|
684
|
+
{ label: "edit entry", key: "2" },
|
|
685
|
+
{ label: "delete entry", key: "3" },
|
|
686
|
+
{ label: `view all ${$.d}(${entries.length})${$.r}`, key: "4" },
|
|
687
|
+
{ label: "export to markdown", key: "5" },
|
|
688
|
+
{ label: "────────────────────────────────────", key: "_", dim: true },
|
|
689
|
+
{ label: "back", key: "0" },
|
|
690
|
+
], tick);
|
|
691
|
+
tick();
|
|
692
|
+
if (choice === "1")
|
|
693
|
+
await _aa(pw, tick);
|
|
694
|
+
else if (choice === "2")
|
|
695
|
+
await _ae(pw, tick);
|
|
696
|
+
else if (choice === "3")
|
|
697
|
+
await _ad(pw, tick);
|
|
698
|
+
else if (choice === "4") {
|
|
699
|
+
_box("all entries");
|
|
700
|
+
await _pg(entries, tick);
|
|
701
|
+
}
|
|
702
|
+
else if (choice === "5") {
|
|
703
|
+
const out = await _pr(" save to (blank = ~/secry-changelog.md): ", tick);
|
|
704
|
+
_ex(out);
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
_st();
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
async function cmdChangelog(parsed) {
|
|
713
|
+
const f = parsed.flags ?? {};
|
|
714
|
+
const isAdmin = "admin" in f;
|
|
715
|
+
const isExp = "export" in f;
|
|
716
|
+
const tag = f["tag"];
|
|
717
|
+
const plat = f["platform"];
|
|
718
|
+
const search = f["search"];
|
|
719
|
+
const out = typeof f["export"] === "string" ? f["export"] : "";
|
|
720
|
+
if (isAdmin)
|
|
721
|
+
await adminPanel();
|
|
722
|
+
else if (isExp)
|
|
723
|
+
_ex(out);
|
|
724
|
+
else
|
|
725
|
+
_vi(tag, plat, search);
|
|
726
|
+
}
|