canvaslms-cli 1.3.2 ā 1.4.0
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/commands/announcements.js +102 -80
- package/commands/assignments.js +100 -130
- package/commands/config.js +239 -242
- package/commands/grades.js +201 -205
- package/commands/list.js +68 -70
- package/commands/profile.js +35 -34
- package/commands/submit.js +105 -143
- package/lib/api-client.js +4 -8
- package/lib/config-validator.js +60 -63
- package/lib/config.js +103 -135
- package/lib/file-upload.js +73 -78
- package/lib/interactive.js +180 -132
- package/package.json +4 -2
- package/src/index.js +26 -48
package/commands/submit.js
CHANGED
|
@@ -2,91 +2,84 @@
|
|
|
2
2
|
* Submit command for interactive assignment submission
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { makeCanvasRequest } from '../lib/api-client.js';
|
|
8
|
+
import { createReadlineInterface, askQuestion, askConfirmation, selectFilesImproved, pad } from '../lib/interactive.js';
|
|
9
|
+
import { uploadSingleFileToCanvas, submitAssignmentWithFiles } from '../lib/file-upload.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
10
11
|
|
|
11
|
-
async function submitAssignment(options) {
|
|
12
|
+
export async function submitAssignment(options) {
|
|
12
13
|
const rl = createReadlineInterface();
|
|
13
14
|
|
|
14
15
|
try {
|
|
15
16
|
let courseId = options.course;
|
|
16
17
|
let assignmentId = options.assignment;
|
|
17
|
-
let filePath = options.file;
|
|
18
18
|
let selectedCourse = null;
|
|
19
19
|
let selectedAssignment = null;
|
|
20
20
|
|
|
21
21
|
// Step 1: Select Course (if not provided)
|
|
22
|
-
|
|
23
|
-
console.log('
|
|
24
|
-
|
|
22
|
+
while (!courseId) {
|
|
23
|
+
console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
|
|
24
|
+
console.log(chalk.cyan.bold('Loading your courses, please wait...'));
|
|
25
25
|
const courses = await makeCanvasRequest('get', 'courses', [
|
|
26
26
|
'enrollment_state=active',
|
|
27
27
|
'include[]=favorites'
|
|
28
28
|
]);
|
|
29
|
-
|
|
30
29
|
if (!courses || courses.length === 0) {
|
|
31
|
-
console.log('No courses found.');
|
|
30
|
+
console.log(chalk.red('Error: No courses found.'));
|
|
32
31
|
rl.close();
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
let selectableCourses = courses;
|
|
35
|
+
if (!options.all) {
|
|
36
|
+
selectableCourses = courses.filter(course => course.is_favorite);
|
|
37
|
+
if (selectableCourses.length === 0) {
|
|
38
|
+
console.log(chalk.red('Error: No starred courses found. Showing all enrolled courses...'));
|
|
39
|
+
selectableCourses = courses;
|
|
40
|
+
}
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
console.log('Select a course:');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`${index + 1}. ${starIcon}${course.name}`);
|
|
42
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
43
|
+
console.log(chalk.cyan.bold('Select a course:'));
|
|
44
|
+
selectableCourses.forEach((course, index) => {
|
|
45
|
+
console.log(pad(chalk.white((index + 1) + '. '), 5) + chalk.white(course.name));
|
|
48
46
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const courseIndex = parseInt(courseChoice) - 1;
|
|
52
|
-
|
|
53
|
-
if (courseIndex < 0 || courseIndex >= starredCourses.length) {
|
|
54
|
-
console.log('Invalid course selection.');
|
|
47
|
+
const courseChoice = await askQuestion(rl, chalk.bold.cyan('\nEnter course number (or ".."/"back" to cancel): '));
|
|
48
|
+
if (courseChoice === '..' || courseChoice.toLowerCase() === 'back') {
|
|
55
49
|
rl.close();
|
|
56
50
|
return;
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
} else {
|
|
63
|
-
// Fetch course details if ID was provided
|
|
64
|
-
try {
|
|
65
|
-
selectedCourse = await makeCanvasRequest('get', `courses/${courseId}`);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.log(`ā ļø Could not fetch course details for ID ${courseId}`);
|
|
68
|
-
selectedCourse = { id: courseId, name: `Course ${courseId}` };
|
|
52
|
+
if (!courseChoice.trim()) {
|
|
53
|
+
console.log(chalk.red('Error: No course selected. Exiting...'));
|
|
54
|
+
rl.close();
|
|
55
|
+
return;
|
|
69
56
|
}
|
|
57
|
+
const courseIndex = parseInt(courseChoice) - 1;
|
|
58
|
+
if (courseIndex < 0 || courseIndex >= selectableCourses.length) {
|
|
59
|
+
console.log(chalk.red('Error: Invalid course selection.'));
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
selectedCourse = selectableCourses[courseIndex];
|
|
63
|
+
courseId = selectedCourse.id;
|
|
64
|
+
console.log(chalk.green(`Success: Selected ${selectedCourse.name}\n`));
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
// Step 2: Select Assignment (if not provided)
|
|
73
|
-
|
|
74
|
-
console.log('
|
|
75
|
-
|
|
68
|
+
while (!assignmentId) {
|
|
69
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
70
|
+
console.log(chalk.cyan.bold('Loading assignments, please wait...'));
|
|
76
71
|
const assignments = await makeCanvasRequest('get', `courses/${courseId}/assignments`, [
|
|
77
72
|
'include[]=submission',
|
|
78
73
|
'order_by=due_at',
|
|
79
74
|
'per_page=100'
|
|
80
75
|
]);
|
|
81
|
-
|
|
82
76
|
if (!assignments || assignments.length === 0) {
|
|
83
|
-
console.log('No assignments found for this course.');
|
|
77
|
+
console.log(chalk.red('Error: No assignments found for this course.'));
|
|
84
78
|
rl.close();
|
|
85
79
|
return;
|
|
86
80
|
}
|
|
87
|
-
|
|
88
|
-
console.log(`Found ${assignments.length} assignment(s)
|
|
89
|
-
|
|
81
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
82
|
+
console.log(chalk.cyan.bold(`Found ${assignments.length} assignment(s):`));
|
|
90
83
|
// Show summary of assignment statuses
|
|
91
84
|
const submittedCount = assignments.filter(a => a.submission && a.submission.submitted_at).length;
|
|
92
85
|
const pendingCount = assignments.length - submittedCount;
|
|
@@ -95,23 +88,15 @@ async function submitAssignment(options) {
|
|
|
95
88
|
a.submission_types.includes('online_upload') &&
|
|
96
89
|
a.workflow_state === 'published'
|
|
97
90
|
).length;
|
|
98
|
-
|
|
99
|
-
console.log(
|
|
100
|
-
|
|
91
|
+
console.log(chalk.yellow(`Summary: ${submittedCount} submitted, ${pendingCount} pending, ${uploadableCount} accept file uploads`));
|
|
92
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
93
|
+
// Numbered menu
|
|
101
94
|
assignments.forEach((assignment, index) => {
|
|
102
95
|
const dueDate = assignment.due_at ? new Date(assignment.due_at).toLocaleDateString() : 'No due date';
|
|
103
|
-
const submitted = assignment.submission && assignment.submission.submitted_at ? '
|
|
104
|
-
|
|
105
|
-
// Check if assignment accepts file uploads
|
|
106
|
-
const canSubmitFiles = assignment.submission_types &&
|
|
107
|
-
assignment.submission_types.includes('online_upload') &&
|
|
108
|
-
assignment.workflow_state === 'published';
|
|
109
|
-
const submissionIcon = canSubmitFiles ? 'š¤' : 'š';
|
|
110
|
-
|
|
111
|
-
// Format grade like Canvas web interface
|
|
96
|
+
const submitted = assignment.submission && assignment.submission.submitted_at ? chalk.green('Submitted') : chalk.yellow('Not submitted');
|
|
97
|
+
const canSubmitFiles = assignment.submission_types && assignment.submission_types.includes('online_upload') && assignment.workflow_state === 'published';
|
|
112
98
|
let gradeDisplay = '';
|
|
113
99
|
const submission = assignment.submission;
|
|
114
|
-
|
|
115
100
|
if (submission && submission.score !== null && submission.score !== undefined) {
|
|
116
101
|
const score = submission.score % 1 === 0 ? Math.round(submission.score) : submission.score;
|
|
117
102
|
const total = assignment.points_possible || 0;
|
|
@@ -123,137 +108,118 @@ async function submitAssignment(options) {
|
|
|
123
108
|
} else if (assignment.points_possible) {
|
|
124
109
|
gradeDisplay = ` | Grade: ā/${assignment.points_possible}`;
|
|
125
110
|
}
|
|
126
|
-
|
|
127
|
-
console.log(`${index + 1}. ${submissionIcon} ${assignment.name} ${submitted}`);
|
|
128
|
-
console.log(` Due: ${dueDate} | Points: ${assignment.points_possible || 'N/A'}${gradeDisplay}`);
|
|
111
|
+
let line = pad(chalk.white((index + 1) + '. '), 5) + chalk.white(assignment.name) + chalk.gray(` (${dueDate})`) + ' ' + submitted + gradeDisplay;
|
|
129
112
|
if (!canSubmitFiles) {
|
|
130
|
-
|
|
113
|
+
line += chalk.red(' [No file uploads]');
|
|
131
114
|
}
|
|
115
|
+
console.log(line);
|
|
132
116
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
const assignmentChoice = await askQuestion(rl, chalk.bold.cyan('\nEnter assignment number (or ".."/"back" to re-select course): '));
|
|
118
|
+
if (assignmentChoice === '..' || assignmentChoice.toLowerCase() === 'back') {
|
|
119
|
+
courseId = null;
|
|
120
|
+
selectedCourse = null;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
if (!assignmentChoice.trim()) {
|
|
124
|
+
console.log(chalk.red('Error: No assignment selected. Exiting...'));
|
|
137
125
|
rl.close();
|
|
138
126
|
return;
|
|
139
127
|
}
|
|
140
|
-
|
|
128
|
+
const assignmentIndex = parseInt(assignmentChoice) - 1;
|
|
129
|
+
if (assignmentIndex < 0 || assignmentIndex >= assignments.length) {
|
|
130
|
+
console.log(chalk.red('Error: Invalid assignment selection.'));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
141
133
|
selectedAssignment = assignments[assignmentIndex];
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!selectedAssignment.submission_types ||
|
|
145
|
-
!selectedAssignment.submission_types.includes('online_upload') ||
|
|
146
|
-
selectedAssignment.workflow_state !== 'published') {
|
|
147
|
-
console.log('ā This assignment does not accept file uploads or is not published.');
|
|
134
|
+
if (!selectedAssignment.submission_types || !selectedAssignment.submission_types.includes('online_upload') || selectedAssignment.workflow_state !== 'published') {
|
|
135
|
+
console.log(chalk.red('Error: This assignment does not accept file uploads or is not published.'));
|
|
148
136
|
rl.close();
|
|
149
137
|
return;
|
|
150
138
|
}
|
|
151
|
-
|
|
152
139
|
assignmentId = selectedAssignment.id;
|
|
153
|
-
console.log(
|
|
154
|
-
|
|
155
|
-
// Check if already submitted
|
|
140
|
+
console.log(chalk.green(`Success: Selected ${selectedAssignment.name}\n`));
|
|
156
141
|
if (selectedAssignment.submission && selectedAssignment.submission.submitted_at) {
|
|
157
|
-
const resubmit = await askConfirmation(rl, 'This assignment has already been submitted. Do you want to resubmit?', false);
|
|
142
|
+
const resubmit = await askConfirmation(rl, chalk.yellow('This assignment has already been submitted. Do you want to resubmit?'), false);
|
|
158
143
|
if (!resubmit) {
|
|
159
|
-
console.log('Submission cancelled.');
|
|
144
|
+
console.log(chalk.yellow('Submission cancelled.'));
|
|
160
145
|
rl.close();
|
|
161
146
|
return;
|
|
162
147
|
}
|
|
163
148
|
}
|
|
164
|
-
} else {
|
|
165
|
-
// Fetch assignment details if ID was provided
|
|
166
|
-
try {
|
|
167
|
-
selectedAssignment = await makeCanvasRequest('get', `courses/${courseId}/assignments/${assignmentId}`);
|
|
168
|
-
console.log(`ā
Using assignment: ${selectedAssignment.name}\n`);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.log(`ā ļø Could not fetch assignment details for ID ${assignmentId}`);
|
|
171
|
-
selectedAssignment = { id: assignmentId, name: `Assignment ${assignmentId}` };
|
|
172
|
-
}
|
|
173
|
-
} // Step 3: Select Files (if not provided)
|
|
174
|
-
let filePaths = [];
|
|
175
|
-
if (filePath) {
|
|
176
|
-
filePaths = [filePath]; // Single file provided via option
|
|
177
|
-
} else {
|
|
178
|
-
console.log('\nš File Selection');
|
|
179
|
-
console.log(`š Course: ${selectedCourse.name}`);
|
|
180
|
-
console.log(`š Assignment: ${selectedAssignment.name}\n`);
|
|
181
|
-
|
|
182
|
-
filePaths = await selectFilesImproved(rl);
|
|
183
149
|
}
|
|
184
|
-
|
|
150
|
+
// Step 3: Always list files in current directory and prompt user to select
|
|
151
|
+
let filePaths = [];
|
|
152
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
153
|
+
console.log(chalk.cyan.bold('File Selection'));
|
|
154
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
155
|
+
console.log(chalk.white('Course: ') + chalk.bold(selectedCourse.name));
|
|
156
|
+
console.log(chalk.white('Assignment: ') + chalk.bold(selectedAssignment.name) + '\n');
|
|
157
|
+
filePaths = await selectFilesImproved(rl);
|
|
185
158
|
// Validate all selected files exist
|
|
186
159
|
const validFiles = [];
|
|
187
160
|
for (const file of filePaths) {
|
|
188
161
|
if (fs.existsSync(file)) {
|
|
189
162
|
validFiles.push(file);
|
|
190
163
|
} else {
|
|
191
|
-
console.log(
|
|
164
|
+
console.log(chalk.red('Error: File not found: ' + file));
|
|
192
165
|
}
|
|
193
166
|
}
|
|
194
|
-
|
|
195
167
|
if (validFiles.length === 0) {
|
|
196
|
-
console.log('No valid files selected.');
|
|
168
|
+
console.log(chalk.red('Error: No valid files selected.'));
|
|
197
169
|
rl.close();
|
|
198
170
|
return;
|
|
199
171
|
}
|
|
200
|
-
|
|
201
172
|
filePaths = validFiles;
|
|
202
|
-
|
|
203
173
|
// Step 4: Confirm and Submit
|
|
204
|
-
console.log('
|
|
205
|
-
console.log(
|
|
206
|
-
console.log(
|
|
207
|
-
console.log(
|
|
174
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
175
|
+
console.log(chalk.cyan.bold('Submission Summary:'));
|
|
176
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
177
|
+
console.log(chalk.white('Course: ') + chalk.bold(selectedCourse?.name || 'Unknown Course'));
|
|
178
|
+
console.log(chalk.white('Assignment: ') + chalk.bold(selectedAssignment?.name || 'Unknown Assignment'));
|
|
179
|
+
console.log(chalk.white(`Files (${filePaths.length}):`));
|
|
208
180
|
filePaths.forEach((file, index) => {
|
|
209
181
|
const stats = fs.statSync(file);
|
|
210
182
|
const size = (stats.size / 1024).toFixed(1) + ' KB';
|
|
211
|
-
console.log(
|
|
183
|
+
console.log(pad(chalk.white((index + 1) + '.'), 5) + pad(path.basename(file), 35) + chalk.gray(size));
|
|
212
184
|
});
|
|
213
|
-
|
|
214
|
-
const confirm = await askConfirmation(rl, '\nProceed with submission?', true);
|
|
185
|
+
const confirm = await askConfirmation(rl, chalk.bold.cyan('\nProceed with submission?'), true);
|
|
215
186
|
if (!confirm) {
|
|
216
|
-
console.log('Submission cancelled.');
|
|
187
|
+
console.log(chalk.yellow('Submission cancelled.'));
|
|
217
188
|
rl.close();
|
|
218
189
|
return;
|
|
219
190
|
}
|
|
220
|
-
|
|
221
|
-
console.log('\nš Uploading files...');
|
|
222
|
-
|
|
191
|
+
console.log(chalk.cyan.bold('\nUploading files, please wait...'));
|
|
223
192
|
// Upload all files
|
|
224
193
|
const uploadedFileIds = [];
|
|
225
194
|
for (let i = 0; i < filePaths.length; i++) {
|
|
226
195
|
const currentFile = filePaths[i];
|
|
227
|
-
|
|
228
|
-
|
|
196
|
+
process.stdout.write(chalk.yellow(`Uploading ${i + 1}/${filePaths.length}: ${path.basename(currentFile)} ... `));
|
|
229
197
|
try {
|
|
230
198
|
const fileId = await uploadSingleFileToCanvas(courseId, assignmentId, currentFile);
|
|
231
199
|
uploadedFileIds.push(fileId);
|
|
232
|
-
console.log(
|
|
233
|
-
|
|
234
|
-
|
|
200
|
+
console.log(chalk.green('Success: Uploaded.'));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(chalk.red(`Error: Failed to upload ${currentFile}: ${error.message}`));
|
|
203
|
+
const continueUpload = await askConfirmation(rl, chalk.yellow('Continue with remaining files?'), true);
|
|
235
204
|
if (!continueUpload) {
|
|
236
205
|
break;
|
|
237
206
|
}
|
|
238
207
|
}
|
|
239
208
|
}
|
|
240
|
-
|
|
241
|
-
if (uploadedFileIds.length
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
209
|
+
// Submit assignment with uploaded files
|
|
210
|
+
if (uploadedFileIds.length > 0) {
|
|
211
|
+
try {
|
|
212
|
+
console.log(chalk.cyan.bold('Submitting assignment, please wait...'));
|
|
213
|
+
await submitAssignmentWithFiles(courseId, assignmentId, uploadedFileIds);
|
|
214
|
+
console.log(chalk.green('Success: Assignment submitted successfully!'));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(chalk.red('Error: Failed to submit assignment: ' + error.message));
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
console.log(chalk.red('Error: No files were uploaded. Submission not completed.'));
|
|
245
220
|
}
|
|
246
|
-
|
|
247
|
-
// Submit the assignment with all uploaded files
|
|
248
|
-
console.log('\nš Submitting assignment...');
|
|
249
|
-
const submission = await submitAssignmentWithFiles(courseId, assignmentId, uploadedFileIds);
|
|
250
|
-
|
|
251
|
-
console.log(`ā
Assignment submitted successfully with ${uploadedFileIds.length} file(s)!`);
|
|
252
|
-
console.log(`Submission ID: ${submission.id}`);
|
|
253
|
-
console.log(`Submitted at: ${new Date(submission.submitted_at).toLocaleString()}`);
|
|
254
|
-
|
|
255
221
|
} catch (error) {
|
|
256
|
-
console.error('
|
|
222
|
+
console.error(chalk.red('Error: Submission failed: ' + error.message));
|
|
257
223
|
} finally {
|
|
258
224
|
rl.close();
|
|
259
225
|
}
|
|
@@ -379,7 +345,3 @@ async function selectSingleFileFromList(rl, files) {
|
|
|
379
345
|
return [manualFile];
|
|
380
346
|
}
|
|
381
347
|
}
|
|
382
|
-
|
|
383
|
-
module.exports = {
|
|
384
|
-
submitAssignment
|
|
385
|
-
};
|
package/lib/api-client.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Canvas API client
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { getInstanceConfig } from './config.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Make Canvas API request
|
|
11
11
|
*/
|
|
12
|
-
async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody = null) {
|
|
12
|
+
export async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody = null) {
|
|
13
13
|
const instanceConfig = getInstanceConfig();
|
|
14
14
|
|
|
15
15
|
// Construct the full URL
|
|
@@ -73,7 +73,3 @@ async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody
|
|
|
73
73
|
process.exit(1);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
module.exports = {
|
|
78
|
-
makeCanvasRequest
|
|
79
|
-
};
|
package/lib/config-validator.js
CHANGED
|
@@ -1,63 +1,60 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration validation and setup helpers
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if configuration is valid and prompt setup if needed
|
|
10
|
-
*/
|
|
11
|
-
async function ensureConfig() {
|
|
12
|
-
if (!configExists()) {
|
|
13
|
-
console.log('
|
|
14
|
-
console.log('\
|
|
15
|
-
|
|
16
|
-
const rl = createReadlineInterface();
|
|
17
|
-
const setup = await askQuestion(rl, 'Would you like to set up your configuration now? (Y/n): ');
|
|
18
|
-
rl.close();
|
|
19
|
-
|
|
20
|
-
if (setup.toLowerCase() === 'n' || setup.toLowerCase() === 'no') {
|
|
21
|
-
console.log('\
|
|
22
|
-
console.log('
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Import and run setup (dynamic import to avoid circular dependency)
|
|
27
|
-
const { setupConfig } =
|
|
28
|
-
await setupConfig();
|
|
29
|
-
|
|
30
|
-
// Check if setup was successful
|
|
31
|
-
if (!configExists()) {
|
|
32
|
-
console.log('\
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('\
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate existing config
|
|
41
|
-
const config = readConfig();
|
|
42
|
-
if (!config || !config.domain || !config.token) {
|
|
43
|
-
console.log('
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Wrapper function to ensure config exists before running a command
|
|
52
|
-
*/
|
|
53
|
-
function requireConfig(
|
|
54
|
-
return async function(...args) {
|
|
55
|
-
await ensureConfig();
|
|
56
|
-
return
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
ensureConfig,
|
|
62
|
-
requireConfig
|
|
63
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Configuration validation and setup helpers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { configExists, readConfig } from './config.js';
|
|
6
|
+
import { createReadlineInterface, askQuestion } from './interactive.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if configuration is valid and prompt setup if needed
|
|
10
|
+
*/
|
|
11
|
+
async function ensureConfig() {
|
|
12
|
+
if (!configExists()) {
|
|
13
|
+
console.log('No Canvas configuration found!');
|
|
14
|
+
console.log('\nLet\'s set up your Canvas CLI configuration...\n');
|
|
15
|
+
|
|
16
|
+
const rl = createReadlineInterface();
|
|
17
|
+
const setup = await askQuestion(rl, 'Would you like to set up your configuration now? (Y/n): ');
|
|
18
|
+
rl.close();
|
|
19
|
+
|
|
20
|
+
if (setup.toLowerCase() === 'n' || setup.toLowerCase() === 'no') {
|
|
21
|
+
console.log('\nTo set up later, run: canvas config setup');
|
|
22
|
+
console.log('Or set environment variables: CANVAS_DOMAIN and CANVAS_API_TOKEN');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Import and run setup (dynamic import to avoid circular dependency)
|
|
27
|
+
const { setupConfig } = await import('../commands/config.js');
|
|
28
|
+
await setupConfig();
|
|
29
|
+
|
|
30
|
+
// Check if setup was successful
|
|
31
|
+
if (!configExists()) {
|
|
32
|
+
console.log('\nConfiguration setup was not completed. Please run "canvas config setup" to try again.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('\nConfiguration complete! You can now use Canvas CLI commands.');
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate existing config
|
|
41
|
+
const config = readConfig();
|
|
42
|
+
if (!config || !config.domain || !config.token) {
|
|
43
|
+
console.log('Invalid configuration found. Please run "canvas config setup" to reconfigure.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Wrapper function to ensure config exists before running a command
|
|
52
|
+
*/
|
|
53
|
+
export function requireConfig(fn) {
|
|
54
|
+
return async function(...args) {
|
|
55
|
+
await ensureConfig();
|
|
56
|
+
return fn(...args);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { ensureConfig };
|