learnhouse-mcp-server 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apply_aesthetics.d.ts +1 -0
- package/dist/apply_aesthetics.js +40 -0
- package/dist/client.d.ts +171 -0
- package/dist/client.js +378 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +489 -0
- package/dist/test-api.d.ts +6 -0
- package/dist/test-api.js +166 -0
- package/dist/types.d.ts +220 -0
- package/dist/types.js +21 -0
- package/package.json +4 -1
- package/src/client.ts +1 -1
- package/src/index.ts +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { LearnHouseClient } from "./client.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const config = {
|
|
5
|
+
baseUrl: process.env.LEARNHOUSE_URL || "http://localhost:3000",
|
|
6
|
+
email: process.env.LEARNHOUSE_EMAIL || "",
|
|
7
|
+
password: process.env.LEARNHOUSE_PASSWORD || "",
|
|
8
|
+
orgId: parseInt(process.env.LEARNHOUSE_ORG_ID || "1", 10),
|
|
9
|
+
};
|
|
10
|
+
async function main() {
|
|
11
|
+
const client = new LearnHouseClient({
|
|
12
|
+
baseUrl: config.baseUrl,
|
|
13
|
+
orgId: config.orgId,
|
|
14
|
+
});
|
|
15
|
+
console.log("Logging in...");
|
|
16
|
+
await client.login(config.email, config.password);
|
|
17
|
+
// 1. Upload Thumbnail to Mastering MCP
|
|
18
|
+
const courseUuid = "course_d6d76f09-c553-4f82-ab81-82a1c69fdd5f";
|
|
19
|
+
const imagePath = "\\\\192.168.2.108\\Users\\Laptop\\Desktop\\Projects\\Studies\\Winter_2026\\01_SIA 3000 - Projet integrateur\\AgentOne_Project\\_Handover\\Hero\\1_Hero_Backgrounds\\hero_grid_08_circuit_grid.png";
|
|
20
|
+
if (fs.existsSync(imagePath)) {
|
|
21
|
+
console.log(`Uploading thumbnail: ${imagePath}`);
|
|
22
|
+
const buffer = fs.readFileSync(imagePath);
|
|
23
|
+
const fileName = path.basename(imagePath);
|
|
24
|
+
const updatedCourse = await client.uploadCourseThumbnail(courseUuid, buffer, fileName);
|
|
25
|
+
console.log(`✅ Thumbnail uploaded! URL: ${updatedCourse.thumbnail_image}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.warn(`⚠️ Image not found: ${imagePath}`);
|
|
29
|
+
}
|
|
30
|
+
// 2. Create Collection
|
|
31
|
+
console.log("Creating collection 'Artemis AI Core Protocols'...");
|
|
32
|
+
const collection = await client.createCollection({
|
|
33
|
+
name: "Artemis AI Core Protocols",
|
|
34
|
+
description: "Foundational protocols and frameworks for the Agentic Internet.",
|
|
35
|
+
courses: [16], // Mastering MCP
|
|
36
|
+
public: true,
|
|
37
|
+
});
|
|
38
|
+
console.log(`✅ Collection created! UUID: ${collection.collection_uuid}`);
|
|
39
|
+
}
|
|
40
|
+
main().catch(console.error);
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LearnHouse API Client
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript client for interacting with the LearnHouse LMS API.
|
|
5
|
+
*/
|
|
6
|
+
import { LearnHouseConfig, LoginResponse, User, Organization, Course, CourseCreate, CourseUpdate, FullCourse, Chapter, ChapterCreate, ChapterUpdate, Activity, ActivityCreate, ActivityUpdate, Collection, CollectionCreate, TrailProgress, ActivityStatus, SearchResult } from "./types.js";
|
|
7
|
+
export declare class LearnHouseClient {
|
|
8
|
+
private config;
|
|
9
|
+
private accessToken?;
|
|
10
|
+
constructor(config: LearnHouseConfig);
|
|
11
|
+
private request;
|
|
12
|
+
private get;
|
|
13
|
+
private post;
|
|
14
|
+
private put;
|
|
15
|
+
private delete;
|
|
16
|
+
private postForm;
|
|
17
|
+
private putForm;
|
|
18
|
+
/**
|
|
19
|
+
* Login with email and password
|
|
20
|
+
*/
|
|
21
|
+
login(email: string, password: string): Promise<LoginResponse>;
|
|
22
|
+
/**
|
|
23
|
+
* Get current access token
|
|
24
|
+
*/
|
|
25
|
+
getAccessToken(): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Set access token manually
|
|
28
|
+
*/
|
|
29
|
+
setAccessToken(token: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get current authenticated user
|
|
32
|
+
*/
|
|
33
|
+
getCurrentUser(): Promise<User>;
|
|
34
|
+
/**
|
|
35
|
+
* List all organizations
|
|
36
|
+
*/
|
|
37
|
+
listOrganizations(): Promise<Organization[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Get organization by ID
|
|
40
|
+
*/
|
|
41
|
+
getOrganization(orgId: number): Promise<Organization>;
|
|
42
|
+
/**
|
|
43
|
+
* Get organization by slug
|
|
44
|
+
*/
|
|
45
|
+
getOrganizationBySlug(slug: string): Promise<Organization>;
|
|
46
|
+
/**
|
|
47
|
+
* List courses for the configured organization
|
|
48
|
+
*/
|
|
49
|
+
listCourses(page?: number, limit?: number): Promise<Course[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Get course by UUID
|
|
52
|
+
*/
|
|
53
|
+
getCourse(courseUuid: string): Promise<Course>;
|
|
54
|
+
/**
|
|
55
|
+
* Get course by numeric ID
|
|
56
|
+
*/
|
|
57
|
+
getCourseById(courseId: number): Promise<Course>;
|
|
58
|
+
/**
|
|
59
|
+
* Get course with full metadata (chapters & activities)
|
|
60
|
+
*/
|
|
61
|
+
getCourseMeta(courseUuid: string, withUnpublished?: boolean): Promise<FullCourse>;
|
|
62
|
+
/**
|
|
63
|
+
* Create a new course
|
|
64
|
+
*/
|
|
65
|
+
createCourse(course: CourseCreate): Promise<Course>;
|
|
66
|
+
/**
|
|
67
|
+
* Update a course
|
|
68
|
+
*/
|
|
69
|
+
updateCourse(courseUuid: string, updates: CourseUpdate): Promise<Course>;
|
|
70
|
+
/**
|
|
71
|
+
* Upload a course thumbnail
|
|
72
|
+
*/
|
|
73
|
+
uploadCourseThumbnail(courseUuid: string, imageBuffer: Buffer, fileName: string, type?: "IMAGE" | "VIDEO"): Promise<Course>;
|
|
74
|
+
/**
|
|
75
|
+
* Delete a course
|
|
76
|
+
*/
|
|
77
|
+
deleteCourse(courseUuid: string): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Get chapter by ID
|
|
80
|
+
*/
|
|
81
|
+
getChapter(chapterId: number): Promise<Chapter>;
|
|
82
|
+
/**
|
|
83
|
+
* List chapters for a course
|
|
84
|
+
*/
|
|
85
|
+
listChapters(courseId: number, page?: number, limit?: number): Promise<Chapter[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Create a new chapter
|
|
88
|
+
*/
|
|
89
|
+
createChapter(chapter: ChapterCreate): Promise<Chapter>;
|
|
90
|
+
/**
|
|
91
|
+
* Update a chapter
|
|
92
|
+
*/
|
|
93
|
+
updateChapter(chapterId: number, updates: ChapterUpdate): Promise<Chapter>;
|
|
94
|
+
/**
|
|
95
|
+
* Delete a chapter
|
|
96
|
+
*/
|
|
97
|
+
deleteChapter(chapterId: number): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Get activity by UUID
|
|
100
|
+
*/
|
|
101
|
+
getActivity(activityUuid: string): Promise<Activity>;
|
|
102
|
+
/**
|
|
103
|
+
* Get activity by numeric ID
|
|
104
|
+
*/
|
|
105
|
+
getActivityById(activityId: number): Promise<Activity>;
|
|
106
|
+
/**
|
|
107
|
+
* List activities for a chapter
|
|
108
|
+
*/
|
|
109
|
+
listActivities(chapterId: number): Promise<Activity[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Create a new activity
|
|
112
|
+
*/
|
|
113
|
+
createActivity(activity: ActivityCreate): Promise<Activity>;
|
|
114
|
+
/**
|
|
115
|
+
* Update an activity
|
|
116
|
+
*/
|
|
117
|
+
updateActivity(activityUuid: string, updates: ActivityUpdate): Promise<Activity>;
|
|
118
|
+
/**
|
|
119
|
+
* Delete an activity
|
|
120
|
+
*/
|
|
121
|
+
deleteActivity(activityUuid: string): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* List collections for the configured organization
|
|
124
|
+
*/
|
|
125
|
+
listCollections(page?: number, limit?: number): Promise<Collection[]>;
|
|
126
|
+
/**
|
|
127
|
+
* Get collection by UUID
|
|
128
|
+
*/
|
|
129
|
+
getCollection(collectionUuid: string): Promise<Collection>;
|
|
130
|
+
/**
|
|
131
|
+
* Create a new collection
|
|
132
|
+
*/
|
|
133
|
+
createCollection(collection: Omit<CollectionCreate, "org_id">): Promise<Collection>;
|
|
134
|
+
/**
|
|
135
|
+
* Update a collection
|
|
136
|
+
*/
|
|
137
|
+
updateCollection(collectionUuid: string, updates: Partial<CollectionCreate>): Promise<Collection>;
|
|
138
|
+
/**
|
|
139
|
+
* Delete a collection
|
|
140
|
+
*/
|
|
141
|
+
deleteCollection(collectionUuid: string): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Get progress for a course
|
|
144
|
+
*/
|
|
145
|
+
getCourseProgress(courseUuid: string): Promise<TrailProgress>;
|
|
146
|
+
/**
|
|
147
|
+
* Get activity completion status
|
|
148
|
+
*/
|
|
149
|
+
getActivityStatus(activityUuid: string): Promise<ActivityStatus>;
|
|
150
|
+
/**
|
|
151
|
+
* Mark activity as complete
|
|
152
|
+
*/
|
|
153
|
+
markActivityComplete(activityUuid: string): Promise<{
|
|
154
|
+
completed: boolean;
|
|
155
|
+
}>;
|
|
156
|
+
/**
|
|
157
|
+
* Mark activity as incomplete
|
|
158
|
+
*/
|
|
159
|
+
markActivityIncomplete(activityUuid: string): Promise<{
|
|
160
|
+
completed: boolean;
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Search across courses, users, and collections
|
|
164
|
+
*/
|
|
165
|
+
search(orgSlug: string, query: string): Promise<SearchResult>;
|
|
166
|
+
/**
|
|
167
|
+
* Check API health
|
|
168
|
+
*/
|
|
169
|
+
healthCheck(): Promise<Record<string, unknown>>;
|
|
170
|
+
}
|
|
171
|
+
export declare function createClient(config: LearnHouseConfig): LearnHouseClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LearnHouse API Client
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript client for interacting with the LearnHouse LMS API.
|
|
5
|
+
*/
|
|
6
|
+
export class LearnHouseClient {
|
|
7
|
+
config;
|
|
8
|
+
accessToken;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = {
|
|
11
|
+
...config,
|
|
12
|
+
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
13
|
+
};
|
|
14
|
+
this.accessToken = config.accessToken;
|
|
15
|
+
}
|
|
16
|
+
// ============================================================
|
|
17
|
+
// HTTP Helpers
|
|
18
|
+
// ============================================================
|
|
19
|
+
async request(method, path, options = {}) {
|
|
20
|
+
const url = new URL(`${this.config.baseUrl}/api/v1${path}`);
|
|
21
|
+
// Add query parameters
|
|
22
|
+
if (options.query) {
|
|
23
|
+
Object.entries(options.query).forEach(([key, value]) => {
|
|
24
|
+
if (value !== undefined) {
|
|
25
|
+
url.searchParams.set(key, String(value));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const headers = {};
|
|
30
|
+
if (this.accessToken) {
|
|
31
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
32
|
+
}
|
|
33
|
+
let body;
|
|
34
|
+
if (options.formData) {
|
|
35
|
+
body = options.formData;
|
|
36
|
+
// Don't set Content-Type for FormData - browser will set it with boundary
|
|
37
|
+
}
|
|
38
|
+
else if (options.body) {
|
|
39
|
+
headers["Content-Type"] = "application/json";
|
|
40
|
+
body = JSON.stringify(options.body);
|
|
41
|
+
}
|
|
42
|
+
const response = await fetch(url.toString(), {
|
|
43
|
+
method,
|
|
44
|
+
headers,
|
|
45
|
+
body,
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const error = await response.json().catch(() => ({
|
|
49
|
+
detail: `HTTP ${response.status}: ${response.statusText}`,
|
|
50
|
+
}));
|
|
51
|
+
throw new Error(error.detail || `Request failed: ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
// Handle empty responses
|
|
54
|
+
const text = await response.text();
|
|
55
|
+
if (!text) {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
return JSON.parse(text);
|
|
59
|
+
}
|
|
60
|
+
get(path, query) {
|
|
61
|
+
return this.request("GET", path, { query });
|
|
62
|
+
}
|
|
63
|
+
post(path, body, query) {
|
|
64
|
+
return this.request("POST", path, { body, query });
|
|
65
|
+
}
|
|
66
|
+
put(path, body) {
|
|
67
|
+
return this.request("PUT", path, { body });
|
|
68
|
+
}
|
|
69
|
+
delete(path) {
|
|
70
|
+
return this.request("DELETE", path);
|
|
71
|
+
}
|
|
72
|
+
postForm(path, formData, query) {
|
|
73
|
+
return this.request("POST", path, { formData, query });
|
|
74
|
+
}
|
|
75
|
+
putForm(path, formData, query) {
|
|
76
|
+
return this.request("PUT", path, { formData, query });
|
|
77
|
+
}
|
|
78
|
+
// ============================================================
|
|
79
|
+
// Authentication
|
|
80
|
+
// ============================================================
|
|
81
|
+
/**
|
|
82
|
+
* Login with email and password
|
|
83
|
+
*/
|
|
84
|
+
async login(email, password) {
|
|
85
|
+
const formData = new URLSearchParams();
|
|
86
|
+
formData.set("username", email);
|
|
87
|
+
formData.set("password", password);
|
|
88
|
+
const response = await fetch(`${this.config.baseUrl}/api/v1/auth/login`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
92
|
+
},
|
|
93
|
+
body: formData.toString(),
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const error = await response.json().catch(() => ({ detail: "Login failed" }));
|
|
97
|
+
throw new Error(error.detail || "Login failed");
|
|
98
|
+
}
|
|
99
|
+
const result = await response.json();
|
|
100
|
+
this.accessToken = result.tokens.access_token;
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get current access token
|
|
105
|
+
*/
|
|
106
|
+
getAccessToken() {
|
|
107
|
+
return this.accessToken;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Set access token manually
|
|
111
|
+
*/
|
|
112
|
+
setAccessToken(token) {
|
|
113
|
+
this.accessToken = token;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get current authenticated user
|
|
117
|
+
*/
|
|
118
|
+
async getCurrentUser() {
|
|
119
|
+
return this.get("/users/profile");
|
|
120
|
+
}
|
|
121
|
+
// ============================================================
|
|
122
|
+
// Organizations
|
|
123
|
+
// ============================================================
|
|
124
|
+
/**
|
|
125
|
+
* List all organizations
|
|
126
|
+
*/
|
|
127
|
+
async listOrganizations() {
|
|
128
|
+
return this.get("/orgs/");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get organization by ID
|
|
132
|
+
*/
|
|
133
|
+
async getOrganization(orgId) {
|
|
134
|
+
return this.get(`/orgs/${orgId}`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get organization by slug
|
|
138
|
+
*/
|
|
139
|
+
async getOrganizationBySlug(slug) {
|
|
140
|
+
return this.get(`/orgs/slug/${slug}`);
|
|
141
|
+
}
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Courses
|
|
144
|
+
// ============================================================
|
|
145
|
+
/**
|
|
146
|
+
* List courses for the configured organization
|
|
147
|
+
*/
|
|
148
|
+
async listCourses(page = 1, limit = 50) {
|
|
149
|
+
const org = await this.getOrganization(this.config.orgId);
|
|
150
|
+
return this.get(`/courses/org_slug/${org.slug}/page/${page}/limit/${limit}`);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get course by UUID
|
|
154
|
+
*/
|
|
155
|
+
async getCourse(courseUuid) {
|
|
156
|
+
return this.get(`/courses/${courseUuid}`);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get course by numeric ID
|
|
160
|
+
*/
|
|
161
|
+
async getCourseById(courseId) {
|
|
162
|
+
return this.get(`/courses/id/${courseId}`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get course with full metadata (chapters & activities)
|
|
166
|
+
*/
|
|
167
|
+
async getCourseMeta(courseUuid, withUnpublished = false) {
|
|
168
|
+
return this.get(`/courses/${courseUuid}/meta`, {
|
|
169
|
+
with_unpublished_activities: withUnpublished,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create a new course
|
|
174
|
+
*/
|
|
175
|
+
async createCourse(course) {
|
|
176
|
+
const formData = new FormData();
|
|
177
|
+
formData.set("name", course.name);
|
|
178
|
+
formData.set("description", course.description);
|
|
179
|
+
formData.set("public", String(course.public ?? true));
|
|
180
|
+
formData.set("about", course.about ?? course.description);
|
|
181
|
+
if (course.learnings) {
|
|
182
|
+
formData.set("learnings", JSON.stringify(course.learnings));
|
|
183
|
+
}
|
|
184
|
+
if (course.tags) {
|
|
185
|
+
formData.set("tags", JSON.stringify(course.tags));
|
|
186
|
+
}
|
|
187
|
+
return this.postForm(`/courses/`, formData, { org_id: this.config.orgId });
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Update a course
|
|
191
|
+
*/
|
|
192
|
+
async updateCourse(courseUuid, updates) {
|
|
193
|
+
return this.put(`/courses/${courseUuid}`, updates);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Upload a course thumbnail
|
|
197
|
+
*/
|
|
198
|
+
async uploadCourseThumbnail(courseUuid, imageBuffer, fileName, type = "IMAGE") {
|
|
199
|
+
const formData = new FormData();
|
|
200
|
+
const blob = new Blob([imageBuffer], { type: "image/png" });
|
|
201
|
+
formData.append("thumbnail", blob, fileName);
|
|
202
|
+
formData.append("thumbnail_type", type);
|
|
203
|
+
return this.putForm(`/courses/${courseUuid}/thumbnail`, formData);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Delete a course
|
|
207
|
+
*/
|
|
208
|
+
async deleteCourse(courseUuid) {
|
|
209
|
+
await this.delete(`/courses/${courseUuid}`);
|
|
210
|
+
}
|
|
211
|
+
// ============================================================
|
|
212
|
+
// Chapters
|
|
213
|
+
// ============================================================
|
|
214
|
+
/**
|
|
215
|
+
* Get chapter by ID
|
|
216
|
+
*/
|
|
217
|
+
async getChapter(chapterId) {
|
|
218
|
+
return this.get(`/chapters/${chapterId}`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* List chapters for a course
|
|
222
|
+
*/
|
|
223
|
+
async listChapters(courseId, page = 1, limit = 50) {
|
|
224
|
+
return this.get(`/chapters/course/${courseId}/page/${page}/limit/${limit}`);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Create a new chapter
|
|
228
|
+
*/
|
|
229
|
+
async createChapter(chapter) {
|
|
230
|
+
return this.post("/chapters/", chapter);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Update a chapter
|
|
234
|
+
*/
|
|
235
|
+
async updateChapter(chapterId, updates) {
|
|
236
|
+
return this.put(`/chapters/${chapterId}`, updates);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Delete a chapter
|
|
240
|
+
*/
|
|
241
|
+
async deleteChapter(chapterId) {
|
|
242
|
+
await this.delete(`/chapters/${chapterId}`);
|
|
243
|
+
}
|
|
244
|
+
// ============================================================
|
|
245
|
+
// Activities
|
|
246
|
+
// ============================================================
|
|
247
|
+
/**
|
|
248
|
+
* Get activity by UUID
|
|
249
|
+
*/
|
|
250
|
+
async getActivity(activityUuid) {
|
|
251
|
+
return this.get(`/activities/${activityUuid}`);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get activity by numeric ID
|
|
255
|
+
*/
|
|
256
|
+
async getActivityById(activityId) {
|
|
257
|
+
return this.get(`/activities/id/${activityId}`);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* List activities for a chapter
|
|
261
|
+
*/
|
|
262
|
+
async listActivities(chapterId) {
|
|
263
|
+
return this.get(`/activities/chapter/${chapterId}`);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Create a new activity
|
|
267
|
+
*/
|
|
268
|
+
async createActivity(activity) {
|
|
269
|
+
return this.post("/activities/", {
|
|
270
|
+
...activity,
|
|
271
|
+
activity_type: activity.activity_type ?? "TYPE_DYNAMIC",
|
|
272
|
+
activity_sub_type: activity.activity_sub_type ?? "SUBTYPE_DYNAMIC_PAGE",
|
|
273
|
+
content: activity.content ?? { type: "doc", content: [] },
|
|
274
|
+
published: activity.published ?? false,
|
|
275
|
+
details: activity.details ?? {},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Update an activity
|
|
280
|
+
*/
|
|
281
|
+
async updateActivity(activityUuid, updates) {
|
|
282
|
+
return this.put(`/activities/${activityUuid}`, updates);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Delete an activity
|
|
286
|
+
*/
|
|
287
|
+
async deleteActivity(activityUuid) {
|
|
288
|
+
await this.delete(`/activities/${activityUuid}`);
|
|
289
|
+
}
|
|
290
|
+
// ============================================================
|
|
291
|
+
// Collections
|
|
292
|
+
// ============================================================
|
|
293
|
+
/**
|
|
294
|
+
* List collections for the configured organization
|
|
295
|
+
*/
|
|
296
|
+
async listCollections(page = 1, limit = 50) {
|
|
297
|
+
return this.get(`/collections/org/${this.config.orgId}/page/${page}/limit/${limit}`);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get collection by UUID
|
|
301
|
+
*/
|
|
302
|
+
async getCollection(collectionUuid) {
|
|
303
|
+
return this.get(`/collections/${collectionUuid}`);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Create a new collection
|
|
307
|
+
*/
|
|
308
|
+
async createCollection(collection) {
|
|
309
|
+
return this.post("/collections/", {
|
|
310
|
+
...collection,
|
|
311
|
+
org_id: this.config.orgId,
|
|
312
|
+
public: collection.public ?? true,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Update a collection
|
|
317
|
+
*/
|
|
318
|
+
async updateCollection(collectionUuid, updates) {
|
|
319
|
+
return this.put(`/collections/${collectionUuid}`, updates);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Delete a collection
|
|
323
|
+
*/
|
|
324
|
+
async deleteCollection(collectionUuid) {
|
|
325
|
+
await this.delete(`/collections/${collectionUuid}`);
|
|
326
|
+
}
|
|
327
|
+
// ============================================================
|
|
328
|
+
// Progress / Trail
|
|
329
|
+
// ============================================================
|
|
330
|
+
/**
|
|
331
|
+
* Get progress for a course
|
|
332
|
+
*/
|
|
333
|
+
async getCourseProgress(courseUuid) {
|
|
334
|
+
return this.get(`/trail/course/${courseUuid}/completion`);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get activity completion status
|
|
338
|
+
*/
|
|
339
|
+
async getActivityStatus(activityUuid) {
|
|
340
|
+
return this.get(`/trail/activity/${activityUuid}/status`);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Mark activity as complete
|
|
344
|
+
*/
|
|
345
|
+
async markActivityComplete(activityUuid) {
|
|
346
|
+
return this.post(`/trail/activity/${activityUuid}/mark_complete`);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Mark activity as incomplete
|
|
350
|
+
*/
|
|
351
|
+
async markActivityIncomplete(activityUuid) {
|
|
352
|
+
return this.post(`/trail/activity/${activityUuid}/mark_incomplete`);
|
|
353
|
+
}
|
|
354
|
+
// ============================================================
|
|
355
|
+
// Search
|
|
356
|
+
// ============================================================
|
|
357
|
+
/**
|
|
358
|
+
* Search across courses, users, and collections
|
|
359
|
+
*/
|
|
360
|
+
async search(orgSlug, query) {
|
|
361
|
+
return this.get(`/search/org_slug/${orgSlug}`, {
|
|
362
|
+
query: query,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
// ============================================================
|
|
366
|
+
// Health
|
|
367
|
+
// ============================================================
|
|
368
|
+
/**
|
|
369
|
+
* Check API health
|
|
370
|
+
*/
|
|
371
|
+
async healthCheck() {
|
|
372
|
+
return this.get("/health");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Export a factory function
|
|
376
|
+
export function createClient(config) {
|
|
377
|
+
return new LearnHouseClient(config);
|
|
378
|
+
}
|