@lukeguo12210/canvas-cli 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/bin/canvas.js +330 -95
- package/dist/bin/canvas.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +4 -0
- package/skills/canvas-shared/SKILL.md +22 -9
- package/skills/canvas-shared/references/auth.md +59 -4
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ Connect Canvas courses to AI agents.
|
|
|
8
8
|
|
|
9
9
|
[Install](#installation--quick-start) · [Agent Skills](#agent-skills) · [Auth](#authentication) · [Commands](#command-system) · [Security](#security--privacy) · [License](#license) · [Roadmap](#roadmap)
|
|
10
10
|
|
|
11
|
+
## Star History
|
|
12
|
+
|
|
13
|
+
[](https://www.star-history.com/#lukeguo12210/canvas-cli&Date)
|
|
14
|
+
|
|
11
15
|
## Why @lukeguo12210/canvas-cli?
|
|
12
16
|
|
|
13
17
|
- **Built for technical students** — bring Canvas into your terminal, scripts, and AI workflow.
|
|
@@ -74,6 +78,10 @@ npm install -g @lukeguo12210/canvas-cli
|
|
|
74
78
|
# 1. Authenticate with your Canvas school
|
|
75
79
|
canvas auth login
|
|
76
80
|
|
|
81
|
+
# Agent/non-interactive auth
|
|
82
|
+
canvas auth schools search "Columbia"
|
|
83
|
+
canvas auth login --school "Columbia" --token-env CANVAS_TOKEN
|
|
84
|
+
|
|
77
85
|
# 2. List courses
|
|
78
86
|
canvas courses list
|
|
79
87
|
|
|
@@ -86,6 +94,12 @@ canvas review pack --course-id <course-id> --out ./review/<course>
|
|
|
86
94
|
|
|
87
95
|
### Install Agent Skills
|
|
88
96
|
|
|
97
|
+
```bash
|
|
98
|
+
canvas skills install
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Equivalent direct installer command:
|
|
102
|
+
|
|
89
103
|
```bash
|
|
90
104
|
npx skills add lukeguo12210/canvas-cli -g --skill "*" -y
|
|
91
105
|
```
|
|
@@ -137,6 +151,9 @@ canvas review pack --course-id <course-id> --out ./review/<course> --include-all
|
|
|
137
151
|
|
|
138
152
|
```bash
|
|
139
153
|
canvas auth login
|
|
154
|
+
canvas auth login --school "Columbia" --token-env CANVAS_TOKEN
|
|
155
|
+
canvas auth login --school-url https://courseworks2.columbia.edu --school-name "Columbia University (CourseWorks)" --token "paste-token-here"
|
|
156
|
+
canvas auth schools search "Columbia"
|
|
140
157
|
canvas auth status
|
|
141
158
|
canvas auth logout
|
|
142
159
|
canvas config show
|
|
@@ -174,6 +191,10 @@ canvas review pack --course-id <course-id> --out ./review/<course> --include-all
|
|
|
174
191
|
|
|
175
192
|
canvas api get /api/v1/courses
|
|
176
193
|
canvas api get /api/v1/courses/<course-id>/modules --params '{"include":["items"]}'
|
|
194
|
+
|
|
195
|
+
canvas skills install
|
|
196
|
+
canvas skills command
|
|
197
|
+
canvas skills status
|
|
177
198
|
```
|
|
178
199
|
|
|
179
200
|
MVP raw API access is GET-only.
|
package/dist/bin/canvas.js
CHANGED
|
@@ -519,16 +519,112 @@ async function runPostLoginBootstrap() {
|
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
+
// src/commands/shared.ts
|
|
523
|
+
async function activeCanvas() {
|
|
524
|
+
const profile = await new ConfigStore().activeProfile();
|
|
525
|
+
return {
|
|
526
|
+
profile,
|
|
527
|
+
client: new CanvasClient({
|
|
528
|
+
baseUrl: profile.baseUrl,
|
|
529
|
+
token: profile.token
|
|
530
|
+
})
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function flagValue(argv, flag) {
|
|
534
|
+
const index = argv.indexOf(flag);
|
|
535
|
+
if (index === -1) {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
538
|
+
return argv[index + 1];
|
|
539
|
+
}
|
|
540
|
+
function requiredFlag(argv, flag, usage) {
|
|
541
|
+
const value = flagValue(argv, flag);
|
|
542
|
+
if (!value) {
|
|
543
|
+
throw new Error(usage);
|
|
544
|
+
}
|
|
545
|
+
return value;
|
|
546
|
+
}
|
|
547
|
+
function hasFlag(argv, flag) {
|
|
548
|
+
return argv.includes(flag);
|
|
549
|
+
}
|
|
550
|
+
function csvFlag(argv, flag) {
|
|
551
|
+
const raw = flagValue(argv, flag);
|
|
552
|
+
if (!raw) {
|
|
553
|
+
return void 0;
|
|
554
|
+
}
|
|
555
|
+
const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
556
|
+
return values.length > 0 ? values : void 0;
|
|
557
|
+
}
|
|
558
|
+
function pageOptions(argv) {
|
|
559
|
+
const pageLimitRaw = flagValue(argv, "--page-limit");
|
|
560
|
+
return {
|
|
561
|
+
pageAll: hasFlag(argv, "--page-all"),
|
|
562
|
+
pageLimit: pageLimitRaw ? Number.parseInt(pageLimitRaw, 10) : void 0
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function positionalArgs(argv) {
|
|
566
|
+
const valueFlags = /* @__PURE__ */ new Set([
|
|
567
|
+
"--course-id",
|
|
568
|
+
"--module-id",
|
|
569
|
+
"--item-id",
|
|
570
|
+
"--assignment-id",
|
|
571
|
+
"--quiz-id",
|
|
572
|
+
"--topic-id",
|
|
573
|
+
"--page",
|
|
574
|
+
"--path",
|
|
575
|
+
"--out",
|
|
576
|
+
"--format",
|
|
577
|
+
"--page-limit",
|
|
578
|
+
"--page-size",
|
|
579
|
+
"--page-delay",
|
|
580
|
+
"--enrollment-state",
|
|
581
|
+
"--state",
|
|
582
|
+
"--include",
|
|
583
|
+
"--params",
|
|
584
|
+
"--bucket",
|
|
585
|
+
"--search",
|
|
586
|
+
"--order-by",
|
|
587
|
+
"--sort",
|
|
588
|
+
"--file-id",
|
|
589
|
+
"--folder-id",
|
|
590
|
+
"--group-id",
|
|
591
|
+
"--content-type",
|
|
592
|
+
"--school",
|
|
593
|
+
"--school-query",
|
|
594
|
+
"--school-url",
|
|
595
|
+
"--url",
|
|
596
|
+
"--school-name",
|
|
597
|
+
"--name",
|
|
598
|
+
"--token",
|
|
599
|
+
"--token-env"
|
|
600
|
+
]);
|
|
601
|
+
const values = [];
|
|
602
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
603
|
+
const arg = argv[index];
|
|
604
|
+
if (arg.startsWith("--")) {
|
|
605
|
+
if (valueFlags.has(arg)) {
|
|
606
|
+
index += 1;
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
values.push(arg);
|
|
611
|
+
}
|
|
612
|
+
return values;
|
|
613
|
+
}
|
|
614
|
+
|
|
522
615
|
// src/commands/auth.ts
|
|
523
616
|
var TOKEN_PURPOSE = "Hyperknow";
|
|
524
617
|
async function handleAuthCommand(argv, options) {
|
|
525
618
|
const [subcommand] = argv;
|
|
526
619
|
if (subcommand === "login") {
|
|
527
|
-
return authLogin(options);
|
|
620
|
+
return authLogin(argv.slice(1), options);
|
|
528
621
|
}
|
|
529
622
|
if (subcommand === "status") {
|
|
530
623
|
return authStatus(options);
|
|
531
624
|
}
|
|
625
|
+
if (subcommand === "schools") {
|
|
626
|
+
return authSchools(argv.slice(1), options);
|
|
627
|
+
}
|
|
532
628
|
if (subcommand === "logout") {
|
|
533
629
|
return authLogout(options);
|
|
534
630
|
}
|
|
@@ -545,16 +641,21 @@ async function handleAuthCommand(argv, options) {
|
|
|
545
641
|
);
|
|
546
642
|
return 1;
|
|
547
643
|
}
|
|
548
|
-
async function authLogin(options) {
|
|
644
|
+
async function authLogin(argv, options) {
|
|
549
645
|
const io = createPrompt();
|
|
550
646
|
try {
|
|
551
|
-
const
|
|
647
|
+
const nonInteractive = hasNonInteractiveLoginArgs(argv);
|
|
648
|
+
const school = nonInteractive ? resolveSchoolFromArgs(argv) : await chooseSchool(io);
|
|
552
649
|
const settingsUrl = `${school.url}/profile/settings`;
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
650
|
+
const providedToken = await tokenFromArgs(argv);
|
|
651
|
+
let token = providedToken;
|
|
652
|
+
if (!token) {
|
|
653
|
+
process.stdout.write(tokenInstructions(school, settingsUrl));
|
|
654
|
+
await io.question("Press Enter to open Canvas settings in your browser...");
|
|
655
|
+
await openBrowser(settingsUrl);
|
|
656
|
+
process.stdout.write("\nWaiting for your Canvas personal access token.\n");
|
|
657
|
+
token = await promptHidden("Paste token: ");
|
|
658
|
+
}
|
|
558
659
|
if (!token) {
|
|
559
660
|
throw new CanvasCliError("EMPTY_TOKEN", "No token entered.");
|
|
560
661
|
}
|
|
@@ -588,7 +689,7 @@ async function authLogin(options) {
|
|
|
588
689
|
},
|
|
589
690
|
user,
|
|
590
691
|
contextBootstrap: bootstrap,
|
|
591
|
-
next: "canvas
|
|
692
|
+
next: "canvas courses list --active --page-all"
|
|
592
693
|
},
|
|
593
694
|
meta: {
|
|
594
695
|
command: "auth login"
|
|
@@ -604,6 +705,25 @@ async function authLogin(options) {
|
|
|
604
705
|
io.close();
|
|
605
706
|
}
|
|
606
707
|
}
|
|
708
|
+
async function authSchools(argv, options) {
|
|
709
|
+
const [subcommand] = argv;
|
|
710
|
+
const query = subcommand === "search" ? positionalArgs(argv.slice(1)).join(" ") : flagValue(argv, "--query") ?? positionalArgs(argv).join(" ");
|
|
711
|
+
await writeOutput(
|
|
712
|
+
{
|
|
713
|
+
ok: true,
|
|
714
|
+
data: searchSchools(query).map((school) => ({
|
|
715
|
+
name: school.name,
|
|
716
|
+
baseUrl: school.url
|
|
717
|
+
})),
|
|
718
|
+
meta: {
|
|
719
|
+
command: "auth schools",
|
|
720
|
+
query
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
options
|
|
724
|
+
);
|
|
725
|
+
return 0;
|
|
726
|
+
}
|
|
607
727
|
async function authStatus(options) {
|
|
608
728
|
const store = new ConfigStore();
|
|
609
729
|
const config = await store.readRedacted();
|
|
@@ -694,11 +814,77 @@ async function chooseSchool(io, write = (message) => process.stdout.write(messag
|
|
|
694
814
|
url: normalizeBaseUrl(school.url)
|
|
695
815
|
};
|
|
696
816
|
}
|
|
817
|
+
function resolveSchoolFromArgs(argv) {
|
|
818
|
+
const schoolUrl = flagValue(argv, "--school-url") ?? flagValue(argv, "--url");
|
|
819
|
+
if (schoolUrl) {
|
|
820
|
+
return makeCustomSchool(flagValue(argv, "--school-name") ?? flagValue(argv, "--name") ?? "Custom Canvas School", schoolUrl);
|
|
821
|
+
}
|
|
822
|
+
const schoolQuery = flagValue(argv, "--school") ?? flagValue(argv, "--school-query");
|
|
823
|
+
if (!schoolQuery) {
|
|
824
|
+
throw new CanvasCliError(
|
|
825
|
+
"MISSING_SCHOOL",
|
|
826
|
+
"Non-interactive auth requires --school <query> or --school-url <url>."
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
const matches = searchSchools(schoolQuery, 20);
|
|
830
|
+
if (matches.length === 0) {
|
|
831
|
+
throw new CanvasCliError(
|
|
832
|
+
"SCHOOL_NOT_FOUND",
|
|
833
|
+
`No Canvas school matched "${schoolQuery}". Use --school-url <url> for a custom Canvas URL.`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
const exact = matches.find((school) => {
|
|
837
|
+
return school.name.toLowerCase() === schoolQuery.toLowerCase() || school.url.toLowerCase() === schoolQuery.toLowerCase();
|
|
838
|
+
});
|
|
839
|
+
if (exact) {
|
|
840
|
+
return {
|
|
841
|
+
name: exact.name,
|
|
842
|
+
url: normalizeBaseUrl(exact.url)
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
if (matches.length === 1) {
|
|
846
|
+
const school = matches[0];
|
|
847
|
+
return {
|
|
848
|
+
name: school.name,
|
|
849
|
+
url: normalizeBaseUrl(school.url)
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
throw new CanvasCliError(
|
|
853
|
+
"AMBIGUOUS_SCHOOL",
|
|
854
|
+
`Multiple schools matched "${schoolQuery}": ${matches.map((school) => `${school.name} (${school.url})`).join("; ")}. Use a more specific --school value or --school-url.`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
async function tokenFromArgs(argv) {
|
|
858
|
+
const directToken = flagValue(argv, "--token");
|
|
859
|
+
if (directToken) {
|
|
860
|
+
return directToken.trim();
|
|
861
|
+
}
|
|
862
|
+
const envName = flagValue(argv, "--token-env");
|
|
863
|
+
if (envName) {
|
|
864
|
+
return process.env[envName]?.trim();
|
|
865
|
+
}
|
|
866
|
+
if (argv.includes("--token-stdin")) {
|
|
867
|
+
return readStdin().then((value) => value.trim());
|
|
868
|
+
}
|
|
869
|
+
return void 0;
|
|
870
|
+
}
|
|
697
871
|
async function promptCustomSchool(io) {
|
|
698
872
|
const name = await io.question("School display name: ");
|
|
699
873
|
const url = await io.question("Canvas base URL: ");
|
|
700
874
|
return makeCustomSchool(name, url);
|
|
701
875
|
}
|
|
876
|
+
function hasNonInteractiveLoginArgs(argv) {
|
|
877
|
+
return Boolean(
|
|
878
|
+
flagValue(argv, "--school") || flagValue(argv, "--school-query") || flagValue(argv, "--school-url") || flagValue(argv, "--url")
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
async function readStdin() {
|
|
882
|
+
const chunks = [];
|
|
883
|
+
for await (const chunk of process.stdin) {
|
|
884
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
885
|
+
}
|
|
886
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
887
|
+
}
|
|
702
888
|
function tokenInstructions(school, settingsUrl) {
|
|
703
889
|
return `
|
|
704
890
|
Canvas token setup for ${school.name}
|
|
@@ -730,91 +916,6 @@ async function validateToken(client) {
|
|
|
730
916
|
}
|
|
731
917
|
}
|
|
732
918
|
|
|
733
|
-
// src/commands/shared.ts
|
|
734
|
-
async function activeCanvas() {
|
|
735
|
-
const profile = await new ConfigStore().activeProfile();
|
|
736
|
-
return {
|
|
737
|
-
profile,
|
|
738
|
-
client: new CanvasClient({
|
|
739
|
-
baseUrl: profile.baseUrl,
|
|
740
|
-
token: profile.token
|
|
741
|
-
})
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
function flagValue(argv, flag) {
|
|
745
|
-
const index = argv.indexOf(flag);
|
|
746
|
-
if (index === -1) {
|
|
747
|
-
return void 0;
|
|
748
|
-
}
|
|
749
|
-
return argv[index + 1];
|
|
750
|
-
}
|
|
751
|
-
function requiredFlag(argv, flag, usage) {
|
|
752
|
-
const value = flagValue(argv, flag);
|
|
753
|
-
if (!value) {
|
|
754
|
-
throw new Error(usage);
|
|
755
|
-
}
|
|
756
|
-
return value;
|
|
757
|
-
}
|
|
758
|
-
function hasFlag(argv, flag) {
|
|
759
|
-
return argv.includes(flag);
|
|
760
|
-
}
|
|
761
|
-
function csvFlag(argv, flag) {
|
|
762
|
-
const raw = flagValue(argv, flag);
|
|
763
|
-
if (!raw) {
|
|
764
|
-
return void 0;
|
|
765
|
-
}
|
|
766
|
-
const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
767
|
-
return values.length > 0 ? values : void 0;
|
|
768
|
-
}
|
|
769
|
-
function pageOptions(argv) {
|
|
770
|
-
const pageLimitRaw = flagValue(argv, "--page-limit");
|
|
771
|
-
return {
|
|
772
|
-
pageAll: hasFlag(argv, "--page-all"),
|
|
773
|
-
pageLimit: pageLimitRaw ? Number.parseInt(pageLimitRaw, 10) : void 0
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
function positionalArgs(argv) {
|
|
777
|
-
const valueFlags = /* @__PURE__ */ new Set([
|
|
778
|
-
"--course-id",
|
|
779
|
-
"--module-id",
|
|
780
|
-
"--item-id",
|
|
781
|
-
"--assignment-id",
|
|
782
|
-
"--quiz-id",
|
|
783
|
-
"--topic-id",
|
|
784
|
-
"--page",
|
|
785
|
-
"--path",
|
|
786
|
-
"--out",
|
|
787
|
-
"--format",
|
|
788
|
-
"--page-limit",
|
|
789
|
-
"--page-size",
|
|
790
|
-
"--page-delay",
|
|
791
|
-
"--enrollment-state",
|
|
792
|
-
"--state",
|
|
793
|
-
"--include",
|
|
794
|
-
"--params",
|
|
795
|
-
"--bucket",
|
|
796
|
-
"--search",
|
|
797
|
-
"--order-by",
|
|
798
|
-
"--sort",
|
|
799
|
-
"--file-id",
|
|
800
|
-
"--folder-id",
|
|
801
|
-
"--group-id",
|
|
802
|
-
"--content-type"
|
|
803
|
-
]);
|
|
804
|
-
const values = [];
|
|
805
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
806
|
-
const arg = argv[index];
|
|
807
|
-
if (arg.startsWith("--")) {
|
|
808
|
-
if (valueFlags.has(arg)) {
|
|
809
|
-
index += 1;
|
|
810
|
-
}
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
813
|
-
values.push(arg);
|
|
814
|
-
}
|
|
815
|
-
return values;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
919
|
// src/commands/api.ts
|
|
819
920
|
async function handleApiCommand(argv, options) {
|
|
820
921
|
const [subcommand] = argv;
|
|
@@ -2153,8 +2254,137 @@ async function reviewPack(argv, options) {
|
|
|
2153
2254
|
return 0;
|
|
2154
2255
|
}
|
|
2155
2256
|
|
|
2257
|
+
// src/commands/skills.ts
|
|
2258
|
+
import { spawn as spawn2 } from "child_process";
|
|
2259
|
+
var SKILLS_INSTALL_COMMAND = [
|
|
2260
|
+
"npx",
|
|
2261
|
+
"skills",
|
|
2262
|
+
"add",
|
|
2263
|
+
"lukeguo12210/canvas-cli",
|
|
2264
|
+
"-g",
|
|
2265
|
+
"--skill",
|
|
2266
|
+
"*",
|
|
2267
|
+
"-y"
|
|
2268
|
+
];
|
|
2269
|
+
var SKILLS_INSTALL_DISPLAY_COMMAND = 'npx skills add lukeguo12210/canvas-cli -g --skill "*" -y';
|
|
2270
|
+
async function handleSkillsCommand(argv, options) {
|
|
2271
|
+
const [subcommand] = argv;
|
|
2272
|
+
try {
|
|
2273
|
+
if (subcommand === "install") {
|
|
2274
|
+
return await installSkills(argv.slice(1), options);
|
|
2275
|
+
}
|
|
2276
|
+
if (subcommand === "command") {
|
|
2277
|
+
return await printSkillsCommand(options);
|
|
2278
|
+
}
|
|
2279
|
+
if (subcommand === "status") {
|
|
2280
|
+
return await skillsStatus(options);
|
|
2281
|
+
}
|
|
2282
|
+
await writeOutput(
|
|
2283
|
+
{
|
|
2284
|
+
ok: false,
|
|
2285
|
+
error: {
|
|
2286
|
+
code: "UNKNOWN_COMMAND",
|
|
2287
|
+
message: `Unknown skills command: ${argv.join(" ")}`,
|
|
2288
|
+
retryable: false
|
|
2289
|
+
}
|
|
2290
|
+
},
|
|
2291
|
+
options
|
|
2292
|
+
);
|
|
2293
|
+
return 1;
|
|
2294
|
+
} catch (error) {
|
|
2295
|
+
await writeOutput(toErrorEnvelope(error), options);
|
|
2296
|
+
return 1;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
async function installSkills(argv, options) {
|
|
2300
|
+
if (hasFlag(argv, "--dry-run") || hasFlag(argv, "--print")) {
|
|
2301
|
+
return printSkillsCommand(options);
|
|
2302
|
+
}
|
|
2303
|
+
const code = await run(SKILLS_INSTALL_COMMAND[0], SKILLS_INSTALL_COMMAND.slice(1));
|
|
2304
|
+
if (code !== 0) {
|
|
2305
|
+
await writeOutput(
|
|
2306
|
+
{
|
|
2307
|
+
ok: false,
|
|
2308
|
+
error: {
|
|
2309
|
+
code: "SKILLS_INSTALL_FAILED",
|
|
2310
|
+
message: `Skills installer exited with code ${code}. Try: ${SKILLS_INSTALL_DISPLAY_COMMAND}`,
|
|
2311
|
+
retryable: true
|
|
2312
|
+
}
|
|
2313
|
+
},
|
|
2314
|
+
options
|
|
2315
|
+
);
|
|
2316
|
+
return code;
|
|
2317
|
+
}
|
|
2318
|
+
await writeOutput(
|
|
2319
|
+
{
|
|
2320
|
+
ok: true,
|
|
2321
|
+
data: {
|
|
2322
|
+
installed: true,
|
|
2323
|
+
command: SKILLS_INSTALL_DISPLAY_COMMAND,
|
|
2324
|
+
next: "Restart or reload your agent so it can discover the updated Canvas skills."
|
|
2325
|
+
},
|
|
2326
|
+
meta: {
|
|
2327
|
+
command: "skills install"
|
|
2328
|
+
}
|
|
2329
|
+
},
|
|
2330
|
+
options
|
|
2331
|
+
);
|
|
2332
|
+
return 0;
|
|
2333
|
+
}
|
|
2334
|
+
async function printSkillsCommand(options) {
|
|
2335
|
+
await writeOutput(
|
|
2336
|
+
{
|
|
2337
|
+
ok: true,
|
|
2338
|
+
data: {
|
|
2339
|
+
command: SKILLS_INSTALL_DISPLAY_COMMAND,
|
|
2340
|
+
note: "Run this to install or update all Canvas agent skills from GitHub."
|
|
2341
|
+
},
|
|
2342
|
+
meta: {
|
|
2343
|
+
command: "skills command"
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2346
|
+
options
|
|
2347
|
+
);
|
|
2348
|
+
return 0;
|
|
2349
|
+
}
|
|
2350
|
+
async function skillsStatus(options) {
|
|
2351
|
+
await writeOutput(
|
|
2352
|
+
{
|
|
2353
|
+
ok: true,
|
|
2354
|
+
data: {
|
|
2355
|
+
installCommand: SKILLS_INSTALL_DISPLAY_COMMAND,
|
|
2356
|
+
skills: [
|
|
2357
|
+
"canvas-shared",
|
|
2358
|
+
"canvas-courses",
|
|
2359
|
+
"canvas-modules",
|
|
2360
|
+
"canvas-pages",
|
|
2361
|
+
"canvas-files",
|
|
2362
|
+
"canvas-assignments",
|
|
2363
|
+
"canvas-review"
|
|
2364
|
+
],
|
|
2365
|
+
note: "Use canvas skills install to install/update these skills."
|
|
2366
|
+
},
|
|
2367
|
+
meta: {
|
|
2368
|
+
command: "skills status"
|
|
2369
|
+
}
|
|
2370
|
+
},
|
|
2371
|
+
options
|
|
2372
|
+
);
|
|
2373
|
+
return 0;
|
|
2374
|
+
}
|
|
2375
|
+
function run(command, args) {
|
|
2376
|
+
return new Promise((resolve2) => {
|
|
2377
|
+
const child = spawn2(command, [...args], {
|
|
2378
|
+
stdio: "inherit",
|
|
2379
|
+
shell: process.platform === "win32"
|
|
2380
|
+
});
|
|
2381
|
+
child.on("close", (code) => resolve2(code ?? 1));
|
|
2382
|
+
child.on("error", () => resolve2(1));
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2156
2386
|
// src/bin/canvas.ts
|
|
2157
|
-
var VERSION = "0.0.
|
|
2387
|
+
var VERSION = "0.0.6";
|
|
2158
2388
|
function helpText() {
|
|
2159
2389
|
return `canvas \u2014 Canvas LMS CLI for students and agents.
|
|
2160
2390
|
|
|
@@ -2164,6 +2394,7 @@ USAGE:
|
|
|
2164
2394
|
COMMANDS:
|
|
2165
2395
|
auth login Interactive Canvas PAT setup
|
|
2166
2396
|
auth status Show redacted auth status
|
|
2397
|
+
auth schools Search supported Canvas school URLs
|
|
2167
2398
|
auth logout Remove local Canvas auth config
|
|
2168
2399
|
config show Show redacted local config
|
|
2169
2400
|
me Show current Canvas user profile
|
|
@@ -2178,6 +2409,7 @@ COMMANDS:
|
|
|
2178
2409
|
folders list List course folders
|
|
2179
2410
|
review pack Create a local course review pack
|
|
2180
2411
|
api get Raw read-only Canvas API GET
|
|
2412
|
+
skills install Install/update bundled agent skills
|
|
2181
2413
|
version Print CLI version
|
|
2182
2414
|
|
|
2183
2415
|
FLAGS:
|
|
@@ -2243,6 +2475,9 @@ async function main(argv) {
|
|
|
2243
2475
|
if (command === "api") {
|
|
2244
2476
|
return handleApiCommand(parsed.argv.slice(1), { format: parsed.format });
|
|
2245
2477
|
}
|
|
2478
|
+
if (command === "skills") {
|
|
2479
|
+
return handleSkillsCommand(parsed.argv.slice(1), { format: parsed.format });
|
|
2480
|
+
}
|
|
2246
2481
|
await writeOutput(
|
|
2247
2482
|
{
|
|
2248
2483
|
ok: false,
|