@tostudy-ai/cli 0.16.0 → 0.16.2
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 +341 -176
- package/dist/cli.js.map +4 -4
- package/package.json +4 -4
- package/bin/cli.js +0 -6
- package/bin/tostudy.ts +0 -14
package/dist/cli.js
CHANGED
|
@@ -41,8 +41,18 @@ 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-4AyoT9ic.mjs
|
|
45
45
|
function isSensitiveKey(key) {
|
|
46
|
+
if (matchesSensitiveField(key)) return true;
|
|
47
|
+
return matchesPluralOfSensitiveField(key);
|
|
48
|
+
}
|
|
49
|
+
function matchesPluralOfSensitiveField(key) {
|
|
50
|
+
const lower = key.toLowerCase();
|
|
51
|
+
if (lower.endsWith("es") && matchesSensitiveField(key.slice(0, -2))) return true;
|
|
52
|
+
if (lower.endsWith("s") && matchesSensitiveField(key.slice(0, -1))) return true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function matchesSensitiveField(key) {
|
|
46
56
|
const lowerKey = key.toLowerCase();
|
|
47
57
|
return SENSITIVE_FIELDS.some((field) => {
|
|
48
58
|
const lowerField = field.toLowerCase();
|
|
@@ -106,7 +116,8 @@ function sanitizeRecord(obj, ctx) {
|
|
|
106
116
|
const sanitized = { ...obj };
|
|
107
117
|
for (const [key, value] of Object.entries(sanitized)) {
|
|
108
118
|
const isAdditionalKey = ctx.additionalSensitiveKeys?.some((k) => key.toLowerCase() === k.toLowerCase());
|
|
109
|
-
|
|
119
|
+
const pluralCountExemption = typeof value === "number" && !isAdditionalKey && !matchesSensitiveField(key) && matchesPluralOfSensitiveField(key);
|
|
120
|
+
if ((isSensitiveKey(key) || isAdditionalKey) && !pluralCountExemption) {
|
|
110
121
|
sanitized[key] = "[REDACTED]";
|
|
111
122
|
continue;
|
|
112
123
|
}
|
|
@@ -136,8 +147,8 @@ function sanitizeEntryData(obj, additionalSensitiveKeys, includeStack) {
|
|
|
136
147
|
});
|
|
137
148
|
}
|
|
138
149
|
var SENSITIVE_FIELDS;
|
|
139
|
-
var
|
|
140
|
-
"../../node_modules/@parisgroup-ai/logger/dist/sanitize-
|
|
150
|
+
var init_sanitize_4AyoT9ic = __esm({
|
|
151
|
+
"../../node_modules/@parisgroup-ai/logger/dist/sanitize-4AyoT9ic.mjs"() {
|
|
141
152
|
SENSITIVE_FIELDS = [
|
|
142
153
|
"password",
|
|
143
154
|
"senha",
|
|
@@ -268,7 +279,7 @@ var require_picocolors = __commonJS({
|
|
|
268
279
|
}
|
|
269
280
|
});
|
|
270
281
|
|
|
271
|
-
// ../../node_modules/@parisgroup-ai/logger/dist/logger.base-
|
|
282
|
+
// ../../node_modules/@parisgroup-ai/logger/dist/logger.base-DOZPgsDx.mjs
|
|
272
283
|
function formatJson(entry) {
|
|
273
284
|
const { timestamp, level, message, service, environment, context, data, error: error49 } = entry;
|
|
274
285
|
const output3 = {
|
|
@@ -327,6 +338,35 @@ function formatPretty(entry) {
|
|
|
327
338
|
}
|
|
328
339
|
return output3;
|
|
329
340
|
}
|
|
341
|
+
function filterStrings(value, safe, visited) {
|
|
342
|
+
if (typeof value === "string") return safe(value);
|
|
343
|
+
if (value === null || typeof value !== "object") return value;
|
|
344
|
+
if (visited.has(value)) return value;
|
|
345
|
+
visited.add(value);
|
|
346
|
+
if (Array.isArray(value)) return value.map((item) => filterStrings(item, safe, visited));
|
|
347
|
+
const proto = Object.getPrototypeOf(value);
|
|
348
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
349
|
+
const out = {};
|
|
350
|
+
for (const [key, item] of Object.entries(value)) out[key] = filterStrings(item, safe, visited);
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
function applyPiiFilterToEntry(entry, filter) {
|
|
354
|
+
const safe = (text) => {
|
|
355
|
+
try {
|
|
356
|
+
return filter(text);
|
|
357
|
+
} catch {
|
|
358
|
+
return "[PII_FILTER_ERROR]";
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
362
|
+
return {
|
|
363
|
+
...entry,
|
|
364
|
+
message: safe(entry.message),
|
|
365
|
+
context: filterStrings(entry.context, safe, visited),
|
|
366
|
+
data: entry.data ? filterStrings(entry.data, safe, visited) : entry.data,
|
|
367
|
+
error: entry.error ? filterStrings(entry.error, safe, visited) : entry.error
|
|
368
|
+
};
|
|
369
|
+
}
|
|
330
370
|
function extractErrorInfo(error49, includeStack, visited = /* @__PURE__ */ new WeakSet()) {
|
|
331
371
|
const info = {
|
|
332
372
|
name: error49.name,
|
|
@@ -343,9 +383,9 @@ function extractErrorInfo(error49, includeStack, visited = /* @__PURE__ */ new W
|
|
|
343
383
|
return info;
|
|
344
384
|
}
|
|
345
385
|
var import_picocolors, LOG_LEVELS, LEVEL_COLORS, LEVEL_WIDTH, BaseLogger;
|
|
346
|
-
var
|
|
347
|
-
"../../node_modules/@parisgroup-ai/logger/dist/logger.base-
|
|
348
|
-
|
|
386
|
+
var init_logger_base_DOZPgsDx = __esm({
|
|
387
|
+
"../../node_modules/@parisgroup-ai/logger/dist/logger.base-DOZPgsDx.mjs"() {
|
|
388
|
+
init_sanitize_4AyoT9ic();
|
|
349
389
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
350
390
|
LOG_LEVELS = {
|
|
351
391
|
trace: 0,
|
|
@@ -375,6 +415,7 @@ var init_logger_base_MYys2STJ = __esm({
|
|
|
375
415
|
shouldSanitize;
|
|
376
416
|
sensitiveFields;
|
|
377
417
|
includeCaller;
|
|
418
|
+
piiFilter;
|
|
378
419
|
constructor(options, defaults) {
|
|
379
420
|
this.level = options.level ?? defaults.level;
|
|
380
421
|
this.format = options.format ?? defaults.format;
|
|
@@ -386,6 +427,7 @@ var init_logger_base_MYys2STJ = __esm({
|
|
|
386
427
|
this.shouldSanitize = options.sanitize ?? true;
|
|
387
428
|
this.sensitiveFields = options.sensitiveFields;
|
|
388
429
|
this.includeCaller = options.includeCaller ?? "errors";
|
|
430
|
+
this.piiFilter = options.piiFilter;
|
|
389
431
|
}
|
|
390
432
|
/**
|
|
391
433
|
* Get caller information for error logging (Node.js only).
|
|
@@ -401,10 +443,11 @@ var init_logger_base_MYys2STJ = __esm({
|
|
|
401
443
|
return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
|
|
402
444
|
}
|
|
403
445
|
buildEntry(level, message, data, error49) {
|
|
404
|
-
const
|
|
446
|
+
const merged = {
|
|
405
447
|
...this.getLogContext() ?? {},
|
|
406
448
|
...this.additionalContext
|
|
407
449
|
};
|
|
450
|
+
const context = this.shouldSanitize ? sanitizeEntryData(merged, this.sensitiveFields, !this.isProd()) : merged;
|
|
408
451
|
if (this.module) context.module = this.module;
|
|
409
452
|
if (this.includeCaller === true || this.includeCaller === "errors" && (level === "error" || level === "fatal")) {
|
|
410
453
|
const caller = this.getCaller();
|
|
@@ -447,7 +490,8 @@ var init_logger_base_MYys2STJ = __esm({
|
|
|
447
490
|
if (!this.shouldLog(level)) return;
|
|
448
491
|
const normalizedData = this.normalizeData(data);
|
|
449
492
|
const errorInfo = errorObj ? extractErrorInfo(errorObj, !this.isProd()) : void 0;
|
|
450
|
-
const
|
|
493
|
+
const built = this.buildEntry(level, message, normalizedData, errorInfo);
|
|
494
|
+
const entry = this.shouldSanitize && this.piiFilter ? applyPiiFilterToEntry(built, this.piiFilter) : built;
|
|
451
495
|
const formatted = this.format === "json" ? formatJson(entry) : formatPretty(entry);
|
|
452
496
|
for (const transport of this.transports) transport.log(entry, formatted);
|
|
453
497
|
this.flush();
|
|
@@ -609,8 +653,8 @@ function createLogger(module, options) {
|
|
|
609
653
|
var asyncLocalStorage, ConsoleTransport, FileTransport, HttpTransport, SINGLETON_KEY, Logger, logger;
|
|
610
654
|
var init_dist = __esm({
|
|
611
655
|
"../../node_modules/@parisgroup-ai/logger/dist/index.mjs"() {
|
|
612
|
-
|
|
613
|
-
|
|
656
|
+
init_logger_base_DOZPgsDx();
|
|
657
|
+
init_sanitize_4AyoT9ic();
|
|
614
658
|
asyncLocalStorage = new AsyncLocalStorage();
|
|
615
659
|
ConsoleTransport = class {
|
|
616
660
|
log(entry, formatted) {
|
|
@@ -768,7 +812,8 @@ var init_dist = __esm({
|
|
|
768
812
|
environment: this.environment,
|
|
769
813
|
sanitize: this.shouldSanitize,
|
|
770
814
|
sensitiveFields: this.sensitiveFields,
|
|
771
|
-
includeCaller: this.includeCaller
|
|
815
|
+
includeCaller: this.includeCaller,
|
|
816
|
+
piiFilter: this.piiFilter
|
|
772
817
|
});
|
|
773
818
|
child.additionalContext = {
|
|
774
819
|
...this.additionalContext,
|
|
@@ -836,9 +881,15 @@ async function cliApiFetch(url2, token, init) {
|
|
|
836
881
|
...init?.headers
|
|
837
882
|
}
|
|
838
883
|
});
|
|
839
|
-
const body = await response.json();
|
|
884
|
+
const body = await response.json().catch(() => ({}));
|
|
840
885
|
if (!response.ok) {
|
|
841
|
-
|
|
886
|
+
const code = typeof body.error === "string" && body.error.length > 0 ? body.error : void 0;
|
|
887
|
+
const friendly = typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
|
|
888
|
+
throw new CliApiError(
|
|
889
|
+
friendly ?? code ?? `API error ${response.status}`,
|
|
890
|
+
response.status,
|
|
891
|
+
code
|
|
892
|
+
);
|
|
842
893
|
}
|
|
843
894
|
return body;
|
|
844
895
|
}
|
|
@@ -973,12 +1024,14 @@ var init_http = __esm({
|
|
|
973
1024
|
"../../packages/tostudy-core/src/adapters/http.ts"() {
|
|
974
1025
|
"use strict";
|
|
975
1026
|
CliApiError = class extends Error {
|
|
976
|
-
constructor(message, status) {
|
|
1027
|
+
constructor(message, status, code) {
|
|
977
1028
|
super(message);
|
|
978
1029
|
this.status = status;
|
|
1030
|
+
this.code = code;
|
|
979
1031
|
this.name = "CliApiError";
|
|
980
1032
|
}
|
|
981
1033
|
status;
|
|
1034
|
+
code;
|
|
982
1035
|
};
|
|
983
1036
|
}
|
|
984
1037
|
});
|
|
@@ -1251,8 +1304,68 @@ var init_session_store = __esm({
|
|
|
1251
1304
|
}
|
|
1252
1305
|
});
|
|
1253
1306
|
|
|
1307
|
+
// ../../packages/tostudy-core/src/slug.ts
|
|
1308
|
+
function slugify(text, maxLength = 60) {
|
|
1309
|
+
return text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLength).replace(/-+$/, "");
|
|
1310
|
+
}
|
|
1311
|
+
var init_slug = __esm({
|
|
1312
|
+
"../../packages/tostudy-core/src/slug.ts"() {
|
|
1313
|
+
"use strict";
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
// ../../packages/tostudy-core/src/lessons/workspace-tokens.ts
|
|
1318
|
+
function resolveWorkspaceTokensInText(text, paths) {
|
|
1319
|
+
return text.replace(WORKSPACE_TOKEN, () => paths.workspace).replace(VAULT_TOKEN, () => paths.vault);
|
|
1320
|
+
}
|
|
1321
|
+
function resolveWorkspaceTokens(value, paths) {
|
|
1322
|
+
if (typeof value === "string") {
|
|
1323
|
+
return resolveWorkspaceTokensInText(value, paths);
|
|
1324
|
+
}
|
|
1325
|
+
if (Array.isArray(value)) {
|
|
1326
|
+
return value.map((item) => resolveWorkspaceTokens(item, paths));
|
|
1327
|
+
}
|
|
1328
|
+
if (value !== null && typeof value === "object") {
|
|
1329
|
+
const out = {};
|
|
1330
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1331
|
+
out[key] = resolveWorkspaceTokens(item, paths);
|
|
1332
|
+
}
|
|
1333
|
+
return out;
|
|
1334
|
+
}
|
|
1335
|
+
return value;
|
|
1336
|
+
}
|
|
1337
|
+
var WORKSPACE_TOKEN, VAULT_TOKEN;
|
|
1338
|
+
var init_workspace_tokens = __esm({
|
|
1339
|
+
"../../packages/tostudy-core/src/lessons/workspace-tokens.ts"() {
|
|
1340
|
+
"use strict";
|
|
1341
|
+
init_slug();
|
|
1342
|
+
WORKSPACE_TOKEN = /\$WORKSPACE(?![A-Za-z0-9_])/g;
|
|
1343
|
+
VAULT_TOKEN = /\$VAULT(?![A-Za-z0-9_])/g;
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1254
1347
|
// src/output/formatter.ts
|
|
1348
|
+
var formatter_exports = {};
|
|
1349
|
+
__export(formatter_exports, {
|
|
1350
|
+
error: () => error,
|
|
1351
|
+
filterExerciseContent: () => filterExerciseContent,
|
|
1352
|
+
formatCourseList: () => formatCourseList,
|
|
1353
|
+
formatHint: () => formatHint,
|
|
1354
|
+
formatLesson: () => formatLesson,
|
|
1355
|
+
formatModuleStart: () => formatModuleStart,
|
|
1356
|
+
formatMoreTheory: () => formatMoreTheory,
|
|
1357
|
+
formatProgress: () => formatProgress,
|
|
1358
|
+
formatValidation: () => formatValidation,
|
|
1359
|
+
jsonError: () => jsonError,
|
|
1360
|
+
output: () => output,
|
|
1361
|
+
progressBar: () => progressBar,
|
|
1362
|
+
setWorkspaceTokenPaths: () => setWorkspaceTokenPaths
|
|
1363
|
+
});
|
|
1364
|
+
function setWorkspaceTokenPaths(paths) {
|
|
1365
|
+
workspaceTokenPaths = paths;
|
|
1366
|
+
}
|
|
1255
1367
|
function output(data, opts) {
|
|
1368
|
+
if (workspaceTokenPaths) data = resolveWorkspaceTokens(data, workspaceTokenPaths);
|
|
1256
1369
|
if (opts.json) {
|
|
1257
1370
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
1258
1371
|
} else if (typeof data === "string") {
|
|
@@ -1494,10 +1607,12 @@ function formatMoreTheory(data) {
|
|
|
1494
1607
|
lines.push("", "\u2192 tostudy lesson voltar ao exerc\xEDcio atual");
|
|
1495
1608
|
return lines.join("\n");
|
|
1496
1609
|
}
|
|
1497
|
-
var TIER_HEADERS, UNEVALUATED_CRITERION_SENTINEL;
|
|
1610
|
+
var workspaceTokenPaths, TIER_HEADERS, UNEVALUATED_CRITERION_SENTINEL;
|
|
1498
1611
|
var init_formatter = __esm({
|
|
1499
1612
|
"src/output/formatter.ts"() {
|
|
1500
1613
|
"use strict";
|
|
1614
|
+
init_workspace_tokens();
|
|
1615
|
+
workspaceTokenPaths = null;
|
|
1501
1616
|
TIER_HEADERS = [/^## Prática Guiada/im, /^## Prática Semi-?[Gg]uiada/im, /^## Desafio/im];
|
|
1502
1617
|
UNEVALUATED_CRITERION_SENTINEL = "Crit\xE9rio n\xE3o avaliado explicitamente; tratado como n\xE3o atendido.";
|
|
1503
1618
|
}
|
|
@@ -1925,16 +2040,6 @@ var init_instruction_template_v3 = __esm({
|
|
|
1925
2040
|
}
|
|
1926
2041
|
});
|
|
1927
2042
|
|
|
1928
|
-
// ../../packages/tostudy-core/src/slug.ts
|
|
1929
|
-
function slugify(text, maxLength = 60) {
|
|
1930
|
-
return text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLength).replace(/-+$/, "");
|
|
1931
|
-
}
|
|
1932
|
-
var init_slug = __esm({
|
|
1933
|
-
"../../packages/tostudy-core/src/slug.ts"() {
|
|
1934
|
-
"use strict";
|
|
1935
|
-
}
|
|
1936
|
-
});
|
|
1937
|
-
|
|
1938
2043
|
// src/workspace/runtime-registry.ts
|
|
1939
2044
|
var runtime_registry_exports = {};
|
|
1940
2045
|
__export(runtime_registry_exports, {
|
|
@@ -2638,6 +2743,15 @@ var init_course_drift = __esm({
|
|
|
2638
2743
|
});
|
|
2639
2744
|
|
|
2640
2745
|
// src/onboarding/course-state.ts
|
|
2746
|
+
var course_state_exports = {};
|
|
2747
|
+
__export(course_state_exports, {
|
|
2748
|
+
checkCourseDrift: () => checkCourseDrift,
|
|
2749
|
+
clearAllCourseOnboarding: () => clearAllCourseOnboarding,
|
|
2750
|
+
getCourseOnboardingState: () => getCourseOnboardingState,
|
|
2751
|
+
saveCourseLearnerProfile: () => saveCourseLearnerProfile,
|
|
2752
|
+
setCourseWorkspacePath: () => setCourseWorkspacePath,
|
|
2753
|
+
setLastInitCourseId: () => setLastInitCourseId
|
|
2754
|
+
});
|
|
2641
2755
|
import fs6 from "node:fs";
|
|
2642
2756
|
async function getCourseOnboardingState(courseId, configDir) {
|
|
2643
2757
|
const state = readCourseOnboardingState(configDir);
|
|
@@ -2734,17 +2848,21 @@ function resolveLocale() {
|
|
|
2734
2848
|
function getErrors(locale) {
|
|
2735
2849
|
return BUNDLES[locale ?? resolveLocale()];
|
|
2736
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
|
+
}
|
|
2737
2858
|
function isInsufficientCreditsError(err) {
|
|
2738
|
-
|
|
2739
|
-
return msg.includes("INSUFFICIENT_CREDITS");
|
|
2859
|
+
return resolveErrorCode(err).includes("INSUFFICIENT_CREDITS");
|
|
2740
2860
|
}
|
|
2741
2861
|
function isCourseArchivedError(err) {
|
|
2742
|
-
|
|
2743
|
-
return msg.includes("COURSE_ARCHIVED");
|
|
2862
|
+
return resolveErrorCode(err).includes("COURSE_ARCHIVED");
|
|
2744
2863
|
}
|
|
2745
2864
|
function isEnrollmentNotEntitledError(err) {
|
|
2746
|
-
|
|
2747
|
-
return msg.includes("ENROLLMENT_NOT_ENTITLED");
|
|
2865
|
+
return resolveErrorCode(err).includes("ENROLLMENT_NOT_ENTITLED");
|
|
2748
2866
|
}
|
|
2749
2867
|
var BUNDLES, _cachedLocale;
|
|
2750
2868
|
var init_errors = __esm({
|
|
@@ -2777,6 +2895,121 @@ var init_logout = __esm({
|
|
|
2777
2895
|
}
|
|
2778
2896
|
});
|
|
2779
2897
|
|
|
2898
|
+
// src/workspace/resolve.ts
|
|
2899
|
+
var resolve_exports = {};
|
|
2900
|
+
__export(resolve_exports, {
|
|
2901
|
+
cliWorkspacePaths: () => cliWorkspacePaths,
|
|
2902
|
+
courseSlug: () => courseSlug,
|
|
2903
|
+
findCwdWorkspaceUpwards: () => findCwdWorkspaceUpwards,
|
|
2904
|
+
findExistingVault: () => findExistingVault,
|
|
2905
|
+
isCwdWorkspace: () => isCwdWorkspace,
|
|
2906
|
+
resolveActiveCourseFromWorkspace: () => resolveActiveCourseFromWorkspace,
|
|
2907
|
+
resolveActiveCourseWithOverride: () => resolveActiveCourseWithOverride,
|
|
2908
|
+
resolveCwdWorkspacePath: () => resolveCwdWorkspacePath,
|
|
2909
|
+
resolveEffectiveWorkspace: () => resolveEffectiveWorkspace,
|
|
2910
|
+
resolveVaultPath: () => resolveVaultPath,
|
|
2911
|
+
resolveWorkspace: () => resolveWorkspace
|
|
2912
|
+
});
|
|
2913
|
+
import fs7 from "node:fs/promises";
|
|
2914
|
+
import path9 from "node:path";
|
|
2915
|
+
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
|
+
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
2920
|
+
const slug = courseSlug(courseTitle);
|
|
2921
|
+
const candidate = path9.join(basePath, slug);
|
|
2922
|
+
try {
|
|
2923
|
+
await fs7.access(path9.join(candidate, ".ana-config.json"));
|
|
2924
|
+
return { found: true, workspacePath: candidate, source: "default" };
|
|
2925
|
+
} catch {
|
|
2926
|
+
return { found: false, workspacePath: null };
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
async function isCwdWorkspace(cwd = process.cwd()) {
|
|
2930
|
+
return await resolveCwdWorkspacePath(cwd) !== null;
|
|
2931
|
+
}
|
|
2932
|
+
async function resolveCwdWorkspacePath(cwd = process.cwd()) {
|
|
2933
|
+
const tostudyDir = path9.join(cwd, ".tostudy");
|
|
2934
|
+
try {
|
|
2935
|
+
const stat = await fs7.stat(tostudyDir);
|
|
2936
|
+
if (stat.isDirectory()) return tostudyDir;
|
|
2937
|
+
} catch {
|
|
2938
|
+
}
|
|
2939
|
+
try {
|
|
2940
|
+
await fs7.access(path9.join(cwd, ".ana-config.json"));
|
|
2941
|
+
return cwd;
|
|
2942
|
+
} catch {
|
|
2943
|
+
return null;
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
|
|
2947
|
+
const home = path9.resolve(homeDir);
|
|
2948
|
+
let current = path9.resolve(startCwd);
|
|
2949
|
+
while (true) {
|
|
2950
|
+
if (current === home) return null;
|
|
2951
|
+
const direct = await resolveCwdWorkspacePath(current);
|
|
2952
|
+
if (direct) return direct;
|
|
2953
|
+
const parent = path9.dirname(current);
|
|
2954
|
+
if (parent === current) return null;
|
|
2955
|
+
current = parent;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
function resolveVaultPath(workspacePath, slug) {
|
|
2959
|
+
const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
|
|
2960
|
+
return path9.join(base, `vault-${slug}`);
|
|
2961
|
+
}
|
|
2962
|
+
function cliWorkspacePaths(workspacePath, courseTitle) {
|
|
2963
|
+
const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
|
|
2964
|
+
return { workspace: base, vault: resolveVaultPath(workspacePath, courseSlug(courseTitle)) };
|
|
2965
|
+
}
|
|
2966
|
+
async function findExistingVault(workspacePath, slug) {
|
|
2967
|
+
const slugged = resolveVaultPath(workspacePath, slug);
|
|
2968
|
+
try {
|
|
2969
|
+
await fs7.access(path9.join(slugged, ".ana-vault.json"));
|
|
2970
|
+
return slugged;
|
|
2971
|
+
} catch {
|
|
2972
|
+
}
|
|
2973
|
+
const legacy = path9.join(workspacePath, "vault");
|
|
2974
|
+
try {
|
|
2975
|
+
await fs7.access(path9.join(legacy, ".ana-vault.json"));
|
|
2976
|
+
return legacy;
|
|
2977
|
+
} catch {
|
|
2978
|
+
return null;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.cwd(), defaultBasePath = DEFAULT_BASE, homeDir = os6.homedir()) {
|
|
2982
|
+
const cwdWorkspace = await findCwdWorkspaceUpwards(cwd, homeDir);
|
|
2983
|
+
if (cwdWorkspace) {
|
|
2984
|
+
return { found: true, workspacePath: cwdWorkspace, source: "cwd" };
|
|
2985
|
+
}
|
|
2986
|
+
if (storedPath) {
|
|
2987
|
+
try {
|
|
2988
|
+
await fs7.access(path9.join(storedPath, ".ana-config.json"));
|
|
2989
|
+
return { found: true, workspacePath: storedPath, source: "stored" };
|
|
2990
|
+
} catch {
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
const result = await resolveWorkspace(courseTitle, defaultBasePath);
|
|
2994
|
+
if (result.found) {
|
|
2995
|
+
return { ...result, source: "default" };
|
|
2996
|
+
}
|
|
2997
|
+
return { found: false, workspacePath: null };
|
|
2998
|
+
}
|
|
2999
|
+
async function resolveActiveCourseFromWorkspace(options = {}) {
|
|
3000
|
+
const { findWorkspaceState: findWorkspaceState2 } = await Promise.resolve().then(() => (init_workspace_state(), workspace_state_exports));
|
|
3001
|
+
const result = await findWorkspaceState2(options.cwd, options.homeDir);
|
|
3002
|
+
return result?.state ?? null;
|
|
3003
|
+
}
|
|
3004
|
+
var DEFAULT_BASE, resolveActiveCourseWithOverride;
|
|
3005
|
+
var init_resolve = __esm({
|
|
3006
|
+
"src/workspace/resolve.ts"() {
|
|
3007
|
+
"use strict";
|
|
3008
|
+
DEFAULT_BASE = path9.join(os6.homedir(), "study");
|
|
3009
|
+
resolveActiveCourseWithOverride = resolveActiveCourseFromWorkspace;
|
|
3010
|
+
}
|
|
3011
|
+
});
|
|
3012
|
+
|
|
2780
3013
|
// src/auth/guards.ts
|
|
2781
3014
|
async function refreshCliSession(session, configDir) {
|
|
2782
3015
|
try {
|
|
@@ -2823,10 +3056,32 @@ async function requireSession(configDir) {
|
|
|
2823
3056
|
async function requireActiveCourse() {
|
|
2824
3057
|
const { findWorkspaceState: findWorkspaceState2 } = await Promise.resolve().then(() => (init_workspace_state(), workspace_state_exports));
|
|
2825
3058
|
const result = await findWorkspaceState2();
|
|
2826
|
-
if (result)
|
|
3059
|
+
if (result) {
|
|
3060
|
+
await primeWorkspaceTokenPaths(result.state);
|
|
3061
|
+
return result.state;
|
|
3062
|
+
}
|
|
2827
3063
|
process.stderr.write("Erro: Nenhum workspace configurado. Rode: tostudy setup\n");
|
|
2828
3064
|
process.exit(3);
|
|
2829
3065
|
}
|
|
3066
|
+
async function primeWorkspaceTokenPaths(state) {
|
|
3067
|
+
try {
|
|
3068
|
+
const [
|
|
3069
|
+
{ setWorkspaceTokenPaths: setWorkspaceTokenPaths2 },
|
|
3070
|
+
{ resolveEffectiveWorkspace: resolveEffectiveWorkspace2, cliWorkspacePaths: cliWorkspacePaths2 },
|
|
3071
|
+
onboarding
|
|
3072
|
+
] = await Promise.all([
|
|
3073
|
+
Promise.resolve().then(() => (init_formatter(), formatter_exports)),
|
|
3074
|
+
Promise.resolve().then(() => (init_resolve(), resolve_exports)),
|
|
3075
|
+
Promise.resolve().then(() => (init_course_state(), course_state_exports))
|
|
3076
|
+
]);
|
|
3077
|
+
const stored = await onboarding.getCourseOnboardingState(state.courseId);
|
|
3078
|
+
const ws = await resolveEffectiveWorkspace2(state.courseTitle, stored?.workspacePath);
|
|
3079
|
+
setWorkspaceTokenPaths2(
|
|
3080
|
+
ws.found && ws.workspacePath ? cliWorkspacePaths2(ws.workspacePath, state.courseTitle) : null
|
|
3081
|
+
);
|
|
3082
|
+
} catch {
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
2830
3085
|
var init_guards = __esm({
|
|
2831
3086
|
"src/auth/guards.ts"() {
|
|
2832
3087
|
"use strict";
|
|
@@ -2942,7 +3197,7 @@ var CLI_VERSION;
|
|
|
2942
3197
|
var init_version = __esm({
|
|
2943
3198
|
"src/version.ts"() {
|
|
2944
3199
|
"use strict";
|
|
2945
|
-
CLI_VERSION = true ? "0.16.
|
|
3200
|
+
CLI_VERSION = true ? "0.16.2" : "0.7.1";
|
|
2946
3201
|
}
|
|
2947
3202
|
});
|
|
2948
3203
|
|
|
@@ -2956,13 +3211,13 @@ __export(update_checker_exports, {
|
|
|
2956
3211
|
readCache: () => readCache,
|
|
2957
3212
|
writeCache: () => writeCache
|
|
2958
3213
|
});
|
|
2959
|
-
import
|
|
2960
|
-
import
|
|
3214
|
+
import fs8 from "node:fs";
|
|
3215
|
+
import path10 from "node:path";
|
|
2961
3216
|
function readCache(configDir) {
|
|
2962
3217
|
try {
|
|
2963
|
-
const p =
|
|
2964
|
-
if (!
|
|
2965
|
-
return JSON.parse(
|
|
3218
|
+
const p = path10.join(configDir ?? getConfigDir(), CACHE_FILE);
|
|
3219
|
+
if (!fs8.existsSync(p)) return null;
|
|
3220
|
+
return JSON.parse(fs8.readFileSync(p, "utf-8"));
|
|
2966
3221
|
} catch {
|
|
2967
3222
|
return null;
|
|
2968
3223
|
}
|
|
@@ -2970,8 +3225,8 @@ function readCache(configDir) {
|
|
|
2970
3225
|
function writeCache(cache, configDir) {
|
|
2971
3226
|
try {
|
|
2972
3227
|
const dir = configDir ?? getConfigDir();
|
|
2973
|
-
|
|
2974
|
-
|
|
3228
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3229
|
+
fs8.writeFileSync(path10.join(dir, CACHE_FILE), JSON.stringify(cache));
|
|
2975
3230
|
} catch {
|
|
2976
3231
|
}
|
|
2977
3232
|
}
|
|
@@ -3041,17 +3296,17 @@ var init_update_checker = __esm({
|
|
|
3041
3296
|
});
|
|
3042
3297
|
|
|
3043
3298
|
// src/learner-brief/cache.ts
|
|
3044
|
-
import
|
|
3045
|
-
import
|
|
3299
|
+
import fs9 from "node:fs";
|
|
3300
|
+
import path11 from "node:path";
|
|
3046
3301
|
function resolveCachePath(configDir) {
|
|
3047
|
-
return
|
|
3302
|
+
return path11.join(configDir, "student-brief.json");
|
|
3048
3303
|
}
|
|
3049
3304
|
async function readBriefCache(configDir) {
|
|
3050
3305
|
const dir = configDir ?? getConfigDir();
|
|
3051
3306
|
const p = resolveCachePath(dir);
|
|
3052
|
-
if (!
|
|
3307
|
+
if (!fs9.existsSync(p)) return null;
|
|
3053
3308
|
try {
|
|
3054
|
-
const raw =
|
|
3309
|
+
const raw = fs9.readFileSync(p, "utf-8");
|
|
3055
3310
|
return JSON.parse(raw);
|
|
3056
3311
|
} catch {
|
|
3057
3312
|
return null;
|
|
@@ -3059,8 +3314,8 @@ async function readBriefCache(configDir) {
|
|
|
3059
3314
|
}
|
|
3060
3315
|
async function writeBriefCache(configDir, cache) {
|
|
3061
3316
|
const dir = configDir ?? getConfigDir();
|
|
3062
|
-
|
|
3063
|
-
|
|
3317
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
3318
|
+
fs9.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
|
|
3064
3319
|
}
|
|
3065
3320
|
var init_cache = __esm({
|
|
3066
3321
|
"src/learner-brief/cache.ts"() {
|
|
@@ -3070,23 +3325,23 @@ var init_cache = __esm({
|
|
|
3070
3325
|
});
|
|
3071
3326
|
|
|
3072
3327
|
// src/tutor-persona/cache.ts
|
|
3073
|
-
import
|
|
3074
|
-
import
|
|
3328
|
+
import fs10 from "node:fs";
|
|
3329
|
+
import path12 from "node:path";
|
|
3075
3330
|
function resolveCachePath2(configDir) {
|
|
3076
|
-
return
|
|
3331
|
+
return path12.join(configDir, "tutor-personalities.json");
|
|
3077
3332
|
}
|
|
3078
3333
|
function readAll(configDir) {
|
|
3079
3334
|
const p = resolveCachePath2(configDir);
|
|
3080
|
-
if (!
|
|
3335
|
+
if (!fs10.existsSync(p)) return {};
|
|
3081
3336
|
try {
|
|
3082
|
-
return JSON.parse(
|
|
3337
|
+
return JSON.parse(fs10.readFileSync(p, "utf-8"));
|
|
3083
3338
|
} catch {
|
|
3084
3339
|
return {};
|
|
3085
3340
|
}
|
|
3086
3341
|
}
|
|
3087
3342
|
function writeAll(configDir, cache) {
|
|
3088
|
-
|
|
3089
|
-
|
|
3343
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
3344
|
+
fs10.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
|
|
3090
3345
|
}
|
|
3091
3346
|
async function readPersonaCacheForCourse(configDir, courseId) {
|
|
3092
3347
|
const dir = configDir ?? getConfigDir();
|
|
@@ -3356,97 +3611,6 @@ var init_courses2 = __esm({
|
|
|
3356
3611
|
}
|
|
3357
3612
|
});
|
|
3358
3613
|
|
|
3359
|
-
// src/workspace/resolve.ts
|
|
3360
|
-
import fs10 from "node:fs/promises";
|
|
3361
|
-
import path12 from "node:path";
|
|
3362
|
-
import os6 from "node:os";
|
|
3363
|
-
function courseSlug(title) {
|
|
3364
|
-
return title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
3365
|
-
}
|
|
3366
|
-
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
3367
|
-
const slug = courseSlug(courseTitle);
|
|
3368
|
-
const candidate = path12.join(basePath, slug);
|
|
3369
|
-
try {
|
|
3370
|
-
await fs10.access(path12.join(candidate, ".ana-config.json"));
|
|
3371
|
-
return { found: true, workspacePath: candidate, source: "default" };
|
|
3372
|
-
} catch {
|
|
3373
|
-
return { found: false, workspacePath: null };
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
async function isCwdWorkspace(cwd = process.cwd()) {
|
|
3377
|
-
return await resolveCwdWorkspacePath(cwd) !== null;
|
|
3378
|
-
}
|
|
3379
|
-
async function resolveCwdWorkspacePath(cwd = process.cwd()) {
|
|
3380
|
-
const tostudyDir = path12.join(cwd, ".tostudy");
|
|
3381
|
-
try {
|
|
3382
|
-
const stat = await fs10.stat(tostudyDir);
|
|
3383
|
-
if (stat.isDirectory()) return tostudyDir;
|
|
3384
|
-
} catch {
|
|
3385
|
-
}
|
|
3386
|
-
try {
|
|
3387
|
-
await fs10.access(path12.join(cwd, ".ana-config.json"));
|
|
3388
|
-
return cwd;
|
|
3389
|
-
} catch {
|
|
3390
|
-
return null;
|
|
3391
|
-
}
|
|
3392
|
-
}
|
|
3393
|
-
async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
|
|
3394
|
-
const home = path12.resolve(homeDir);
|
|
3395
|
-
let current = path12.resolve(startCwd);
|
|
3396
|
-
while (true) {
|
|
3397
|
-
if (current === home) return null;
|
|
3398
|
-
const direct = await resolveCwdWorkspacePath(current);
|
|
3399
|
-
if (direct) return direct;
|
|
3400
|
-
const parent = path12.dirname(current);
|
|
3401
|
-
if (parent === current) return null;
|
|
3402
|
-
current = parent;
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
|
-
function resolveVaultPath(workspacePath, slug) {
|
|
3406
|
-
const base = path12.basename(workspacePath) === ".tostudy" ? path12.dirname(workspacePath) : workspacePath;
|
|
3407
|
-
return path12.join(base, `vault-${slug}`);
|
|
3408
|
-
}
|
|
3409
|
-
async function findExistingVault(workspacePath, slug) {
|
|
3410
|
-
const slugged = resolveVaultPath(workspacePath, slug);
|
|
3411
|
-
try {
|
|
3412
|
-
await fs10.access(path12.join(slugged, ".ana-vault.json"));
|
|
3413
|
-
return slugged;
|
|
3414
|
-
} catch {
|
|
3415
|
-
}
|
|
3416
|
-
const legacy = path12.join(workspacePath, "vault");
|
|
3417
|
-
try {
|
|
3418
|
-
await fs10.access(path12.join(legacy, ".ana-vault.json"));
|
|
3419
|
-
return legacy;
|
|
3420
|
-
} catch {
|
|
3421
|
-
return null;
|
|
3422
|
-
}
|
|
3423
|
-
}
|
|
3424
|
-
async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.cwd(), defaultBasePath = DEFAULT_BASE, homeDir = os6.homedir()) {
|
|
3425
|
-
const cwdWorkspace = await findCwdWorkspaceUpwards(cwd, homeDir);
|
|
3426
|
-
if (cwdWorkspace) {
|
|
3427
|
-
return { found: true, workspacePath: cwdWorkspace, source: "cwd" };
|
|
3428
|
-
}
|
|
3429
|
-
if (storedPath) {
|
|
3430
|
-
try {
|
|
3431
|
-
await fs10.access(path12.join(storedPath, ".ana-config.json"));
|
|
3432
|
-
return { found: true, workspacePath: storedPath, source: "stored" };
|
|
3433
|
-
} catch {
|
|
3434
|
-
}
|
|
3435
|
-
}
|
|
3436
|
-
const result = await resolveWorkspace(courseTitle, defaultBasePath);
|
|
3437
|
-
if (result.found) {
|
|
3438
|
-
return { ...result, source: "default" };
|
|
3439
|
-
}
|
|
3440
|
-
return { found: false, workspacePath: null };
|
|
3441
|
-
}
|
|
3442
|
-
var DEFAULT_BASE;
|
|
3443
|
-
var init_resolve = __esm({
|
|
3444
|
-
"src/workspace/resolve.ts"() {
|
|
3445
|
-
"use strict";
|
|
3446
|
-
DEFAULT_BASE = path12.join(os6.homedir(), "study");
|
|
3447
|
-
}
|
|
3448
|
-
});
|
|
3449
|
-
|
|
3450
3614
|
// src/onboarding/status.ts
|
|
3451
3615
|
async function getCourseOnboardingStatus(activeCourse, configDir, cwd = process.cwd()) {
|
|
3452
3616
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId, configDir);
|
|
@@ -4130,6 +4294,7 @@ var init_lessons = __esm({
|
|
|
4130
4294
|
init_get_hint();
|
|
4131
4295
|
init_start_module();
|
|
4132
4296
|
init_start_next_module();
|
|
4297
|
+
init_workspace_tokens();
|
|
4133
4298
|
}
|
|
4134
4299
|
});
|
|
4135
4300
|
|
|
@@ -4163,7 +4328,7 @@ async function runStart(opts, deps = defaultDeps3) {
|
|
|
4163
4328
|
`);
|
|
4164
4329
|
}
|
|
4165
4330
|
} catch (err) {
|
|
4166
|
-
if (err instanceof CliApiError && err.status === 403 && err.message === "CHANNEL_MISMATCH") {
|
|
4331
|
+
if (err instanceof CliApiError && err.status === 403 && (err.code ?? err.message) === "CHANNEL_MISMATCH") {
|
|
4167
4332
|
process.stderr.write("Este curso n\xE3o est\xE1 dispon\xEDvel via CLI.\n");
|
|
4168
4333
|
process.stderr.write("Acesse tostudy.ai para estudar este curso pelo ChatStudy.\n");
|
|
4169
4334
|
process.exit(1);
|
|
@@ -4251,7 +4416,7 @@ var init_start_next = __esm({
|
|
|
4251
4416
|
output(formatModuleStart(moduleData), { json: false });
|
|
4252
4417
|
}
|
|
4253
4418
|
} catch (err) {
|
|
4254
|
-
if (err instanceof CliApiError && err.message === "COURSE_COMPLETE") {
|
|
4419
|
+
if (err instanceof CliApiError && (err.code ?? err.message) === "COURSE_COMPLETE") {
|
|
4255
4420
|
process.stderr.write("\u{1F389} Parab\xE9ns! Curso conclu\xEDdo!\n");
|
|
4256
4421
|
process.stderr.write("\u2192 tostudy courses para ver outros cursos\n");
|
|
4257
4422
|
process.exit(0);
|
|
@@ -4324,26 +4489,37 @@ var init_next = __esm({
|
|
|
4324
4489
|
process.exit(1);
|
|
4325
4490
|
}
|
|
4326
4491
|
if (err instanceof CliApiError) {
|
|
4327
|
-
|
|
4492
|
+
const code = err.code ?? err.message;
|
|
4493
|
+
if (err.status === 403 && code === "CHANNEL_MISMATCH") {
|
|
4328
4494
|
process.stderr.write("Este curso n\xE3o est\xE1 dispon\xEDvel via CLI.\n");
|
|
4329
4495
|
process.stderr.write("Acesse tostudy.ai para estudar este curso pelo ChatStudy.\n");
|
|
4330
4496
|
process.exit(1);
|
|
4331
4497
|
}
|
|
4332
|
-
if (
|
|
4498
|
+
if (code === "MODULE_COMPLETE") {
|
|
4333
4499
|
process.stderr.write("\u2713 M\xF3dulo conclu\xEDdo!\n");
|
|
4334
4500
|
process.stderr.write("\u2192 tostudy start-next para iniciar o pr\xF3ximo m\xF3dulo\n");
|
|
4335
4501
|
process.exit(0);
|
|
4336
4502
|
}
|
|
4337
|
-
if (
|
|
4503
|
+
if (code === "COURSE_COMPLETE") {
|
|
4338
4504
|
process.stderr.write("\u{1F389} Parab\xE9ns! Curso conclu\xEDdo!\n");
|
|
4339
4505
|
process.stderr.write("\u2192 tostudy courses para ver outros cursos\n");
|
|
4340
4506
|
process.exit(0);
|
|
4341
4507
|
}
|
|
4342
|
-
if (
|
|
4508
|
+
if (code === "EXERCISE_NOT_PASSED") {
|
|
4509
|
+
process.stderr.write("Voc\xEA ainda n\xE3o foi aprovado no exerc\xEDcio desta li\xE7\xE3o.\n");
|
|
4510
|
+
process.stderr.write("\u2192 tostudy validate para enviar sua solu\xE7\xE3o\n");
|
|
4511
|
+
process.exit(1);
|
|
4512
|
+
}
|
|
4513
|
+
if (code === "NO_ACTIVE_LESSON") {
|
|
4343
4514
|
process.stderr.write("Nenhuma li\xE7\xE3o em progresso.\n");
|
|
4344
4515
|
process.stderr.write("\u2192 tostudy start-next para iniciar o pr\xF3ximo m\xF3dulo\n");
|
|
4345
4516
|
process.exit(0);
|
|
4346
4517
|
}
|
|
4518
|
+
if (err.status === 409) {
|
|
4519
|
+
process.stderr.write(`${err.message}
|
|
4520
|
+
`);
|
|
4521
|
+
process.exit(1);
|
|
4522
|
+
}
|
|
4347
4523
|
}
|
|
4348
4524
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4349
4525
|
if (opts.json) jsonError(msg);
|
|
@@ -4363,12 +4539,6 @@ function adjustTimeEstimate(type, baseMinutes) {
|
|
|
4363
4539
|
function isCheckpoint(title) {
|
|
4364
4540
|
return /^checkpoint/i.test(title.trim());
|
|
4365
4541
|
}
|
|
4366
|
-
function replacePlaceholders(text, workspacePath, courseTitle) {
|
|
4367
|
-
const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
|
|
4368
|
-
const slug = courseSlug(courseTitle);
|
|
4369
|
-
const vaultPath = resolveVaultPath(workspacePath, slug);
|
|
4370
|
-
return text.replace(/\$WORKSPACE(?![A-Za-z0-9_])/g, () => base).replace(/\$VAULT(?![A-Za-z0-9_])/g, () => vaultPath);
|
|
4371
|
-
}
|
|
4372
4542
|
function formatLessonContent(data, workspacePath, courseTitle) {
|
|
4373
4543
|
const time3 = adjustTimeEstimate(data.type, data.estimatedTimeMinutes);
|
|
4374
4544
|
const isPractical = data.type === "exercise" || isCheckpoint(data.title);
|
|
@@ -4420,6 +4590,7 @@ var init_lesson = __esm({
|
|
|
4420
4590
|
init_course_state();
|
|
4421
4591
|
init_formatter();
|
|
4422
4592
|
init_resolve();
|
|
4593
|
+
init_workspace_tokens();
|
|
4423
4594
|
init_errors();
|
|
4424
4595
|
logger13 = createLogger("cli:lesson");
|
|
4425
4596
|
lessonCommand = new Command11("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").option(
|
|
@@ -4455,22 +4626,6 @@ var init_lesson = __esm({
|
|
|
4455
4626
|
activeCourse.courseTitle,
|
|
4456
4627
|
onboardingState?.workspacePath
|
|
4457
4628
|
);
|
|
4458
|
-
if (ws.found && ws.workspacePath) {
|
|
4459
|
-
if (content.content) {
|
|
4460
|
-
content.content = replacePlaceholders(
|
|
4461
|
-
content.content,
|
|
4462
|
-
ws.workspacePath,
|
|
4463
|
-
activeCourse.courseTitle
|
|
4464
|
-
);
|
|
4465
|
-
}
|
|
4466
|
-
if (content.acceptanceCriteria) {
|
|
4467
|
-
content.acceptanceCriteria = replacePlaceholders(
|
|
4468
|
-
content.acceptanceCriteria,
|
|
4469
|
-
ws.workspacePath,
|
|
4470
|
-
activeCourse.courseTitle
|
|
4471
|
-
);
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
4629
|
if (opts.json) {
|
|
4475
4630
|
output(content, { json: true });
|
|
4476
4631
|
} else {
|
|
@@ -4611,6 +4766,11 @@ var init_hint = __esm({
|
|
|
4611
4766
|
else process.stderr.write(friendly);
|
|
4612
4767
|
process.exit(1);
|
|
4613
4768
|
}
|
|
4769
|
+
if (err instanceof CliApiError && (err.code ?? err.message) === "NO_ACTIVE_LESSON") {
|
|
4770
|
+
process.stderr.write("Nenhuma li\xE7\xE3o em progresso.\n");
|
|
4771
|
+
process.stderr.write("\u2192 tostudy start-next para iniciar o pr\xF3ximo m\xF3dulo\n");
|
|
4772
|
+
process.exit(0);
|
|
4773
|
+
}
|
|
4614
4774
|
if (isInsufficientCreditsError(err)) {
|
|
4615
4775
|
const friendly = getErrors().insufficientCredits;
|
|
4616
4776
|
if (opts.json) jsonError("insufficient_credits", { message: friendly });
|
|
@@ -21666,7 +21826,12 @@ var init_theory = __esm({
|
|
|
21666
21826
|
});
|
|
21667
21827
|
if (!res.ok) {
|
|
21668
21828
|
const body = await res.json().catch(() => ({}));
|
|
21669
|
-
|
|
21829
|
+
if (body.error === "INSUFFICIENT_CREDITS" || (body.message ?? "").includes("INSUFFICIENT_CREDITS")) {
|
|
21830
|
+
const friendly = getErrors().insufficientCredits;
|
|
21831
|
+
if (opts.json) jsonError("insufficient_credits", { message: friendly });
|
|
21832
|
+
error(friendly);
|
|
21833
|
+
}
|
|
21834
|
+
const msg = body.error ?? body.message ?? `Falha ao pedir teoria (${res.status})`;
|
|
21670
21835
|
if (opts.json) jsonError("request_failed", { message: msg, code: res.status });
|
|
21671
21836
|
error(msg);
|
|
21672
21837
|
}
|