@tostudy-ai/cli 0.16.2 → 0.16.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.js +340 -137
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
41
41
|
mod
|
|
42
42
|
));
|
|
43
43
|
|
|
44
|
-
// ../../node_modules/@parisgroup-ai/logger/dist/sanitize-
|
|
44
|
+
// ../../node_modules/@parisgroup-ai/logger/dist/sanitize-DSkXy3jL.mjs
|
|
45
45
|
function isSensitiveKey(key) {
|
|
46
46
|
if (matchesSensitiveField(key)) return true;
|
|
47
47
|
return matchesPluralOfSensitiveField(key);
|
|
@@ -114,11 +114,12 @@ function sanitizeRecord(obj, ctx) {
|
|
|
114
114
|
if (ctx.visited.has(obj)) return "[Circular]";
|
|
115
115
|
ctx.visited.add(obj);
|
|
116
116
|
const sanitized = { ...obj };
|
|
117
|
+
const mask = ctx.redactMask ?? "[REDACTED]";
|
|
117
118
|
for (const [key, value] of Object.entries(sanitized)) {
|
|
118
119
|
const isAdditionalKey = ctx.additionalSensitiveKeys?.some((k) => key.toLowerCase() === k.toLowerCase());
|
|
119
120
|
const pluralCountExemption = typeof value === "number" && !isAdditionalKey && !matchesSensitiveField(key) && matchesPluralOfSensitiveField(key);
|
|
120
121
|
if ((isSensitiveKey(key) || isAdditionalKey) && !pluralCountExemption) {
|
|
121
|
-
sanitized[key] =
|
|
122
|
+
sanitized[key] = mask;
|
|
122
123
|
continue;
|
|
123
124
|
}
|
|
124
125
|
if (value instanceof Error) {
|
|
@@ -139,16 +140,17 @@ function sanitizeRecord(obj, ctx) {
|
|
|
139
140
|
ctx.visited.delete(obj);
|
|
140
141
|
return sanitized;
|
|
141
142
|
}
|
|
142
|
-
function sanitizeEntryData(obj, additionalSensitiveKeys, includeStack) {
|
|
143
|
+
function sanitizeEntryData(obj, additionalSensitiveKeys, includeStack, redactMask) {
|
|
143
144
|
return sanitizeRecord(obj, {
|
|
144
145
|
additionalSensitiveKeys,
|
|
145
146
|
includeStack,
|
|
146
|
-
visited: /* @__PURE__ */ new WeakSet()
|
|
147
|
+
visited: /* @__PURE__ */ new WeakSet(),
|
|
148
|
+
redactMask
|
|
147
149
|
});
|
|
148
150
|
}
|
|
149
151
|
var SENSITIVE_FIELDS;
|
|
150
|
-
var
|
|
151
|
-
"../../node_modules/@parisgroup-ai/logger/dist/sanitize-
|
|
152
|
+
var init_sanitize_DSkXy3jL = __esm({
|
|
153
|
+
"../../node_modules/@parisgroup-ai/logger/dist/sanitize-DSkXy3jL.mjs"() {
|
|
152
154
|
SENSITIVE_FIELDS = [
|
|
153
155
|
"password",
|
|
154
156
|
"senha",
|
|
@@ -279,7 +281,7 @@ var require_picocolors = __commonJS({
|
|
|
279
281
|
}
|
|
280
282
|
});
|
|
281
283
|
|
|
282
|
-
// ../../node_modules/@parisgroup-ai/logger/dist/logger.base-
|
|
284
|
+
// ../../node_modules/@parisgroup-ai/logger/dist/logger.base-DkbC4pBB.mjs
|
|
283
285
|
function formatJson(entry) {
|
|
284
286
|
const { timestamp, level, message, service, environment, context, data, error: error49 } = entry;
|
|
285
287
|
const output3 = {
|
|
@@ -383,9 +385,9 @@ function extractErrorInfo(error49, includeStack, visited = /* @__PURE__ */ new W
|
|
|
383
385
|
return info;
|
|
384
386
|
}
|
|
385
387
|
var import_picocolors, LOG_LEVELS, LEVEL_COLORS, LEVEL_WIDTH, BaseLogger;
|
|
386
|
-
var
|
|
387
|
-
"../../node_modules/@parisgroup-ai/logger/dist/logger.base-
|
|
388
|
-
|
|
388
|
+
var init_logger_base_DkbC4pBB = __esm({
|
|
389
|
+
"../../node_modules/@parisgroup-ai/logger/dist/logger.base-DkbC4pBB.mjs"() {
|
|
390
|
+
init_sanitize_DSkXy3jL();
|
|
389
391
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
390
392
|
LOG_LEVELS = {
|
|
391
393
|
trace: 0,
|
|
@@ -414,6 +416,8 @@ var init_logger_base_DOZPgsDx = __esm({
|
|
|
414
416
|
transports;
|
|
415
417
|
shouldSanitize;
|
|
416
418
|
sensitiveFields;
|
|
419
|
+
redactMask;
|
|
420
|
+
sampleRate;
|
|
417
421
|
includeCaller;
|
|
418
422
|
piiFilter;
|
|
419
423
|
constructor(options, defaults) {
|
|
@@ -426,6 +430,8 @@ var init_logger_base_DOZPgsDx = __esm({
|
|
|
426
430
|
this.transports = [];
|
|
427
431
|
this.shouldSanitize = options.sanitize ?? true;
|
|
428
432
|
this.sensitiveFields = options.sensitiveFields;
|
|
433
|
+
this.redactMask = options.redactMask;
|
|
434
|
+
this.sampleRate = options.sampleRate;
|
|
429
435
|
this.includeCaller = options.includeCaller ?? "errors";
|
|
430
436
|
this.piiFilter = options.piiFilter;
|
|
431
437
|
}
|
|
@@ -440,14 +446,19 @@ var init_logger_base_DOZPgsDx = __esm({
|
|
|
440
446
|
flush() {
|
|
441
447
|
}
|
|
442
448
|
shouldLog(level) {
|
|
443
|
-
|
|
449
|
+
if (LOG_LEVELS[level] < LOG_LEVELS[this.level]) return false;
|
|
450
|
+
if (this.sampleRate !== void 0 && (level === "trace" || level === "debug" || level === "info")) {
|
|
451
|
+
if (this.sampleRate <= 0) return false;
|
|
452
|
+
if (this.sampleRate < 1 && Math.random() > this.sampleRate) return false;
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
444
455
|
}
|
|
445
456
|
buildEntry(level, message, data, error49) {
|
|
446
457
|
const merged = {
|
|
447
458
|
...this.getLogContext() ?? {},
|
|
448
459
|
...this.additionalContext
|
|
449
460
|
};
|
|
450
|
-
const context = this.shouldSanitize ? sanitizeEntryData(merged, this.sensitiveFields, !this.isProd()) : merged;
|
|
461
|
+
const context = this.shouldSanitize ? sanitizeEntryData(merged, this.sensitiveFields, !this.isProd(), this.redactMask) : merged;
|
|
451
462
|
if (this.module) context.module = this.module;
|
|
452
463
|
if (this.includeCaller === true || this.includeCaller === "errors" && (level === "error" || level === "fatal")) {
|
|
453
464
|
const caller = this.getCaller();
|
|
@@ -478,11 +489,11 @@ var init_logger_base_DOZPgsDx = __esm({
|
|
|
478
489
|
if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") return { value: data };
|
|
479
490
|
if (Array.isArray(data)) {
|
|
480
491
|
const wrapped = { items: data };
|
|
481
|
-
return this.shouldSanitize ? sanitizeEntryData(wrapped, this.sensitiveFields, includeStack) : wrapped;
|
|
492
|
+
return this.shouldSanitize ? sanitizeEntryData(wrapped, this.sensitiveFields, includeStack, this.redactMask) : wrapped;
|
|
482
493
|
}
|
|
483
494
|
if (typeof data === "object") {
|
|
484
495
|
const obj = data;
|
|
485
|
-
return this.shouldSanitize ? sanitizeEntryData(obj, this.sensitiveFields, includeStack) : obj;
|
|
496
|
+
return this.shouldSanitize ? sanitizeEntryData(obj, this.sensitiveFields, includeStack, this.redactMask) : obj;
|
|
486
497
|
}
|
|
487
498
|
return { value: String(data) };
|
|
488
499
|
}
|
|
@@ -590,16 +601,60 @@ import { join } from "node:path";
|
|
|
590
601
|
function getLogContext() {
|
|
591
602
|
return asyncLocalStorage.getStore();
|
|
592
603
|
}
|
|
593
|
-
function
|
|
594
|
-
|
|
604
|
+
function getOrCreateGlobalSingleton(key, factory) {
|
|
605
|
+
const slot = globalThis;
|
|
606
|
+
const existing = slot[key];
|
|
607
|
+
if (existing) return existing;
|
|
608
|
+
const created = factory();
|
|
609
|
+
slot[key] = created;
|
|
610
|
+
return created;
|
|
595
611
|
}
|
|
596
612
|
function getOrCreateHttpTransport(options) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return
|
|
613
|
+
return getOrCreateGlobalSingleton(SINGLETON_KEY$1, () => new HttpTransport(options));
|
|
614
|
+
}
|
|
615
|
+
function toAttributeValue(value) {
|
|
616
|
+
if (typeof value === "boolean") return { boolValue: value };
|
|
617
|
+
if (typeof value === "number") return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
|
|
618
|
+
if (value == null) return { stringValue: "" };
|
|
619
|
+
if (typeof value === "object") try {
|
|
620
|
+
return { stringValue: JSON.stringify(value) };
|
|
621
|
+
} catch {
|
|
622
|
+
return { stringValue: String(value) };
|
|
623
|
+
}
|
|
624
|
+
return { stringValue: String(value) };
|
|
625
|
+
}
|
|
626
|
+
function toAttributes(record2) {
|
|
627
|
+
return Object.entries(record2).map(([key, value]) => ({
|
|
628
|
+
key,
|
|
629
|
+
value: toAttributeValue(value)
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
632
|
+
function toLogRecord(entry) {
|
|
633
|
+
const { number: number4, text } = SEVERITY[entry.level] ?? SEVERITY.info;
|
|
634
|
+
const attributes = {
|
|
635
|
+
...entry.context,
|
|
636
|
+
...entry.data
|
|
637
|
+
};
|
|
638
|
+
return {
|
|
639
|
+
timeUnixNano: String(BigInt(Date.parse(entry.timestamp)) * 1000000n),
|
|
640
|
+
severityNumber: number4,
|
|
641
|
+
severityText: text,
|
|
642
|
+
body: { stringValue: entry.message },
|
|
643
|
+
attributes: toAttributes(attributes)
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function getOrCreateOtlpLogsTransport(options) {
|
|
647
|
+
return getOrCreateGlobalSingleton(SINGLETON_KEY, () => new OtlpLogsTransport(options));
|
|
648
|
+
}
|
|
649
|
+
function parseOtlpResourceAttributes(raw) {
|
|
650
|
+
if (!raw) return {};
|
|
651
|
+
const out = {};
|
|
652
|
+
for (const pair of raw.split(",")) {
|
|
653
|
+
const eq = pair.indexOf("=");
|
|
654
|
+
if (eq <= 0) continue;
|
|
655
|
+
out[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
|
|
656
|
+
}
|
|
657
|
+
return out;
|
|
603
658
|
}
|
|
604
659
|
function detectEnvironment() {
|
|
605
660
|
const isProd = process.env.NODE_ENV === "production" || !!process.env.VERCEL || !!process.env.RAILWAY_ENVIRONMENT;
|
|
@@ -650,11 +705,11 @@ function createLogger(module, options) {
|
|
|
650
705
|
module
|
|
651
706
|
});
|
|
652
707
|
}
|
|
653
|
-
var asyncLocalStorage, ConsoleTransport, FileTransport, HttpTransport, SINGLETON_KEY, Logger, logger;
|
|
708
|
+
var asyncLocalStorage, ConsoleTransport, FileTransport, HttpTransport, SINGLETON_KEY$1, SEVERITY, OtlpLogsTransport, SINGLETON_KEY, Logger, logger;
|
|
654
709
|
var init_dist = __esm({
|
|
655
710
|
"../../node_modules/@parisgroup-ai/logger/dist/index.mjs"() {
|
|
656
|
-
|
|
657
|
-
|
|
711
|
+
init_logger_base_DkbC4pBB();
|
|
712
|
+
init_sanitize_DSkXy3jL();
|
|
658
713
|
asyncLocalStorage = new AsyncLocalStorage();
|
|
659
714
|
ConsoleTransport = class {
|
|
660
715
|
log(entry, formatted) {
|
|
@@ -758,7 +813,113 @@ var init_dist = __esm({
|
|
|
758
813
|
});
|
|
759
814
|
}
|
|
760
815
|
};
|
|
761
|
-
SINGLETON_KEY = /* @__PURE__ */ Symbol.for("@repo/logger.sharedHttpTransport");
|
|
816
|
+
SINGLETON_KEY$1 = /* @__PURE__ */ Symbol.for("@repo/logger.sharedHttpTransport");
|
|
817
|
+
SEVERITY = {
|
|
818
|
+
trace: {
|
|
819
|
+
number: 1,
|
|
820
|
+
text: "TRACE"
|
|
821
|
+
},
|
|
822
|
+
debug: {
|
|
823
|
+
number: 5,
|
|
824
|
+
text: "DEBUG"
|
|
825
|
+
},
|
|
826
|
+
info: {
|
|
827
|
+
number: 9,
|
|
828
|
+
text: "INFO"
|
|
829
|
+
},
|
|
830
|
+
warn: {
|
|
831
|
+
number: 13,
|
|
832
|
+
text: "WARN"
|
|
833
|
+
},
|
|
834
|
+
error: {
|
|
835
|
+
number: 17,
|
|
836
|
+
text: "ERROR"
|
|
837
|
+
},
|
|
838
|
+
fatal: {
|
|
839
|
+
number: 21,
|
|
840
|
+
text: "FATAL"
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
OtlpLogsTransport = class {
|
|
844
|
+
buffer = [];
|
|
845
|
+
timer = null;
|
|
846
|
+
flushing = false;
|
|
847
|
+
disposed = false;
|
|
848
|
+
url;
|
|
849
|
+
serviceName;
|
|
850
|
+
resourceAttributes;
|
|
851
|
+
extraHeaders;
|
|
852
|
+
maxBufferSize;
|
|
853
|
+
flushInterval;
|
|
854
|
+
onError;
|
|
855
|
+
constructor(options) {
|
|
856
|
+
this.url = `${options.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
857
|
+
this.serviceName = options.serviceName;
|
|
858
|
+
this.resourceAttributes = options.resourceAttributes ?? {};
|
|
859
|
+
this.extraHeaders = options.headers ?? {};
|
|
860
|
+
this.maxBufferSize = options.maxBufferSize ?? 50;
|
|
861
|
+
this.flushInterval = options.flushInterval ?? 5e3;
|
|
862
|
+
this.onError = options.onError;
|
|
863
|
+
this.timer = setInterval(() => {
|
|
864
|
+
this.flush().catch(() => {
|
|
865
|
+
});
|
|
866
|
+
}, this.flushInterval);
|
|
867
|
+
process.once("beforeExit", () => this.dispose());
|
|
868
|
+
process.once("SIGTERM", () => this.dispose());
|
|
869
|
+
}
|
|
870
|
+
log(entry, _formatted) {
|
|
871
|
+
if (this.disposed) return;
|
|
872
|
+
this.buffer.push(entry);
|
|
873
|
+
if (this.buffer.length >= this.maxBufferSize) this.flush().catch(() => {
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
serialize(batch) {
|
|
877
|
+
const resourceAttrs = {
|
|
878
|
+
"service.name": this.serviceName,
|
|
879
|
+
...this.resourceAttributes
|
|
880
|
+
};
|
|
881
|
+
return JSON.stringify({ resourceLogs: [{
|
|
882
|
+
resource: { attributes: toAttributes(resourceAttrs) },
|
|
883
|
+
scopeLogs: [{
|
|
884
|
+
scope: { name: "@parisgroup-ai/logger" },
|
|
885
|
+
logRecords: batch.map(toLogRecord)
|
|
886
|
+
}]
|
|
887
|
+
}] });
|
|
888
|
+
}
|
|
889
|
+
async flush() {
|
|
890
|
+
if (this.flushing || this.buffer.length === 0) return;
|
|
891
|
+
this.flushing = true;
|
|
892
|
+
const batch = this.buffer.splice(0);
|
|
893
|
+
try {
|
|
894
|
+
const response = await fetch(this.url, {
|
|
895
|
+
method: "POST",
|
|
896
|
+
headers: {
|
|
897
|
+
"Content-Type": "application/json",
|
|
898
|
+
...this.extraHeaders
|
|
899
|
+
},
|
|
900
|
+
body: this.serialize(batch),
|
|
901
|
+
signal: AbortSignal.timeout(1e4)
|
|
902
|
+
});
|
|
903
|
+
if (!response.ok) throw new Error(`OTLP log ingest failed: ${response.status} ${response.statusText}`);
|
|
904
|
+
} catch (error49) {
|
|
905
|
+
if (this.buffer.length < this.maxBufferSize * 3) this.buffer.unshift(...batch);
|
|
906
|
+
this.onError?.(error49, batch.length);
|
|
907
|
+
} finally {
|
|
908
|
+
this.flushing = false;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
dispose() {
|
|
912
|
+
if (this.disposed) return;
|
|
913
|
+
this.disposed = true;
|
|
914
|
+
if (this.timer) {
|
|
915
|
+
clearInterval(this.timer);
|
|
916
|
+
this.timer = null;
|
|
917
|
+
}
|
|
918
|
+
if (this.buffer.length > 0) this.flush().catch(() => {
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
SINGLETON_KEY = /* @__PURE__ */ Symbol.for("@repo/logger.sharedOtlpLogsTransport");
|
|
762
923
|
Logger = class Logger2 extends BaseLogger {
|
|
763
924
|
fileTransport;
|
|
764
925
|
_isProd;
|
|
@@ -783,6 +944,16 @@ var init_dist = __esm({
|
|
|
783
944
|
authorization: `Bearer ${drainToken}`,
|
|
784
945
|
onError: (err, count) => {
|
|
785
946
|
process.stderr.write(`[logger] HTTP transport error: ${err.message} (${count} entries)
|
|
947
|
+
`);
|
|
948
|
+
}
|
|
949
|
+
}));
|
|
950
|
+
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
951
|
+
if (otlpEndpoint) this.transports.push(getOrCreateOtlpLogsTransport({
|
|
952
|
+
endpoint: otlpEndpoint,
|
|
953
|
+
serviceName: process.env.OTEL_SERVICE_NAME || getDefaultService(),
|
|
954
|
+
resourceAttributes: parseOtlpResourceAttributes(process.env.OTEL_RESOURCE_ATTRIBUTES),
|
|
955
|
+
onError: (err, count) => {
|
|
956
|
+
process.stderr.write(`[logger] OTLP transport error: ${err.message} (${count} entries)
|
|
786
957
|
`);
|
|
787
958
|
}
|
|
788
959
|
}));
|
|
@@ -812,6 +983,8 @@ var init_dist = __esm({
|
|
|
812
983
|
environment: this.environment,
|
|
813
984
|
sanitize: this.shouldSanitize,
|
|
814
985
|
sensitiveFields: this.sensitiveFields,
|
|
986
|
+
redactMask: this.redactMask,
|
|
987
|
+
sampleRate: this.sampleRate,
|
|
815
988
|
includeCaller: this.includeCaller,
|
|
816
989
|
piiFilter: this.piiFilter
|
|
817
990
|
});
|
|
@@ -1414,7 +1587,7 @@ function formatCourseList(courses) {
|
|
|
1414
1587
|
return [
|
|
1415
1588
|
"Nenhum curso encontrado.",
|
|
1416
1589
|
"",
|
|
1417
|
-
"\u2192 tostudy
|
|
1590
|
+
"\u2192 Acesse tostudy.ai para se matricular em um curso"
|
|
1418
1591
|
].join("\n");
|
|
1419
1592
|
}
|
|
1420
1593
|
const lines = ["Seus cursos:", ""];
|
|
@@ -2578,6 +2751,101 @@ var init_user_profile = __esm({
|
|
|
2578
2751
|
}
|
|
2579
2752
|
});
|
|
2580
2753
|
|
|
2754
|
+
// src/errors/errors-pt-br.ts
|
|
2755
|
+
var ptBrErrors;
|
|
2756
|
+
var init_errors_pt_br = __esm({
|
|
2757
|
+
"src/errors/errors-pt-br.ts"() {
|
|
2758
|
+
"use strict";
|
|
2759
|
+
ptBrErrors = {
|
|
2760
|
+
notFound: "n\xE3o encontrado",
|
|
2761
|
+
workspaceNotFoundShort: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' primeiro.\n",
|
|
2762
|
+
workspaceNotFoundFromExport: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' ou rode 'tostudy select' desta pasta.\n",
|
|
2763
|
+
vaultNotFound: "\u274C Vault n\xE3o encontrado. Execute 'tostudy vault init' primeiro.\n",
|
|
2764
|
+
logoutSuccess: "\n Deslogado com sucesso.\n",
|
|
2765
|
+
workspaceCommandDescription: "Gerenciar workspace de estudo local",
|
|
2766
|
+
workspaceSetupDescription: "Criar estrutura do workspace para o curso ativo",
|
|
2767
|
+
insufficientCredits: "\u274C Voc\xEA est\xE1 sem cr\xE9ditos. Cada resposta do tutor de IA consome cr\xE9ditos para cobrir o processamento. Recarregue em https://tostudy.ai/student/credits para continuar de onde parou.\n",
|
|
2768
|
+
workspaceRefusedAtHome: "N\xE3o d\xE1 para configurar o estudo direto na sua pasta pessoal.\nCrie uma pasta para o curso e entre nela antes de continuar:\n\n mkdir ~/meus-cursos && cd ~/meus-cursos\n",
|
|
2769
|
+
courseArchived: "Este curso foi arquivado e est\xE1 dispon\xEDvel somente para leitura.\nUse `tostudy lesson` para revisar o conte\xFAdo j\xE1 estudado.\n",
|
|
2770
|
+
enrollmentNotEntitled: "\u{1F6AB} Sua matr\xEDcula neste curso n\xE3o est\xE1 mais ativa (reembolso, cancelamento ou preview expirado).\nPara voltar a estudar, adquira o curso novamente em https://tostudy.ai.\n"
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
});
|
|
2774
|
+
|
|
2775
|
+
// src/errors/errors-en-us.ts
|
|
2776
|
+
var enUsErrors;
|
|
2777
|
+
var init_errors_en_us = __esm({
|
|
2778
|
+
"src/errors/errors-en-us.ts"() {
|
|
2779
|
+
"use strict";
|
|
2780
|
+
enUsErrors = {
|
|
2781
|
+
notFound: "not found",
|
|
2782
|
+
workspaceNotFoundShort: "\u274C Workspace not found. Run 'tostudy workspace setup' first.\n",
|
|
2783
|
+
workspaceNotFoundFromExport: "\u274C Workspace not found. Run 'tostudy workspace setup' or 'tostudy select' from this folder.\n",
|
|
2784
|
+
vaultNotFound: "\u274C Vault not found. Run 'tostudy vault init' first.\n",
|
|
2785
|
+
logoutSuccess: "\n Logged out successfully.\n",
|
|
2786
|
+
workspaceCommandDescription: "Manage local study workspace",
|
|
2787
|
+
workspaceSetupDescription: "Create the workspace structure for the active course",
|
|
2788
|
+
insufficientCredits: "\u274C You're out of credits. Every reply from the AI tutor uses credits to cover processing. Top up at https://tostudy.ai/student/credits to continue from where you stopped.\n",
|
|
2789
|
+
workspaceRefusedAtHome: "Study can't be set up directly in your home folder.\nCreate a folder for the course and switch into it first:\n\n mkdir ~/my-courses && cd ~/my-courses\n",
|
|
2790
|
+
courseArchived: "This course has been archived and is available for reading only.\nUse `tostudy lesson` to review the content you've already studied.\n",
|
|
2791
|
+
enrollmentNotEntitled: "\u{1F6AB} Your enrollment in this course is no longer active (refund, cancellation, or expired preview).\nTo study again, purchase the course at https://tostudy.ai.\n"
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
});
|
|
2795
|
+
|
|
2796
|
+
// src/errors/index.ts
|
|
2797
|
+
function resolveLocale() {
|
|
2798
|
+
if (_cachedLocale !== null) return _cachedLocale;
|
|
2799
|
+
const argv = process.argv;
|
|
2800
|
+
const flagIdx = argv.indexOf("--locale");
|
|
2801
|
+
if (flagIdx !== -1 && flagIdx + 1 < argv.length) {
|
|
2802
|
+
const val = argv[flagIdx + 1];
|
|
2803
|
+
if (val === "pt-BR" || val === "en-US") {
|
|
2804
|
+
_cachedLocale = val;
|
|
2805
|
+
return val;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
const lang = (process.env["LANG"] ?? "").toLowerCase();
|
|
2809
|
+
if (lang.startsWith("en")) {
|
|
2810
|
+
_cachedLocale = "en-US";
|
|
2811
|
+
return "en-US";
|
|
2812
|
+
}
|
|
2813
|
+
_cachedLocale = "pt-BR";
|
|
2814
|
+
return "pt-BR";
|
|
2815
|
+
}
|
|
2816
|
+
function getErrors(locale) {
|
|
2817
|
+
return BUNDLES[locale ?? resolveLocale()];
|
|
2818
|
+
}
|
|
2819
|
+
function resolveErrorCode(err) {
|
|
2820
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
2821
|
+
const code = err.code;
|
|
2822
|
+
if (typeof code === "string" && code.length > 0) return code;
|
|
2823
|
+
}
|
|
2824
|
+
return err instanceof Error ? err.message : String(err);
|
|
2825
|
+
}
|
|
2826
|
+
function isInsufficientCreditsError(err) {
|
|
2827
|
+
return resolveErrorCode(err).includes("INSUFFICIENT_CREDITS");
|
|
2828
|
+
}
|
|
2829
|
+
function isCourseArchivedError(err) {
|
|
2830
|
+
return resolveErrorCode(err).includes("COURSE_ARCHIVED");
|
|
2831
|
+
}
|
|
2832
|
+
function isEnrollmentNotEntitledError(err) {
|
|
2833
|
+
return resolveErrorCode(err).includes("ENROLLMENT_NOT_ENTITLED");
|
|
2834
|
+
}
|
|
2835
|
+
var BUNDLES, _cachedLocale;
|
|
2836
|
+
var init_errors = __esm({
|
|
2837
|
+
"src/errors/index.ts"() {
|
|
2838
|
+
"use strict";
|
|
2839
|
+
init_errors_pt_br();
|
|
2840
|
+
init_errors_en_us();
|
|
2841
|
+
BUNDLES = {
|
|
2842
|
+
"pt-BR": ptBrErrors,
|
|
2843
|
+
"en-US": enUsErrors
|
|
2844
|
+
};
|
|
2845
|
+
_cachedLocale = null;
|
|
2846
|
+
}
|
|
2847
|
+
});
|
|
2848
|
+
|
|
2581
2849
|
// src/workspace/workspace-marker.ts
|
|
2582
2850
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
2583
2851
|
import os4 from "node:os";
|
|
@@ -2607,9 +2875,7 @@ async function writeWorkspaceMarker(workspacePath, input2, options = {}) {
|
|
|
2607
2875
|
const homeDir = path7.resolve(options.homeDir ?? os4.homedir());
|
|
2608
2876
|
const resolvedWorkspace = path7.resolve(workspacePath);
|
|
2609
2877
|
if (resolvedWorkspace === homeDir || homeDir.startsWith(resolvedWorkspace + path7.sep)) {
|
|
2610
|
-
throw new Error(
|
|
2611
|
-
`Refusing to write workspace marker at ${workspacePath}: it is the home directory or an ancestor of it.`
|
|
2612
|
-
);
|
|
2878
|
+
throw new Error(getErrors().workspaceRefusedAtHome);
|
|
2613
2879
|
}
|
|
2614
2880
|
const filePath = resolveMarkerPath(workspacePath);
|
|
2615
2881
|
const dir = path7.dirname(filePath);
|
|
@@ -2634,6 +2900,7 @@ var CURRENT_VERSION, SUPPORTED_VERSIONS;
|
|
|
2634
2900
|
var init_workspace_marker = __esm({
|
|
2635
2901
|
"src/workspace/workspace-marker.ts"() {
|
|
2636
2902
|
"use strict";
|
|
2903
|
+
init_errors();
|
|
2637
2904
|
CURRENT_VERSION = 1;
|
|
2638
2905
|
SUPPORTED_VERSIONS = /* @__PURE__ */ new Set([1, 2]);
|
|
2639
2906
|
}
|
|
@@ -2785,99 +3052,6 @@ var init_course_state = __esm({
|
|
|
2785
3052
|
}
|
|
2786
3053
|
});
|
|
2787
3054
|
|
|
2788
|
-
// src/errors/errors-pt-br.ts
|
|
2789
|
-
var ptBrErrors;
|
|
2790
|
-
var init_errors_pt_br = __esm({
|
|
2791
|
-
"src/errors/errors-pt-br.ts"() {
|
|
2792
|
-
"use strict";
|
|
2793
|
-
ptBrErrors = {
|
|
2794
|
-
notFound: "n\xE3o encontrado",
|
|
2795
|
-
workspaceNotFoundShort: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' primeiro.\n",
|
|
2796
|
-
workspaceNotFoundFromExport: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' ou rode 'tostudy select' desta pasta.\n",
|
|
2797
|
-
vaultNotFound: "\u274C Vault n\xE3o encontrado. Execute 'tostudy vault init' primeiro.\n",
|
|
2798
|
-
logoutSuccess: "\n Deslogado com sucesso.\n",
|
|
2799
|
-
workspaceCommandDescription: "Gerenciar workspace de estudo local",
|
|
2800
|
-
workspaceSetupDescription: "Criar estrutura do workspace para o curso ativo",
|
|
2801
|
-
insufficientCredits: "\u274C Voc\xEA est\xE1 sem cr\xE9ditos. Cada resposta do tutor de IA consome cr\xE9ditos para cobrir o processamento. Recarregue em https://tostudy.ai/student/credits para continuar de onde parou.\n",
|
|
2802
|
-
courseArchived: "Este curso foi arquivado e est\xE1 dispon\xEDvel somente para leitura.\nUse `tostudy lesson` para revisar o conte\xFAdo j\xE1 estudado.\n",
|
|
2803
|
-
enrollmentNotEntitled: "\u{1F6AB} Sua matr\xEDcula neste curso n\xE3o est\xE1 mais ativa (reembolso, cancelamento ou preview expirado).\nPara voltar a estudar, adquira o curso novamente em https://tostudy.ai.\n"
|
|
2804
|
-
};
|
|
2805
|
-
}
|
|
2806
|
-
});
|
|
2807
|
-
|
|
2808
|
-
// src/errors/errors-en-us.ts
|
|
2809
|
-
var enUsErrors;
|
|
2810
|
-
var init_errors_en_us = __esm({
|
|
2811
|
-
"src/errors/errors-en-us.ts"() {
|
|
2812
|
-
"use strict";
|
|
2813
|
-
enUsErrors = {
|
|
2814
|
-
notFound: "not found",
|
|
2815
|
-
workspaceNotFoundShort: "\u274C Workspace not found. Run 'tostudy workspace setup' first.\n",
|
|
2816
|
-
workspaceNotFoundFromExport: "\u274C Workspace not found. Run 'tostudy workspace setup' or 'tostudy select' from this folder.\n",
|
|
2817
|
-
vaultNotFound: "\u274C Vault not found. Run 'tostudy vault init' first.\n",
|
|
2818
|
-
logoutSuccess: "\n Logged out successfully.\n",
|
|
2819
|
-
workspaceCommandDescription: "Manage local study workspace",
|
|
2820
|
-
workspaceSetupDescription: "Create the workspace structure for the active course",
|
|
2821
|
-
insufficientCredits: "\u274C You're out of credits. Every reply from the AI tutor uses credits to cover processing. Top up at https://tostudy.ai/student/credits to continue from where you stopped.\n",
|
|
2822
|
-
courseArchived: "This course has been archived and is available for reading only.\nUse `tostudy lesson` to review the content you've already studied.\n",
|
|
2823
|
-
enrollmentNotEntitled: "\u{1F6AB} Your enrollment in this course is no longer active (refund, cancellation, or expired preview).\nTo study again, purchase the course at https://tostudy.ai.\n"
|
|
2824
|
-
};
|
|
2825
|
-
}
|
|
2826
|
-
});
|
|
2827
|
-
|
|
2828
|
-
// src/errors/index.ts
|
|
2829
|
-
function resolveLocale() {
|
|
2830
|
-
if (_cachedLocale !== null) return _cachedLocale;
|
|
2831
|
-
const argv = process.argv;
|
|
2832
|
-
const flagIdx = argv.indexOf("--locale");
|
|
2833
|
-
if (flagIdx !== -1 && flagIdx + 1 < argv.length) {
|
|
2834
|
-
const val = argv[flagIdx + 1];
|
|
2835
|
-
if (val === "pt-BR" || val === "en-US") {
|
|
2836
|
-
_cachedLocale = val;
|
|
2837
|
-
return val;
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
const lang = (process.env["LANG"] ?? "").toLowerCase();
|
|
2841
|
-
if (lang.startsWith("en")) {
|
|
2842
|
-
_cachedLocale = "en-US";
|
|
2843
|
-
return "en-US";
|
|
2844
|
-
}
|
|
2845
|
-
_cachedLocale = "pt-BR";
|
|
2846
|
-
return "pt-BR";
|
|
2847
|
-
}
|
|
2848
|
-
function getErrors(locale) {
|
|
2849
|
-
return BUNDLES[locale ?? resolveLocale()];
|
|
2850
|
-
}
|
|
2851
|
-
function resolveErrorCode(err) {
|
|
2852
|
-
if (err && typeof err === "object" && "code" in err) {
|
|
2853
|
-
const code = err.code;
|
|
2854
|
-
if (typeof code === "string" && code.length > 0) return code;
|
|
2855
|
-
}
|
|
2856
|
-
return err instanceof Error ? err.message : String(err);
|
|
2857
|
-
}
|
|
2858
|
-
function isInsufficientCreditsError(err) {
|
|
2859
|
-
return resolveErrorCode(err).includes("INSUFFICIENT_CREDITS");
|
|
2860
|
-
}
|
|
2861
|
-
function isCourseArchivedError(err) {
|
|
2862
|
-
return resolveErrorCode(err).includes("COURSE_ARCHIVED");
|
|
2863
|
-
}
|
|
2864
|
-
function isEnrollmentNotEntitledError(err) {
|
|
2865
|
-
return resolveErrorCode(err).includes("ENROLLMENT_NOT_ENTITLED");
|
|
2866
|
-
}
|
|
2867
|
-
var BUNDLES, _cachedLocale;
|
|
2868
|
-
var init_errors = __esm({
|
|
2869
|
-
"src/errors/index.ts"() {
|
|
2870
|
-
"use strict";
|
|
2871
|
-
init_errors_pt_br();
|
|
2872
|
-
init_errors_en_us();
|
|
2873
|
-
BUNDLES = {
|
|
2874
|
-
"pt-BR": ptBrErrors,
|
|
2875
|
-
"en-US": enUsErrors
|
|
2876
|
-
};
|
|
2877
|
-
_cachedLocale = null;
|
|
2878
|
-
}
|
|
2879
|
-
});
|
|
2880
|
-
|
|
2881
3055
|
// src/commands/logout.ts
|
|
2882
3056
|
import { Command as Command2 } from "commander";
|
|
2883
3057
|
var logoutCommand;
|
|
@@ -2913,9 +3087,6 @@ __export(resolve_exports, {
|
|
|
2913
3087
|
import fs7 from "node:fs/promises";
|
|
2914
3088
|
import path9 from "node:path";
|
|
2915
3089
|
import os6 from "node:os";
|
|
2916
|
-
function courseSlug(title) {
|
|
2917
|
-
return title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
2918
|
-
}
|
|
2919
3090
|
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
2920
3091
|
const slug = courseSlug(courseTitle);
|
|
2921
3092
|
const candidate = path9.join(basePath, slug);
|
|
@@ -3001,11 +3172,13 @@ async function resolveActiveCourseFromWorkspace(options = {}) {
|
|
|
3001
3172
|
const result = await findWorkspaceState2(options.cwd, options.homeDir);
|
|
3002
3173
|
return result?.state ?? null;
|
|
3003
3174
|
}
|
|
3004
|
-
var DEFAULT_BASE, resolveActiveCourseWithOverride;
|
|
3175
|
+
var DEFAULT_BASE, courseSlug, resolveActiveCourseWithOverride;
|
|
3005
3176
|
var init_resolve = __esm({
|
|
3006
3177
|
"src/workspace/resolve.ts"() {
|
|
3007
3178
|
"use strict";
|
|
3179
|
+
init_slug();
|
|
3008
3180
|
DEFAULT_BASE = path9.join(os6.homedir(), "study");
|
|
3181
|
+
courseSlug = slugify;
|
|
3009
3182
|
resolveActiveCourseWithOverride = resolveActiveCourseFromWorkspace;
|
|
3010
3183
|
}
|
|
3011
3184
|
});
|
|
@@ -3197,7 +3370,7 @@ var CLI_VERSION;
|
|
|
3197
3370
|
var init_version = __esm({
|
|
3198
3371
|
"src/version.ts"() {
|
|
3199
3372
|
"use strict";
|
|
3200
|
-
CLI_VERSION = true ? "0.16.
|
|
3373
|
+
CLI_VERSION = true ? "0.16.5" : "0.7.1";
|
|
3201
3374
|
}
|
|
3202
3375
|
});
|
|
3203
3376
|
|
|
@@ -20404,7 +20577,9 @@ function getStarterCode(structuredData) {
|
|
|
20404
20577
|
return data?.starterCode ?? null;
|
|
20405
20578
|
}
|
|
20406
20579
|
async function extractExercise(input2) {
|
|
20407
|
-
const {
|
|
20580
|
+
const { exerciseTier, workspacePath } = input2;
|
|
20581
|
+
const paths = input2.workspacePaths ?? { workspace: workspacePath, vault: "$VAULT" };
|
|
20582
|
+
const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
|
|
20408
20583
|
const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
|
|
20409
20584
|
const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
|
|
20410
20585
|
const exercisePath = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
|
|
@@ -20522,6 +20697,7 @@ var init_extract_exercise = __esm({
|
|
|
20522
20697
|
"../../packages/tostudy-core/src/workspace/extract-exercise.ts"() {
|
|
20523
20698
|
"use strict";
|
|
20524
20699
|
init_templates();
|
|
20700
|
+
init_workspace_tokens();
|
|
20525
20701
|
}
|
|
20526
20702
|
});
|
|
20527
20703
|
|
|
@@ -20822,7 +20998,10 @@ var init_export = __esm({
|
|
|
20822
20998
|
const result = await extractExercise({
|
|
20823
20999
|
lessonData,
|
|
20824
21000
|
exerciseTier: tier,
|
|
20825
|
-
workspacePath: ws.workspacePath
|
|
21001
|
+
workspacePath: ws.workspacePath,
|
|
21002
|
+
// Same pair `output()` prints with (GH #1278), so the exercise files on
|
|
21003
|
+
// disk and the lesson text on screen name the same folder (GH #1284).
|
|
21004
|
+
workspacePaths: cliWorkspacePaths(ws.workspacePath, activeCourse.courseTitle)
|
|
20826
21005
|
});
|
|
20827
21006
|
if (opts.json) {
|
|
20828
21007
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
@@ -20972,6 +21151,7 @@ var init_vault2 = __esm({
|
|
|
20972
21151
|
"use strict";
|
|
20973
21152
|
init_dist();
|
|
20974
21153
|
init_vault();
|
|
21154
|
+
init_workspace_tokens();
|
|
20975
21155
|
init_courses();
|
|
20976
21156
|
init_http();
|
|
20977
21157
|
init_guards();
|
|
@@ -21017,12 +21197,11 @@ var init_vault2 = __esm({
|
|
|
21017
21197
|
if (!data.success) {
|
|
21018
21198
|
throw new Error("API returned success: false");
|
|
21019
21199
|
}
|
|
21020
|
-
const
|
|
21200
|
+
const files = resolveWorkspaceTokens(
|
|
21021
21201
|
data.files,
|
|
21022
|
-
|
|
21023
|
-
activeCourse.courseId,
|
|
21024
|
-
slug
|
|
21202
|
+
cliWorkspacePaths(workspacePath, activeCourse.courseTitle)
|
|
21025
21203
|
);
|
|
21204
|
+
const result = await writeVaultFiles(files, vaultOutputPath, activeCourse.courseId, slug);
|
|
21026
21205
|
logger21.info("Vault generated", {
|
|
21027
21206
|
courseId: activeCourse.courseId,
|
|
21028
21207
|
vaultPath: result.vaultPath,
|
|
@@ -22233,7 +22412,10 @@ var init_export_grades = __esm({
|
|
|
22233
22412
|
defaultDeps5 = {
|
|
22234
22413
|
requireSession,
|
|
22235
22414
|
requireActiveCourse,
|
|
22236
|
-
|
|
22415
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22416
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22417
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22418
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22237
22419
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22238
22420
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22239
22421
|
// process.exit is typed as `never` — wrap so the cast is local to one line.
|
|
@@ -22420,7 +22602,10 @@ var init_grades2 = __esm({
|
|
|
22420
22602
|
logger33 = createLogger("cli:grades");
|
|
22421
22603
|
defaultDeps6 = {
|
|
22422
22604
|
requireSession,
|
|
22423
|
-
|
|
22605
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22606
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22607
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22608
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22424
22609
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22425
22610
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22426
22611
|
exit: (code) => process.exit(code),
|
|
@@ -22600,7 +22785,10 @@ var init_review2 = __esm({
|
|
|
22600
22785
|
logger34 = createLogger("cli:review");
|
|
22601
22786
|
defaultDeps7 = {
|
|
22602
22787
|
requireSession,
|
|
22603
|
-
|
|
22788
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22789
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22790
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22791
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22604
22792
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22605
22793
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22606
22794
|
exit: (code) => process.exit(code),
|
|
@@ -22785,6 +22973,21 @@ var init_auto_updater = __esm({
|
|
|
22785
22973
|
if (!process.env.LOG_LEVEL) {
|
|
22786
22974
|
process.env.LOG_LEVEL = "fatal";
|
|
22787
22975
|
}
|
|
22976
|
+
var MIN_NODE_MAJOR = 18;
|
|
22977
|
+
var nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
|
|
22978
|
+
if (Number.isFinite(nodeMajor) && nodeMajor < MIN_NODE_MAJOR) {
|
|
22979
|
+
const pt = !(process.env["LANG"] ?? "").toLowerCase().startsWith("en");
|
|
22980
|
+
process.stderr.write(
|
|
22981
|
+
pt ? `Erro: o ToStudy CLI precisa do Node.js ${MIN_NODE_MAJOR} ou superior.
|
|
22982
|
+
Voce esta no Node.js ${process.versions.node}.
|
|
22983
|
+
Atualize em https://nodejs.org e rode o comando de novo.
|
|
22984
|
+
` : `Error: the ToStudy CLI requires Node.js ${MIN_NODE_MAJOR} or newer.
|
|
22985
|
+
You are on Node.js ${process.versions.node}.
|
|
22986
|
+
Update at https://nodejs.org and run the command again.
|
|
22987
|
+
`
|
|
22988
|
+
);
|
|
22989
|
+
process.exit(1);
|
|
22990
|
+
}
|
|
22788
22991
|
var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
|
|
22789
22992
|
var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
|
|
22790
22993
|
var { maybeAutoUpdate: maybeAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_updater(), auto_updater_exports));
|