@mo7yw4ng/openape 1.0.5 → 1.0.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/README.md +11 -8
- package/esm/deno.js +1 -1
- package/esm/src/commands/announcements.d.ts.map +1 -1
- package/esm/src/commands/announcements.js +22 -85
- package/esm/src/commands/assignments.d.ts.map +1 -1
- package/esm/src/commands/assignments.js +2 -3
- package/esm/src/commands/calendar.d.ts.map +1 -1
- package/esm/src/commands/calendar.js +32 -84
- package/esm/src/commands/courses.d.ts.map +1 -1
- package/esm/src/commands/courses.js +2 -38
- package/esm/src/commands/forums.d.ts.map +1 -1
- package/esm/src/commands/forums.js +47 -175
- package/esm/src/commands/grades.d.ts.map +1 -1
- package/esm/src/commands/grades.js +10 -47
- package/esm/src/commands/materials.d.ts.map +1 -1
- package/esm/src/commands/materials.js +47 -58
- package/esm/src/commands/quizzes.d.ts.map +1 -1
- package/esm/src/commands/quizzes.js +2 -37
- package/esm/src/commands/skills.js +3 -3
- package/esm/src/commands/upload.d.ts.map +1 -1
- package/esm/src/commands/upload.js +2 -5
- package/esm/src/commands/videos.d.ts.map +1 -1
- package/esm/src/commands/videos.js +6 -76
- package/esm/src/index.d.ts +2 -1
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +5 -1
- package/esm/src/lib/auth.d.ts +21 -2
- package/esm/src/lib/auth.d.ts.map +1 -1
- package/esm/src/lib/auth.js +78 -19
- package/esm/src/lib/logger.d.ts +2 -2
- package/esm/src/lib/logger.d.ts.map +1 -1
- package/esm/src/lib/logger.js +1 -2
- package/esm/src/lib/moodle.d.ts +14 -0
- package/esm/src/lib/moodle.d.ts.map +1 -1
- package/esm/src/lib/moodle.js +35 -0
- package/esm/src/lib/utils.d.ts +3 -8
- package/esm/src/lib/utils.d.ts.map +1 -1
- package/esm/src/lib/utils.js +3 -10
- package/package.json +1 -1
- package/script/deno.js +1 -1
- package/script/src/commands/announcements.d.ts.map +1 -1
- package/script/src/commands/announcements.js +23 -89
- package/script/src/commands/assignments.d.ts.map +1 -1
- package/script/src/commands/assignments.js +2 -3
- package/script/src/commands/calendar.d.ts.map +1 -1
- package/script/src/commands/calendar.js +33 -85
- package/script/src/commands/courses.d.ts.map +1 -1
- package/script/src/commands/courses.js +9 -48
- package/script/src/commands/forums.d.ts.map +1 -1
- package/script/src/commands/forums.js +50 -181
- package/script/src/commands/grades.d.ts.map +1 -1
- package/script/src/commands/grades.js +14 -54
- package/script/src/commands/materials.d.ts.map +1 -1
- package/script/src/commands/materials.js +47 -58
- package/script/src/commands/quizzes.d.ts.map +1 -1
- package/script/src/commands/quizzes.js +11 -49
- package/script/src/commands/skills.js +3 -3
- package/script/src/commands/upload.d.ts.map +1 -1
- package/script/src/commands/upload.js +2 -5
- package/script/src/commands/videos.d.ts.map +1 -1
- package/script/src/commands/videos.js +11 -81
- package/script/src/index.d.ts +2 -1
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +5 -1
- package/script/src/lib/auth.d.ts +21 -2
- package/script/src/lib/auth.d.ts.map +1 -1
- package/script/src/lib/auth.js +83 -56
- package/script/src/lib/logger.d.ts +2 -2
- package/script/src/lib/logger.d.ts.map +1 -1
- package/script/src/lib/logger.js +1 -2
- package/script/src/lib/moodle.d.ts +14 -0
- package/script/src/lib/moodle.d.ts.map +1 -1
- package/script/src/lib/moodle.js +36 -0
- package/script/src/lib/utils.d.ts +3 -8
- package/script/src/lib/utils.d.ts.map +1 -1
- package/script/src/lib/utils.js +3 -11
- package/skills/openape/SKILL.md +6 -6
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerQuizzesCommand = registerQuizzesCommand;
|
|
7
4
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
5
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
6
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
10
7
|
const index_js_1 = require("../index.js");
|
|
11
|
-
const token_js_1 = require("../lib/token.js");
|
|
12
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
8
|
function stripHtmlKeepLines(html) {
|
|
15
9
|
return html
|
|
16
10
|
.replace(/<br\s*\/?>/gi, "\n")
|
|
@@ -65,38 +59,6 @@ function parseQuizQuestions(questions) {
|
|
|
65
59
|
function registerQuizzesCommand(program) {
|
|
66
60
|
const quizzesCmd = program.command("quizzes");
|
|
67
61
|
quizzesCmd.description("Quiz operations");
|
|
68
|
-
function getOutputFormat(command) {
|
|
69
|
-
const opts = command.optsWithGlobals();
|
|
70
|
-
return opts.output || "json";
|
|
71
|
-
}
|
|
72
|
-
// Pure API context - no browser required (fast!)
|
|
73
|
-
async function createApiContext(options, command) {
|
|
74
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
75
|
-
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
76
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
77
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
78
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
79
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
80
|
-
// Check if session exists
|
|
81
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
82
|
-
log.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
83
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
// Try to load WS token
|
|
87
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
88
|
-
if (!wsToken) {
|
|
89
|
-
log.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
log,
|
|
94
|
-
session: {
|
|
95
|
-
wsToken,
|
|
96
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
62
|
quizzesCmd
|
|
101
63
|
.command("list")
|
|
102
64
|
.description("List incomplete quizzes in a course")
|
|
@@ -104,8 +66,8 @@ function registerQuizzesCommand(program) {
|
|
|
104
66
|
.option("--all", "Include completed quizzes")
|
|
105
67
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
106
68
|
.action(async (courseId, options, command) => {
|
|
107
|
-
const output = getOutputFormat(command);
|
|
108
|
-
const apiContext = await createApiContext(options, command);
|
|
69
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
70
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
109
71
|
if (!apiContext) {
|
|
110
72
|
process.exitCode = 1;
|
|
111
73
|
return;
|
|
@@ -126,8 +88,8 @@ function registerQuizzesCommand(program) {
|
|
|
126
88
|
.option("--all", "Include completed quizzes")
|
|
127
89
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
128
90
|
.action(async (options, command) => {
|
|
129
|
-
const output = getOutputFormat(command);
|
|
130
|
-
const apiContext = await createApiContext(options, command);
|
|
91
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
92
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
131
93
|
if (!apiContext) {
|
|
132
94
|
process.exitCode = 1;
|
|
133
95
|
return;
|
|
@@ -167,8 +129,8 @@ function registerQuizzesCommand(program) {
|
|
|
167
129
|
.argument("<quiz-id>", "Quiz ID")
|
|
168
130
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
169
131
|
.action(async (quizCmid, options, command) => {
|
|
170
|
-
const output = getOutputFormat(command);
|
|
171
|
-
const apiContext = await createApiContext(options, command);
|
|
132
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
133
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
172
134
|
if (!apiContext) {
|
|
173
135
|
process.exitCode = 1;
|
|
174
136
|
return;
|
|
@@ -205,8 +167,8 @@ function registerQuizzesCommand(program) {
|
|
|
205
167
|
.option("--page <number>", "Page number (-1 for all pages)", "-1")
|
|
206
168
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
207
169
|
.action(async (attemptId, options, command) => {
|
|
208
|
-
const output = getOutputFormat(command);
|
|
209
|
-
const apiContext = await createApiContext(options, command);
|
|
170
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
171
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
210
172
|
if (!apiContext) {
|
|
211
173
|
process.exitCode = 1;
|
|
212
174
|
return;
|
|
@@ -240,8 +202,8 @@ function registerQuizzesCommand(program) {
|
|
|
240
202
|
.option("--submit", "Submit the attempt after saving")
|
|
241
203
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
242
204
|
.action(async (attemptId, answersJson, options, command) => {
|
|
243
|
-
const output = getOutputFormat(command);
|
|
244
|
-
const apiContext = await createApiContext(options, command);
|
|
205
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
206
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
245
207
|
if (!apiContext) {
|
|
246
208
|
process.exitCode = 1;
|
|
247
209
|
return;
|
|
@@ -7,6 +7,7 @@ exports.registerSkillsCommand = registerSkillsCommand;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
10
|
+
const node_url_1 = require("node:url");
|
|
10
11
|
const SKILL_NAME = "openape";
|
|
11
12
|
const GITHUB_RAW_URL = `https://raw.githubusercontent.com/mo7yw4ng/openape/refs/heads/main/skills/${SKILL_NAME}/SKILL.md`;
|
|
12
13
|
/**
|
|
@@ -24,11 +25,10 @@ const PLATFORMS = {
|
|
|
24
25
|
async function readSkillContent() {
|
|
25
26
|
// Try local path first (relative to this file's location)
|
|
26
27
|
try {
|
|
27
|
-
const base = node_path_1.default.dirname(
|
|
28
|
-
const normalized = process.platform === "win32" ? base.replace(/^\//, "") : base;
|
|
28
|
+
const base = node_path_1.default.dirname((0, node_url_1.fileURLToPath)(globalThis[Symbol.for("import-meta-ponyfill-commonjs")](require, module).url));
|
|
29
29
|
// When running from source: src/commands/ → ../../skills/openape/SKILL.md
|
|
30
30
|
// When bundled by dnt into build/: esm/commands/ or script/ → ../../skills/openape/SKILL.md
|
|
31
|
-
const localPath = node_path_1.default.resolve(
|
|
31
|
+
const localPath = node_path_1.default.resolve(base, "..", "..", "skills", SKILL_NAME, "SKILL.md");
|
|
32
32
|
return await node_fs_1.default.promises.readFile(localPath, "utf-8");
|
|
33
33
|
}
|
|
34
34
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/src/commands/upload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/src/commands/upload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwD5D"}
|
|
@@ -26,19 +26,16 @@ function registerUploadCommand(program) {
|
|
|
26
26
|
process.exitCode = 1;
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
// Resolve file path
|
|
30
29
|
const resolvedPath = node_path_1.default.resolve(filePath);
|
|
31
|
-
|
|
30
|
+
let stats;
|
|
32
31
|
try {
|
|
33
|
-
await promises_1.default.
|
|
32
|
+
stats = await promises_1.default.stat(resolvedPath);
|
|
34
33
|
}
|
|
35
34
|
catch {
|
|
36
35
|
apiContext.log.error(`檔案不存在: ${filePath}`);
|
|
37
36
|
process.exitCode = 1;
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
|
-
// Get file size
|
|
41
|
-
const stats = await promises_1.default.stat(resolvedPath);
|
|
42
39
|
const fileSizeKB = (0, utils_js_1.formatFileSize)(stats.size);
|
|
43
40
|
apiContext.log.info(`上傳檔案: ${node_path_1.default.basename(resolvedPath)} (${fileSizeKB} KB)`);
|
|
44
41
|
// Upload file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videos.d.ts","sourceRoot":"","sources":["../../../src/src/commands/videos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"videos.d.ts","sourceRoot":"","sources":["../../../src/src/commands/videos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgT5D"}
|
|
@@ -6,80 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.registerVideosCommand = registerVideosCommand;
|
|
7
7
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
8
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const logger_js_1 = require("../lib/logger.js");
|
|
10
9
|
const auth_js_1 = require("../lib/auth.js");
|
|
11
|
-
const session_js_1 = require("../lib/session.js");
|
|
12
|
-
const auth_js_2 = require("../lib/auth.js");
|
|
13
10
|
const index_js_1 = require("../index.js");
|
|
14
|
-
const token_js_1 = require("../lib/token.js");
|
|
15
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
13
|
function registerVideosCommand(program) {
|
|
18
14
|
const videosCmd = program.command("videos");
|
|
19
15
|
videosCmd.description("Video progress operations");
|
|
20
|
-
// Pure API context - no browser required (fast!)
|
|
21
|
-
async function createApiContext(options, command) {
|
|
22
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
23
|
-
// Don't silence logs for commands that don't have explicit output format control
|
|
24
|
-
const outputFormat = command && command.optsWithGlobals ? (0, utils_js_1.getOutputFormat)(command) : "table";
|
|
25
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
26
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
27
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
28
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
29
|
-
// Check if session exists
|
|
30
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
31
|
-
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
32
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
// Try to load WS token
|
|
36
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
37
|
-
if (!wsToken) {
|
|
38
|
-
console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
log,
|
|
43
|
-
session: {
|
|
44
|
-
wsToken,
|
|
45
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
// Helper function to create session context (for browser-only commands)
|
|
50
|
-
async function createSessionContext(options, command) {
|
|
51
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
52
|
-
const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
53
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
54
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
55
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
56
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
57
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
58
|
-
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
const config = {
|
|
62
|
-
username: "",
|
|
63
|
-
password: "",
|
|
64
|
-
courseUrl: "",
|
|
65
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
66
|
-
headless: !options.headed,
|
|
67
|
-
slowMo: 0,
|
|
68
|
-
authStatePath: sessionPath,
|
|
69
|
-
ollamaBaseUrl: "",
|
|
70
|
-
};
|
|
71
|
-
log.info("啟動瀏覽器...");
|
|
72
|
-
const { browser, context, page } = await (0, auth_js_1.launchAuthenticated)(config, log);
|
|
73
|
-
try {
|
|
74
|
-
const session = await (0, session_js_1.extractSessionInfo)(page, config, log);
|
|
75
|
-
return { log, page, session, browser, context };
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
await context.close();
|
|
79
|
-
await browser.close();
|
|
80
|
-
throw err;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
16
|
videosCmd
|
|
84
17
|
.command("list")
|
|
85
18
|
.description("List videos in a course")
|
|
@@ -88,7 +21,7 @@ function registerVideosCommand(program) {
|
|
|
88
21
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
89
22
|
.action(async (courseId, options, command) => {
|
|
90
23
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
91
|
-
const apiContext = await createApiContext(options, command);
|
|
24
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
92
25
|
if (!apiContext) {
|
|
93
26
|
process.exitCode = 1;
|
|
94
27
|
return;
|
|
@@ -109,7 +42,7 @@ function registerVideosCommand(program) {
|
|
|
109
42
|
.action(async (courseId, options, command) => {
|
|
110
43
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
111
44
|
// Get API context for getting incomplete videos and completion
|
|
112
|
-
const apiContext = await createApiContext(options, command);
|
|
45
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
113
46
|
if (!apiContext) {
|
|
114
47
|
process.exitCode = 1;
|
|
115
48
|
return;
|
|
@@ -134,7 +67,7 @@ function registerVideosCommand(program) {
|
|
|
134
67
|
return;
|
|
135
68
|
}
|
|
136
69
|
// Need browser only for getting viewId and duration (not needed for dry-run)
|
|
137
|
-
const context = await
|
|
70
|
+
const context = await (0, auth_js_1.createBrowserContext)(options, command);
|
|
138
71
|
if (!context) {
|
|
139
72
|
process.exitCode = 1;
|
|
140
73
|
return;
|
|
@@ -171,7 +104,7 @@ function registerVideosCommand(program) {
|
|
|
171
104
|
}
|
|
172
105
|
}
|
|
173
106
|
finally {
|
|
174
|
-
await (0,
|
|
107
|
+
await (0, auth_js_1.closeBrowserSafely)(browser, browserContext);
|
|
175
108
|
}
|
|
176
109
|
});
|
|
177
110
|
videosCmd
|
|
@@ -182,7 +115,7 @@ function registerVideosCommand(program) {
|
|
|
182
115
|
.action(async (options, command) => {
|
|
183
116
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
184
117
|
// Get API context for getting incomplete videos and completion
|
|
185
|
-
const apiContext = await createApiContext(options, command);
|
|
118
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
186
119
|
if (!apiContext) {
|
|
187
120
|
process.exitCode = 1;
|
|
188
121
|
return;
|
|
@@ -227,7 +160,7 @@ function registerVideosCommand(program) {
|
|
|
227
160
|
return;
|
|
228
161
|
}
|
|
229
162
|
// Need browser only for getting viewId and duration (not needed for dry-run)
|
|
230
|
-
const context = await
|
|
163
|
+
const context = await (0, auth_js_1.createBrowserContext)(options, command);
|
|
231
164
|
if (!context) {
|
|
232
165
|
process.exitCode = 1;
|
|
233
166
|
return;
|
|
@@ -272,7 +205,7 @@ function registerVideosCommand(program) {
|
|
|
272
205
|
}
|
|
273
206
|
}
|
|
274
207
|
finally {
|
|
275
|
-
await (0,
|
|
208
|
+
await (0, auth_js_1.closeBrowserSafely)(browser, browserContext);
|
|
276
209
|
}
|
|
277
210
|
});
|
|
278
211
|
videosCmd
|
|
@@ -282,7 +215,7 @@ function registerVideosCommand(program) {
|
|
|
282
215
|
.option("--output-dir <path>", "Output directory", "./downloads/videos")
|
|
283
216
|
.option("--incomplete-only", "Download only incomplete videos")
|
|
284
217
|
.action(async (courseId, options, command) => {
|
|
285
|
-
const context = await
|
|
218
|
+
const context = await (0, auth_js_1.createBrowserContext)(options, command);
|
|
286
219
|
if (!context) {
|
|
287
220
|
process.exitCode = 1;
|
|
288
221
|
return;
|
|
@@ -322,7 +255,7 @@ function registerVideosCommand(program) {
|
|
|
322
255
|
const completed = downloaded.filter(d => d.success).length;
|
|
323
256
|
const failed = downloaded.filter(d => !d.success).length;
|
|
324
257
|
log.info(`\n執行結果: ${completed} 成功, ${failed} 失敗`);
|
|
325
|
-
|
|
258
|
+
(0, index_js_1.formatAndOutput)(downloaded, "json", log, {
|
|
326
259
|
status: "success",
|
|
327
260
|
timestamp: new Date().toISOString(),
|
|
328
261
|
course_id: courseId,
|
|
@@ -330,13 +263,10 @@ function registerVideosCommand(program) {
|
|
|
330
263
|
total_videos: videos.length,
|
|
331
264
|
downloaded: completed,
|
|
332
265
|
failed,
|
|
333
|
-
})
|
|
334
|
-
for (const v of downloaded) {
|
|
335
|
-
console.log(JSON.stringify(v));
|
|
336
|
-
}
|
|
266
|
+
});
|
|
337
267
|
}
|
|
338
268
|
finally {
|
|
339
|
-
await (0,
|
|
269
|
+
await (0, auth_js_1.closeBrowserSafely)(browser, browserContext);
|
|
340
270
|
}
|
|
341
271
|
});
|
|
342
272
|
}
|
package/script/src/index.d.ts
CHANGED
|
@@ -21,7 +21,8 @@ export declare function createSessionContext(options: {
|
|
|
21
21
|
/**
|
|
22
22
|
* Helper to output formatted data.
|
|
23
23
|
* For JSON output (agent mode), exits immediately after output.
|
|
24
|
+
* If meta is provided, it is printed as the first line before items.
|
|
24
25
|
*/
|
|
25
|
-
export declare function formatAndOutput<T extends Record<string, unknown>>(data: T | T[], format: OutputFormat, log: Logger): void;
|
|
26
|
+
export declare function formatAndOutput<T extends Record<string, unknown>>(data: T | T[], format: OutputFormat, log: Logger, meta?: Record<string, unknown>): void;
|
|
26
27
|
export { createLogger, type AppConfig, type Logger, type SessionInfo, type OutputFormat };
|
|
27
28
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/src/index.ts"],"names":[],"mappings":";AACA,OAAO,sBAAsB,CAAC;AAO9B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAwEnF;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAAC,CAoBhH;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/src/index.ts"],"names":[],"mappings":";AACA,OAAO,sBAAsB,CAAC;AAO9B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAwEnF;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAAC,CAoBhH;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,EACb,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CA4BN;AA2CD,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,CAAC"}
|
package/script/src/index.js
CHANGED
|
@@ -94,9 +94,13 @@ async function createSessionContext(options) {
|
|
|
94
94
|
/**
|
|
95
95
|
* Helper to output formatted data.
|
|
96
96
|
* For JSON output (agent mode), exits immediately after output.
|
|
97
|
+
* If meta is provided, it is printed as the first line before items.
|
|
97
98
|
*/
|
|
98
|
-
function formatAndOutput(data, format, log) {
|
|
99
|
+
function formatAndOutput(data, format, log, meta) {
|
|
99
100
|
if (format === "json") {
|
|
101
|
+
if (meta) {
|
|
102
|
+
console.log(JSON.stringify(meta));
|
|
103
|
+
}
|
|
100
104
|
if (Array.isArray(data)) {
|
|
101
105
|
for (const item of data) {
|
|
102
106
|
console.log(JSON.stringify(item));
|
package/script/src/lib/auth.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Browser, type BrowserContext, type Page } from "playwright-core";
|
|
2
|
-
import type { AppConfig, Logger, OutputFormat } from "./types.js";
|
|
2
|
+
import type { AppConfig, Logger, OutputFormat, SessionInfo } from "./types.js";
|
|
3
3
|
/**
|
|
4
|
-
* Find a Chromium-based browser executable on Windows.
|
|
4
|
+
* Find a Chromium-based browser executable on Windows, macOS, or Linux.
|
|
5
5
|
* Priority: Edge → Chrome → Brave
|
|
6
6
|
*/
|
|
7
7
|
export declare function findEdgePath(): string;
|
|
@@ -44,4 +44,23 @@ export declare function createApiContext(options: {
|
|
|
44
44
|
moodleBaseUrl: string;
|
|
45
45
|
};
|
|
46
46
|
} | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Create an authenticated browser context for commands that need page access.
|
|
49
|
+
* Launches a browser, restores or creates a session, and extracts session info.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createBrowserContext(options: {
|
|
52
|
+
verbose?: boolean;
|
|
53
|
+
headed?: boolean;
|
|
54
|
+
}, command?: {
|
|
55
|
+
optsWithGlobals(): {
|
|
56
|
+
output?: OutputFormat;
|
|
57
|
+
verbose?: boolean;
|
|
58
|
+
};
|
|
59
|
+
}): Promise<{
|
|
60
|
+
log: Logger;
|
|
61
|
+
page: Page;
|
|
62
|
+
session: SessionInfo;
|
|
63
|
+
browser: Browser;
|
|
64
|
+
context: BrowserContext;
|
|
65
|
+
} | null>;
|
|
47
66
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/src/lib/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/src/lib/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM/E;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAiDrC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4DtF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,cAAc,EACxB,SAAS,GAAE,MAAa,EACxB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAoHD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAChD,OAAO,CAAC,EAAE;IAAE,eAAe,IAAI;QAAE,MAAM,CAAC,EAAE,YAAY,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAC5E,OAAO,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD,GAAG,IAAI,CAAC,CAoBR;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAChD,OAAO,CAAC,EAAE;IAAE,eAAe,IAAI;QAAE,MAAM,CAAC,EAAE,YAAY,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAC5E,OAAO,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,IAAI,CAAC,CA0BR"}
|
package/script/src/lib/auth.js
CHANGED
|
@@ -1,37 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
@@ -40,32 +7,66 @@ exports.findEdgePath = findEdgePath;
|
|
|
40
7
|
exports.launchAuthenticated = launchAuthenticated;
|
|
41
8
|
exports.closeBrowserSafely = closeBrowserSafely;
|
|
42
9
|
exports.createApiContext = createApiContext;
|
|
10
|
+
exports.createBrowserContext = createBrowserContext;
|
|
43
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
44
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
45
13
|
const playwright_core_1 = require("playwright-core");
|
|
46
14
|
const token_js_1 = require("./token.js");
|
|
15
|
+
const logger_js_1 = require("./logger.js");
|
|
16
|
+
const utils_js_1 = require("./utils.js");
|
|
17
|
+
const session_js_1 = require("./session.js");
|
|
47
18
|
/**
|
|
48
|
-
* Find a Chromium-based browser executable on Windows.
|
|
19
|
+
* Find a Chromium-based browser executable on Windows, macOS, or Linux.
|
|
49
20
|
* Priority: Edge → Chrome → Brave
|
|
50
21
|
*/
|
|
51
22
|
function findEdgePath() {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{ name: "Brave", suffix: "BraveSoftware\\Brave-Browser\\Application\\brave.exe" },
|
|
61
|
-
];
|
|
62
|
-
for (const { suffix } of browsers) {
|
|
63
|
-
for (const root of roots) {
|
|
64
|
-
const candidate = node_path_1.default.join(root, suffix);
|
|
23
|
+
const platform = process.platform;
|
|
24
|
+
if (platform === "darwin") {
|
|
25
|
+
const candidates = [
|
|
26
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
27
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
28
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
29
|
+
];
|
|
30
|
+
for (const candidate of candidates) {
|
|
65
31
|
if (node_fs_1.default.existsSync(candidate))
|
|
66
32
|
return candidate;
|
|
67
33
|
}
|
|
68
34
|
}
|
|
35
|
+
else if (platform === "linux") {
|
|
36
|
+
const candidates = [
|
|
37
|
+
"/usr/bin/microsoft-edge",
|
|
38
|
+
"/usr/bin/google-chrome",
|
|
39
|
+
"/usr/bin/chromium-browser",
|
|
40
|
+
"/usr/bin/chromium",
|
|
41
|
+
"/usr/bin/brave-browser",
|
|
42
|
+
];
|
|
43
|
+
for (const candidate of candidates) {
|
|
44
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (platform === "win32") {
|
|
49
|
+
const roots = [
|
|
50
|
+
process.env.PROGRAMFILES,
|
|
51
|
+
process.env["PROGRAMFILES(X86)"],
|
|
52
|
+
process.env.LOCALAPPDATA,
|
|
53
|
+
].filter(Boolean);
|
|
54
|
+
const browsers = [
|
|
55
|
+
{ suffix: "Microsoft\\Edge\\Application\\msedge.exe" },
|
|
56
|
+
{ suffix: "Google\\Chrome\\Application\\chrome.exe" },
|
|
57
|
+
{ suffix: "BraveSoftware\\Brave-Browser\\Application\\brave.exe" },
|
|
58
|
+
];
|
|
59
|
+
for (const { suffix } of browsers) {
|
|
60
|
+
for (const root of roots) {
|
|
61
|
+
const candidate = node_path_1.default.join(root, suffix);
|
|
62
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error(`不支援的作業系統:${platform}`);
|
|
69
|
+
}
|
|
69
70
|
throw new Error("找不到可用的瀏覽器(Edge / Chrome / Brave)。請確認已安裝其中一種。");
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
@@ -245,18 +246,14 @@ async function login(page, config, log) {
|
|
|
245
246
|
* Returns null if session is invalid or WS token is missing.
|
|
246
247
|
*/
|
|
247
248
|
async function createApiContext(options, command) {
|
|
248
|
-
const { createLogger } = await Promise.resolve().then(() => __importStar(require("./logger.js")));
|
|
249
|
-
const { loadWsToken } = await Promise.resolve().then(() => __importStar(require("./token.js")));
|
|
250
|
-
const { getOutputFormat, getSessionPath } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
|
|
251
249
|
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
252
|
-
const outputFormat = command ? getOutputFormat(command) : "json";
|
|
250
|
+
const outputFormat = command ? (0, utils_js_1.getOutputFormat)(command) : "json";
|
|
253
251
|
const silent = outputFormat === "json" && !opts.verbose;
|
|
254
|
-
const log = createLogger(opts.verbose, silent, outputFormat);
|
|
255
|
-
const sessionPath = getSessionPath();
|
|
256
|
-
|
|
257
|
-
const wsToken = loadWsToken(sessionPath);
|
|
252
|
+
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
253
|
+
const sessionPath = (0, utils_js_1.getSessionPath)();
|
|
254
|
+
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
258
255
|
if (!wsToken) {
|
|
259
|
-
|
|
256
|
+
log.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
260
257
|
return null;
|
|
261
258
|
}
|
|
262
259
|
return {
|
|
@@ -267,3 +264,33 @@ async function createApiContext(options, command) {
|
|
|
267
264
|
},
|
|
268
265
|
};
|
|
269
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Create an authenticated browser context for commands that need page access.
|
|
269
|
+
* Launches a browser, restores or creates a session, and extracts session info.
|
|
270
|
+
*/
|
|
271
|
+
async function createBrowserContext(options, command) {
|
|
272
|
+
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
273
|
+
const outputFormat = command ? (0, utils_js_1.getOutputFormat)(command) : "json";
|
|
274
|
+
const silent = outputFormat === "json" && !opts.verbose;
|
|
275
|
+
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
276
|
+
const sessionPath = (0, utils_js_1.getSessionPath)();
|
|
277
|
+
const headed = "headed" in options ? options.headed : false;
|
|
278
|
+
const config = {
|
|
279
|
+
courseUrl: "",
|
|
280
|
+
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
281
|
+
headless: !headed,
|
|
282
|
+
slowMo: 0,
|
|
283
|
+
authStatePath: sessionPath,
|
|
284
|
+
ollamaBaseUrl: "",
|
|
285
|
+
};
|
|
286
|
+
try {
|
|
287
|
+
log.info("啟動瀏覽器...");
|
|
288
|
+
const { browser, context, page, wsToken } = await launchAuthenticated(config, log);
|
|
289
|
+
const session = await (0, session_js_1.extractSessionInfo)(page, config, log, wsToken);
|
|
290
|
+
return { log, page, session, browser, context };
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { Logger } from "./types.js";
|
|
2
|
-
export declare function createLogger(verbose?: boolean, silent?: boolean, outputFormat?:
|
|
1
|
+
import type { Logger, OutputFormat } from "./types.js";
|
|
2
|
+
export declare function createLogger(verbose?: boolean, silent?: boolean, outputFormat?: OutputFormat): Logger;
|
|
3
3
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/src/lib/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/src/lib/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAOvD,wBAAgB,YAAY,CAAC,OAAO,UAAQ,EAAE,MAAM,UAAQ,EAAE,YAAY,GAAE,YAAqB,GAAG,MAAM,CAuBzG"}
|
package/script/src/lib/logger.js
CHANGED
|
@@ -4,8 +4,7 @@ exports.createLogger = createLogger;
|
|
|
4
4
|
const NO_COLOR = !!process.env.NO_COLOR;
|
|
5
5
|
const c = (code, text) => NO_COLOR ? text : `\x1b[${code}m${text}\x1b[0m`;
|
|
6
6
|
function createLogger(verbose = false, silent = false, outputFormat = "json") {
|
|
7
|
-
const
|
|
8
|
-
const errorFn = jsonError
|
|
7
|
+
const errorFn = outputFormat === "json"
|
|
9
8
|
? (msg) => console.error(JSON.stringify({ error: msg }))
|
|
10
9
|
: (msg) => console.error(c("31", "[ERR]") + ` ${msg}`);
|
|
11
10
|
if (silent) {
|
|
@@ -57,6 +57,20 @@ export declare function getForumsApi(session: {
|
|
|
57
57
|
courseid: number;
|
|
58
58
|
timemodified: number;
|
|
59
59
|
}>>;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a forum ID (cmid or instance ID) to a forum instance ID.
|
|
62
|
+
* Tries cmid resolution first (via core_course_get_course_module) to get name/course info.
|
|
63
|
+
* Falls back to treating the ID as a raw forum instance ID.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveForumId(session: {
|
|
66
|
+
wsToken: string;
|
|
67
|
+
moodleBaseUrl: string;
|
|
68
|
+
}, id: string): Promise<{
|
|
69
|
+
forumId: number;
|
|
70
|
+
cmid?: number;
|
|
71
|
+
name?: string;
|
|
72
|
+
courseid?: number;
|
|
73
|
+
} | null>;
|
|
60
74
|
/**
|
|
61
75
|
* Get discussions in a forum via WS API (no browser required).
|
|
62
76
|
* Uses mod_forum_get_forum_discussions
|