@tostudy-ai/cli 0.16.3 → 0.16.6
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 +435 -141
- 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
|
});
|
|
@@ -1193,7 +1366,7 @@ async function loadStoredSessionSecrets(configDir) {
|
|
|
1193
1366
|
const account = getStoreKey(configDir);
|
|
1194
1367
|
if (process.platform === "darwin") {
|
|
1195
1368
|
try {
|
|
1196
|
-
const { stdout } = await execFileAsync("security", [
|
|
1369
|
+
const { stdout: stdout2 } = await execFileAsync("security", [
|
|
1197
1370
|
"find-generic-password",
|
|
1198
1371
|
"-s",
|
|
1199
1372
|
"tostudy-cli-session",
|
|
@@ -1201,21 +1374,21 @@ async function loadStoredSessionSecrets(configDir) {
|
|
|
1201
1374
|
account,
|
|
1202
1375
|
"-w"
|
|
1203
1376
|
]);
|
|
1204
|
-
return JSON.parse(
|
|
1377
|
+
return JSON.parse(stdout2.trim());
|
|
1205
1378
|
} catch {
|
|
1206
1379
|
return null;
|
|
1207
1380
|
}
|
|
1208
1381
|
}
|
|
1209
1382
|
if (process.platform === "linux" && await hasCommand("secret-tool")) {
|
|
1210
1383
|
try {
|
|
1211
|
-
const { stdout } = await execFileAsync("secret-tool", [
|
|
1384
|
+
const { stdout: stdout2 } = await execFileAsync("secret-tool", [
|
|
1212
1385
|
"lookup",
|
|
1213
1386
|
"service",
|
|
1214
1387
|
"tostudy-cli-session",
|
|
1215
1388
|
"account",
|
|
1216
1389
|
account
|
|
1217
1390
|
]);
|
|
1218
|
-
return JSON.parse(
|
|
1391
|
+
return JSON.parse(stdout2.trim());
|
|
1219
1392
|
} catch {
|
|
1220
1393
|
return null;
|
|
1221
1394
|
}
|
|
@@ -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:", ""];
|
|
@@ -2045,6 +2218,7 @@ var runtime_registry_exports = {};
|
|
|
2045
2218
|
__export(runtime_registry_exports, {
|
|
2046
2219
|
RUNTIMES: () => RUNTIMES,
|
|
2047
2220
|
detectedSlashHints: () => detectedSlashHints,
|
|
2221
|
+
needsRootAgentsConsent: () => needsRootAgentsConsent,
|
|
2048
2222
|
upsertRootAgentsBlock: () => upsertRootAgentsBlock
|
|
2049
2223
|
});
|
|
2050
2224
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -2083,6 +2257,15 @@ ${content}`;
|
|
|
2083
2257
|
function detectedSlashHints(slug, home, cwd) {
|
|
2084
2258
|
return RUNTIMES.filter((r) => r.slashHint && r.detect(home, cwd)).map((r) => r.slashHint(slug));
|
|
2085
2259
|
}
|
|
2260
|
+
function needsRootAgentsConsent(cwd) {
|
|
2261
|
+
const filePath = join2(cwd, "AGENTS.md");
|
|
2262
|
+
if (!existsSync2(filePath)) return false;
|
|
2263
|
+
try {
|
|
2264
|
+
return !TOSTUDY_BLOCK_PATTERN.test(readFileSync(filePath, "utf-8"));
|
|
2265
|
+
} catch {
|
|
2266
|
+
return false;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2086
2269
|
function upsertRootAgentsBlock(cwd, ctx) {
|
|
2087
2270
|
const slug = slugifyTitle(ctx.courseTitle);
|
|
2088
2271
|
const block = [
|
|
@@ -2261,9 +2444,10 @@ ${content}`;
|
|
|
2261
2444
|
name: "Generic (AGENTS.md)",
|
|
2262
2445
|
// Windsurf is the remaining consumer of the generic universal install.
|
|
2263
2446
|
detect: (home) => existsSync2(join2(home, ".codeium", "windsurf")),
|
|
2264
|
-
writePerCourse({ cwd, slug, ctx, content }) {
|
|
2447
|
+
writePerCourse({ cwd, slug, ctx, content, rootAgentsConsent }) {
|
|
2265
2448
|
writeFile2(join2(cwd, ".tostudy", "courses", slug), "AGENTS.md", content);
|
|
2266
2449
|
const written = [`.tostudy/courses/${slug}/AGENTS.md`];
|
|
2450
|
+
if (needsRootAgentsConsent(cwd) && rootAgentsConsent !== true) return written;
|
|
2267
2451
|
const rootAgents = upsertRootAgentsBlock(cwd, ctx);
|
|
2268
2452
|
if (rootAgents) written.push(rootAgents);
|
|
2269
2453
|
return written;
|
|
@@ -2284,6 +2468,7 @@ ${content}`;
|
|
|
2284
2468
|
var instruction_files_exports = {};
|
|
2285
2469
|
__export(instruction_files_exports, {
|
|
2286
2470
|
installUniversalCommand: () => installUniversalCommand,
|
|
2471
|
+
needsRootAgentsConsent: () => needsRootAgentsConsent,
|
|
2287
2472
|
slugify: () => slugify,
|
|
2288
2473
|
upsertRootAgentsBlock: () => upsertRootAgentsBlock,
|
|
2289
2474
|
writeInstructionFiles: () => writeInstructionFiles
|
|
@@ -2293,8 +2478,9 @@ function writeInstructionFiles(cwd, ctx, content, opts = {}) {
|
|
|
2293
2478
|
const home = opts.home ?? homedir();
|
|
2294
2479
|
const force = opts.allRuntimes ?? false;
|
|
2295
2480
|
const slug = slugify(ctx.courseTitle);
|
|
2481
|
+
const rootAgentsConsent = opts.rootAgentsConsent ?? false;
|
|
2296
2482
|
const written = RUNTIMES.flatMap(
|
|
2297
|
-
(r) => r.writePerCourse?.({ cwd, home, slug, ctx, content, force }) ?? []
|
|
2483
|
+
(r) => r.writePerCourse?.({ cwd, home, slug, ctx, content, force, rootAgentsConsent }) ?? []
|
|
2298
2484
|
);
|
|
2299
2485
|
logger3.info("Generated instruction files (v3)", { slug, files: written });
|
|
2300
2486
|
return written;
|
|
@@ -2578,6 +2764,101 @@ var init_user_profile = __esm({
|
|
|
2578
2764
|
}
|
|
2579
2765
|
});
|
|
2580
2766
|
|
|
2767
|
+
// src/errors/errors-pt-br.ts
|
|
2768
|
+
var ptBrErrors;
|
|
2769
|
+
var init_errors_pt_br = __esm({
|
|
2770
|
+
"src/errors/errors-pt-br.ts"() {
|
|
2771
|
+
"use strict";
|
|
2772
|
+
ptBrErrors = {
|
|
2773
|
+
notFound: "n\xE3o encontrado",
|
|
2774
|
+
workspaceNotFoundShort: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' primeiro.\n",
|
|
2775
|
+
workspaceNotFoundFromExport: "\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' ou rode 'tostudy select' desta pasta.\n",
|
|
2776
|
+
vaultNotFound: "\u274C Vault n\xE3o encontrado. Execute 'tostudy vault init' primeiro.\n",
|
|
2777
|
+
logoutSuccess: "\n Deslogado com sucesso.\n",
|
|
2778
|
+
workspaceCommandDescription: "Gerenciar workspace de estudo local",
|
|
2779
|
+
workspaceSetupDescription: "Criar estrutura do workspace para o curso ativo",
|
|
2780
|
+
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",
|
|
2781
|
+
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",
|
|
2782
|
+
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",
|
|
2783
|
+
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"
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2787
|
+
|
|
2788
|
+
// src/errors/errors-en-us.ts
|
|
2789
|
+
var enUsErrors;
|
|
2790
|
+
var init_errors_en_us = __esm({
|
|
2791
|
+
"src/errors/errors-en-us.ts"() {
|
|
2792
|
+
"use strict";
|
|
2793
|
+
enUsErrors = {
|
|
2794
|
+
notFound: "not found",
|
|
2795
|
+
workspaceNotFoundShort: "\u274C Workspace not found. Run 'tostudy workspace setup' first.\n",
|
|
2796
|
+
workspaceNotFoundFromExport: "\u274C Workspace not found. Run 'tostudy workspace setup' or 'tostudy select' from this folder.\n",
|
|
2797
|
+
vaultNotFound: "\u274C Vault not found. Run 'tostudy vault init' first.\n",
|
|
2798
|
+
logoutSuccess: "\n Logged out successfully.\n",
|
|
2799
|
+
workspaceCommandDescription: "Manage local study workspace",
|
|
2800
|
+
workspaceSetupDescription: "Create the workspace structure for the active course",
|
|
2801
|
+
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",
|
|
2802
|
+
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",
|
|
2803
|
+
courseArchived: "This course has been archived and is available for reading only.\nUse `tostudy lesson` to review the content you've already studied.\n",
|
|
2804
|
+
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"
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
|
|
2809
|
+
// src/errors/index.ts
|
|
2810
|
+
function resolveLocale() {
|
|
2811
|
+
if (_cachedLocale !== null) return _cachedLocale;
|
|
2812
|
+
const argv = process.argv;
|
|
2813
|
+
const flagIdx = argv.indexOf("--locale");
|
|
2814
|
+
if (flagIdx !== -1 && flagIdx + 1 < argv.length) {
|
|
2815
|
+
const val = argv[flagIdx + 1];
|
|
2816
|
+
if (val === "pt-BR" || val === "en-US") {
|
|
2817
|
+
_cachedLocale = val;
|
|
2818
|
+
return val;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
const lang = (process.env["LANG"] ?? "").toLowerCase();
|
|
2822
|
+
if (lang.startsWith("en")) {
|
|
2823
|
+
_cachedLocale = "en-US";
|
|
2824
|
+
return "en-US";
|
|
2825
|
+
}
|
|
2826
|
+
_cachedLocale = "pt-BR";
|
|
2827
|
+
return "pt-BR";
|
|
2828
|
+
}
|
|
2829
|
+
function getErrors(locale) {
|
|
2830
|
+
return BUNDLES[locale ?? resolveLocale()];
|
|
2831
|
+
}
|
|
2832
|
+
function resolveErrorCode(err) {
|
|
2833
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
2834
|
+
const code = err.code;
|
|
2835
|
+
if (typeof code === "string" && code.length > 0) return code;
|
|
2836
|
+
}
|
|
2837
|
+
return err instanceof Error ? err.message : String(err);
|
|
2838
|
+
}
|
|
2839
|
+
function isInsufficientCreditsError(err) {
|
|
2840
|
+
return resolveErrorCode(err).includes("INSUFFICIENT_CREDITS");
|
|
2841
|
+
}
|
|
2842
|
+
function isCourseArchivedError(err) {
|
|
2843
|
+
return resolveErrorCode(err).includes("COURSE_ARCHIVED");
|
|
2844
|
+
}
|
|
2845
|
+
function isEnrollmentNotEntitledError(err) {
|
|
2846
|
+
return resolveErrorCode(err).includes("ENROLLMENT_NOT_ENTITLED");
|
|
2847
|
+
}
|
|
2848
|
+
var BUNDLES, _cachedLocale;
|
|
2849
|
+
var init_errors = __esm({
|
|
2850
|
+
"src/errors/index.ts"() {
|
|
2851
|
+
"use strict";
|
|
2852
|
+
init_errors_pt_br();
|
|
2853
|
+
init_errors_en_us();
|
|
2854
|
+
BUNDLES = {
|
|
2855
|
+
"pt-BR": ptBrErrors,
|
|
2856
|
+
"en-US": enUsErrors
|
|
2857
|
+
};
|
|
2858
|
+
_cachedLocale = null;
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
|
|
2581
2862
|
// src/workspace/workspace-marker.ts
|
|
2582
2863
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
2583
2864
|
import os4 from "node:os";
|
|
@@ -2607,9 +2888,7 @@ async function writeWorkspaceMarker(workspacePath, input2, options = {}) {
|
|
|
2607
2888
|
const homeDir = path7.resolve(options.homeDir ?? os4.homedir());
|
|
2608
2889
|
const resolvedWorkspace = path7.resolve(workspacePath);
|
|
2609
2890
|
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
|
-
);
|
|
2891
|
+
throw new Error(getErrors().workspaceRefusedAtHome);
|
|
2613
2892
|
}
|
|
2614
2893
|
const filePath = resolveMarkerPath(workspacePath);
|
|
2615
2894
|
const dir = path7.dirname(filePath);
|
|
@@ -2634,6 +2913,7 @@ var CURRENT_VERSION, SUPPORTED_VERSIONS;
|
|
|
2634
2913
|
var init_workspace_marker = __esm({
|
|
2635
2914
|
"src/workspace/workspace-marker.ts"() {
|
|
2636
2915
|
"use strict";
|
|
2916
|
+
init_errors();
|
|
2637
2917
|
CURRENT_VERSION = 1;
|
|
2638
2918
|
SUPPORTED_VERSIONS = /* @__PURE__ */ new Set([1, 2]);
|
|
2639
2919
|
}
|
|
@@ -2785,99 +3065,6 @@ var init_course_state = __esm({
|
|
|
2785
3065
|
}
|
|
2786
3066
|
});
|
|
2787
3067
|
|
|
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
3068
|
// src/commands/logout.ts
|
|
2882
3069
|
import { Command as Command2 } from "commander";
|
|
2883
3070
|
var logoutCommand;
|
|
@@ -3196,7 +3383,7 @@ var CLI_VERSION;
|
|
|
3196
3383
|
var init_version = __esm({
|
|
3197
3384
|
"src/version.ts"() {
|
|
3198
3385
|
"use strict";
|
|
3199
|
-
CLI_VERSION = true ? "0.16.
|
|
3386
|
+
CLI_VERSION = true ? "0.16.6" : "0.7.1";
|
|
3200
3387
|
}
|
|
3201
3388
|
});
|
|
3202
3389
|
|
|
@@ -4030,7 +4217,7 @@ async function writeFilesAdapter(cwd, courseMeta, content, opts = {}) {
|
|
|
4030
4217
|
courseDescription: courseMeta.courseDescription
|
|
4031
4218
|
},
|
|
4032
4219
|
content,
|
|
4033
|
-
{ allRuntimes: opts.allRuntimes }
|
|
4220
|
+
{ allRuntimes: opts.allRuntimes, rootAgentsConsent: opts.rootAgentsConsent }
|
|
4034
4221
|
);
|
|
4035
4222
|
}
|
|
4036
4223
|
function buildPipelineDeps(opts = {}) {
|
|
@@ -4053,6 +4240,43 @@ var init_pipeline_deps = __esm({
|
|
|
4053
4240
|
}
|
|
4054
4241
|
});
|
|
4055
4242
|
|
|
4243
|
+
// src/commands/_shared/root-agents-consent.ts
|
|
4244
|
+
import readline2 from "node:readline/promises";
|
|
4245
|
+
import { stdin, stdout } from "node:process";
|
|
4246
|
+
function rootAgentsSkippedHint() {
|
|
4247
|
+
return " AGENTS.md desta pasta n\xE3o foi alterado \u2014 rode `tostudy sync --root-agents` para adicionar o bloco do ToStudy.";
|
|
4248
|
+
}
|
|
4249
|
+
async function askViaReadline(question) {
|
|
4250
|
+
const rl = readline2.createInterface({ input: stdin, output: stdout });
|
|
4251
|
+
try {
|
|
4252
|
+
return await rl.question(question);
|
|
4253
|
+
} finally {
|
|
4254
|
+
rl.close();
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
async function resolveRootAgentsConsent(input2) {
|
|
4258
|
+
const needs = (input2.needsConsent ?? needsRootAgentsConsent)(input2.cwd);
|
|
4259
|
+
if (!needs) return { consent: true, asked: false };
|
|
4260
|
+
if (input2.flag === true) return { consent: true, asked: true };
|
|
4261
|
+
if (!input2.interactive) return { consent: false, asked: true };
|
|
4262
|
+
const answer = (await (input2.ask ?? askViaReadline)(ROOT_AGENTS_QUESTION)).trim().toLowerCase();
|
|
4263
|
+
return {
|
|
4264
|
+
consent: answer === "s" || answer === "sim" || answer === "y" || answer === "yes",
|
|
4265
|
+
asked: true
|
|
4266
|
+
};
|
|
4267
|
+
}
|
|
4268
|
+
function isInteractiveTerminal(json2) {
|
|
4269
|
+
return Boolean(stdin.isTTY && stdout.isTTY) && json2 !== true;
|
|
4270
|
+
}
|
|
4271
|
+
var ROOT_AGENTS_QUESTION;
|
|
4272
|
+
var init_root_agents_consent = __esm({
|
|
4273
|
+
"src/commands/_shared/root-agents-consent.ts"() {
|
|
4274
|
+
"use strict";
|
|
4275
|
+
init_runtime_registry();
|
|
4276
|
+
ROOT_AGENTS_QUESTION = "Esta pasta j\xE1 tem um AGENTS.md. Adicionar o bloco do ToStudy nele? [s/N] ";
|
|
4277
|
+
}
|
|
4278
|
+
});
|
|
4279
|
+
|
|
4056
4280
|
// src/commands/select.ts
|
|
4057
4281
|
import { Command as Command6 } from "commander";
|
|
4058
4282
|
import os7 from "node:os";
|
|
@@ -4072,8 +4296,12 @@ var init_select = __esm({
|
|
|
4072
4296
|
init_status();
|
|
4073
4297
|
init_instruction_pipeline();
|
|
4074
4298
|
init_pipeline_deps();
|
|
4299
|
+
init_root_agents_consent();
|
|
4075
4300
|
logger8 = createLogger("cli:select");
|
|
4076
|
-
selectCommand = new Command6("select").description("Activate a course by ID or list index number").argument("<course>", "Course ID (UUID) or index number from `tostudy courses`").option("--json", "Output structured JSON").
|
|
4301
|
+
selectCommand = new Command6("select").description("Activate a course by ID or list index number").argument("<course>", "Course ID (UUID) or index number from `tostudy courses`").option("--json", "Output structured JSON").option(
|
|
4302
|
+
"--root-agents",
|
|
4303
|
+
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
4304
|
+
).action(async (course, opts) => {
|
|
4077
4305
|
try {
|
|
4078
4306
|
const session = await requireSession();
|
|
4079
4307
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
@@ -4108,12 +4336,20 @@ var init_select = __esm({
|
|
|
4108
4336
|
enrollmentId
|
|
4109
4337
|
};
|
|
4110
4338
|
const onboarding = await getCourseOnboardingStatus(activeCourseForStatus);
|
|
4339
|
+
const rootAgents = await resolveRootAgentsConsent({
|
|
4340
|
+
cwd: process.cwd(),
|
|
4341
|
+
flag: opts.rootAgents,
|
|
4342
|
+
interactive: isInteractiveTerminal(opts.json)
|
|
4343
|
+
});
|
|
4111
4344
|
let pipelineResult = null;
|
|
4112
4345
|
try {
|
|
4113
4346
|
pipelineResult = await resolveAndGenerate(
|
|
4114
4347
|
{ courseId: detail.courseId, enrollmentId },
|
|
4115
4348
|
{ forceRefresh: false, collectT1Interactive: false, collectT2Interactive: false },
|
|
4116
|
-
{
|
|
4349
|
+
{
|
|
4350
|
+
cwd: process.cwd(),
|
|
4351
|
+
deps: buildPipelineDeps({ rootAgentsConsent: rootAgents.consent })
|
|
4352
|
+
}
|
|
4117
4353
|
);
|
|
4118
4354
|
} catch (err) {
|
|
4119
4355
|
logger8.warn("Failed to run instruction pipeline", {
|
|
@@ -4131,6 +4367,7 @@ var init_select = __esm({
|
|
|
4131
4367
|
workspacePath: onboarding.workspacePath,
|
|
4132
4368
|
workspaceSource: onboarding.workspaceSource,
|
|
4133
4369
|
wroteFiles: pipelineResult?.wroteFiles ?? [],
|
|
4370
|
+
rootAgentsSkipped: rootAgents.asked && !rootAgents.consent,
|
|
4134
4371
|
markerPath: pipelineResult?.markerPath ?? null,
|
|
4135
4372
|
usedT0: pipelineResult?.usedT0 ?? false,
|
|
4136
4373
|
usedT1: pipelineResult?.usedT1 ?? false,
|
|
@@ -4167,6 +4404,9 @@ var init_select = __esm({
|
|
|
4167
4404
|
if (pipelineResult && !pipelineResult.usedT1) {
|
|
4168
4405
|
lines.push(" (sem brief base \u2014 rode `tostudy init` para configurar)");
|
|
4169
4406
|
}
|
|
4407
|
+
if (rootAgents.asked && !rootAgents.consent) {
|
|
4408
|
+
lines.push(rootAgentsSkippedHint());
|
|
4409
|
+
}
|
|
4170
4410
|
lines.push("", "\u2192 Abra sua plataforma (claude, codex, cursor...)");
|
|
4171
4411
|
const hints = courseSlug2 ? detectedSlashHints(courseSlug2, home, cwd) : [];
|
|
4172
4412
|
if (hints.length > 0) {
|
|
@@ -19926,7 +20166,7 @@ var init_menu = __esm({
|
|
|
19926
20166
|
});
|
|
19927
20167
|
|
|
19928
20168
|
// src/onboarding/learner-context.ts
|
|
19929
|
-
import
|
|
20169
|
+
import readline3 from "node:readline/promises";
|
|
19930
20170
|
import { stdin as input, stdout as output2 } from "node:process";
|
|
19931
20171
|
async function askNonEmpty2(question, deps, defaultValue) {
|
|
19932
20172
|
while (true) {
|
|
@@ -19944,7 +20184,7 @@ async function askChoice(question, choices, deps, defaultChoice) {
|
|
|
19944
20184
|
}
|
|
19945
20185
|
}
|
|
19946
20186
|
function createPromptDeps() {
|
|
19947
|
-
const rl =
|
|
20187
|
+
const rl = readline3.createInterface({ input, output: output2 });
|
|
19948
20188
|
return {
|
|
19949
20189
|
ask: (question) => rl.question(question),
|
|
19950
20190
|
write: (content) => output2.write(content),
|
|
@@ -20167,10 +20407,18 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
|
|
|
20167
20407
|
});
|
|
20168
20408
|
await deps.saveCourseLearnerProfile(activeCourse, learnerProfile, artifacts);
|
|
20169
20409
|
try {
|
|
20410
|
+
const rootAgents = await resolveRootAgentsConsent({
|
|
20411
|
+
cwd: process.cwd(),
|
|
20412
|
+
flag: flags.rootAgents,
|
|
20413
|
+
interactive: isInteractiveTerminal(flags.json)
|
|
20414
|
+
});
|
|
20170
20415
|
const pipelineResult = await deps.resolveAndGenerate(
|
|
20171
20416
|
{ courseId: activeCourse.courseId, enrollmentId: activeCourse.enrollmentId },
|
|
20172
20417
|
{ forceRefresh: false, collectT1Interactive: true, collectT2Interactive: false },
|
|
20173
|
-
{
|
|
20418
|
+
{
|
|
20419
|
+
cwd: process.cwd(),
|
|
20420
|
+
deps: deps.buildPipelineDeps({ rootAgentsConsent: rootAgents.consent })
|
|
20421
|
+
}
|
|
20174
20422
|
);
|
|
20175
20423
|
deps.logger.info("Instruction files regenerated via pipeline", {
|
|
20176
20424
|
wroteFiles: pipelineResult.wroteFiles,
|
|
@@ -20211,6 +20459,7 @@ var init_init = __esm({
|
|
|
20211
20459
|
init_learner_context();
|
|
20212
20460
|
init_api3();
|
|
20213
20461
|
init_instruction_pipeline();
|
|
20462
|
+
init_root_agents_consent();
|
|
20214
20463
|
init_pipeline_deps();
|
|
20215
20464
|
logger17 = createLogger("cli:init");
|
|
20216
20465
|
defaultDeps4 = {
|
|
@@ -20231,7 +20480,10 @@ var init_init = __esm({
|
|
|
20231
20480
|
resolveAndGenerate,
|
|
20232
20481
|
buildPipelineDeps
|
|
20233
20482
|
};
|
|
20234
|
-
initCommand = new Command16("init").description("Generate tutor instructions and learner brief for the active course").option("--segment <segment>", "Learner segment/niche").option("--company <company>", "Company or business type").option("--products <products>", "Main products or services").option("--region <region>", "Operating region").option("--team <team>", "Team involved").option("--goal <goal>", "Primary learning goal").option("--level <level>", "Learner level: beginner, intermediate, advanced").option("--adapt-context", "Adapt examples to learner's real context").option(
|
|
20483
|
+
initCommand = new Command16("init").description("Generate tutor instructions and learner brief for the active course").option("--segment <segment>", "Learner segment/niche").option("--company <company>", "Company or business type").option("--products <products>", "Main products or services").option("--region <region>", "Operating region").option("--team <team>", "Team involved").option("--goal <goal>", "Primary learning goal").option("--level <level>", "Learner level: beginner, intermediate, advanced").option("--adapt-context", "Adapt examples to learner's real context").option(
|
|
20484
|
+
"--root-agents",
|
|
20485
|
+
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
20486
|
+
).option("--json", "Output structured JSON").action(async (opts) => {
|
|
20235
20487
|
await runInit(defaultDeps4, opts);
|
|
20236
20488
|
});
|
|
20237
20489
|
}
|
|
@@ -21233,20 +21485,38 @@ var init_sync = __esm({
|
|
|
21233
21485
|
init_guards();
|
|
21234
21486
|
init_formatter();
|
|
21235
21487
|
init_instruction_pipeline();
|
|
21488
|
+
init_root_agents_consent();
|
|
21236
21489
|
init_pipeline_deps();
|
|
21237
21490
|
logger22 = createLogger("cli:sync");
|
|
21238
21491
|
syncCommand = new Command22("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").option(
|
|
21239
21492
|
"--all-runtimes",
|
|
21240
21493
|
"Write instruction files for every supported runtime, even undetected ones"
|
|
21494
|
+
).option(
|
|
21495
|
+
"--root-agents",
|
|
21496
|
+
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
21241
21497
|
).action(async (opts) => {
|
|
21242
21498
|
try {
|
|
21243
21499
|
await requireSession();
|
|
21244
21500
|
const activeCourse = await requireActiveCourse();
|
|
21501
|
+
const rootAgents = await resolveRootAgentsConsent({
|
|
21502
|
+
cwd: process.cwd(),
|
|
21503
|
+
flag: opts.rootAgents,
|
|
21504
|
+
interactive: isInteractiveTerminal(opts.json)
|
|
21505
|
+
});
|
|
21245
21506
|
const pipelineResult = await resolveAndGenerate(
|
|
21246
21507
|
{ courseId: activeCourse.courseId, enrollmentId: activeCourse.enrollmentId },
|
|
21247
21508
|
{ forceRefresh: true, collectT1Interactive: false, collectT2Interactive: false },
|
|
21248
|
-
{
|
|
21509
|
+
{
|
|
21510
|
+
cwd: process.cwd(),
|
|
21511
|
+
deps: buildPipelineDeps({
|
|
21512
|
+
allRuntimes: opts.allRuntimes,
|
|
21513
|
+
rootAgentsConsent: rootAgents.consent
|
|
21514
|
+
})
|
|
21515
|
+
}
|
|
21249
21516
|
);
|
|
21517
|
+
if (rootAgents.asked && !rootAgents.consent && !opts.json) {
|
|
21518
|
+
output(rootAgentsSkippedHint(), { json: false });
|
|
21519
|
+
}
|
|
21250
21520
|
if (opts.json) {
|
|
21251
21521
|
output(
|
|
21252
21522
|
{
|
|
@@ -22238,7 +22508,10 @@ var init_export_grades = __esm({
|
|
|
22238
22508
|
defaultDeps5 = {
|
|
22239
22509
|
requireSession,
|
|
22240
22510
|
requireActiveCourse,
|
|
22241
|
-
|
|
22511
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22512
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22513
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22514
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22242
22515
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22243
22516
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22244
22517
|
// process.exit is typed as `never` — wrap so the cast is local to one line.
|
|
@@ -22425,7 +22698,10 @@ var init_grades2 = __esm({
|
|
|
22425
22698
|
logger33 = createLogger("cli:grades");
|
|
22426
22699
|
defaultDeps6 = {
|
|
22427
22700
|
requireSession,
|
|
22428
|
-
|
|
22701
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22702
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22703
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22704
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22429
22705
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22430
22706
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22431
22707
|
exit: (code) => process.exit(code),
|
|
@@ -22605,7 +22881,10 @@ var init_review2 = __esm({
|
|
|
22605
22881
|
logger34 = createLogger("cli:review");
|
|
22606
22882
|
defaultDeps7 = {
|
|
22607
22883
|
requireSession,
|
|
22608
|
-
|
|
22884
|
+
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
22885
|
+
// antes de qualquer guard. Em runtime sem `fetch` global isso derruba o import
|
|
22886
|
+
// inteiro do cli.ts e, com ele, TODO comando. Encaminhar mantem o load inerte.
|
|
22887
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
22609
22888
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22610
22889
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22611
22890
|
exit: (code) => process.exit(code),
|
|
@@ -22790,6 +23069,21 @@ var init_auto_updater = __esm({
|
|
|
22790
23069
|
if (!process.env.LOG_LEVEL) {
|
|
22791
23070
|
process.env.LOG_LEVEL = "fatal";
|
|
22792
23071
|
}
|
|
23072
|
+
var MIN_NODE_MAJOR = 18;
|
|
23073
|
+
var nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
|
|
23074
|
+
if (Number.isFinite(nodeMajor) && nodeMajor < MIN_NODE_MAJOR) {
|
|
23075
|
+
const pt = !(process.env["LANG"] ?? "").toLowerCase().startsWith("en");
|
|
23076
|
+
process.stderr.write(
|
|
23077
|
+
pt ? `Erro: o ToStudy CLI precisa do Node.js ${MIN_NODE_MAJOR} ou superior.
|
|
23078
|
+
Voce esta no Node.js ${process.versions.node}.
|
|
23079
|
+
Atualize em https://nodejs.org e rode o comando de novo.
|
|
23080
|
+
` : `Error: the ToStudy CLI requires Node.js ${MIN_NODE_MAJOR} or newer.
|
|
23081
|
+
You are on Node.js ${process.versions.node}.
|
|
23082
|
+
Update at https://nodejs.org and run the command again.
|
|
23083
|
+
`
|
|
23084
|
+
);
|
|
23085
|
+
process.exit(1);
|
|
23086
|
+
}
|
|
22793
23087
|
var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
|
|
22794
23088
|
var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
|
|
22795
23089
|
var { maybeAutoUpdate: maybeAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_updater(), auto_updater_exports));
|