@lxpack/cli 0.2.0 → 0.2.1
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 +2 -2
- package/dist/cli.js +158 -109
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
Command-line tool for scaffolding, previewing, validating, and packaging LXPack courses.
|
|
9
9
|
|
|
10
|
-
Part of [LXPack](https://github.com/eddiethedean/lxpack) — an AI-native learning experience compiler and runtime (**v0.2.
|
|
10
|
+
Part of [LXPack](https://github.com/eddiethedean/lxpack) — an AI-native learning experience compiler and runtime (**v0.2.1**).
|
|
11
11
|
|
|
12
12
|
| Related | Package |
|
|
13
13
|
|---------|---------|
|
|
@@ -59,7 +59,7 @@ Output lands in `.lxpack/` unless overridden by `-o` or `lxpack.config.json`.
|
|
|
59
59
|
|
|
60
60
|
**SCORM 2004** builds produce a multi-SCO ZIP: one launch page per activity under `sco/<activityId>/index.html`, plus shared `lxpack-runtime.js` and `lxpack-components.js`.
|
|
61
61
|
|
|
62
|
-
**Preview** serves the runtime client, optional components bundle at `/runtime/components.js`, and installs SCORM API simulators (1.2 and 2004) for local testing.
|
|
62
|
+
**Preview** serves the runtime client, optional components bundle at `/runtime/components.js`, and installs SCORM API simulators (1.2 and 2004) for local testing. Direct HTTP access to `assessments/*.yaml` under `/course/` returns 404; quiz content is embedded in the preview page config only.
|
|
63
63
|
|
|
64
64
|
### Course discovery
|
|
65
65
|
|
package/dist/cli.js
CHANGED
|
@@ -5,24 +5,19 @@ import { Command } from "commander";
|
|
|
5
5
|
import pc5 from "picocolors";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
8
|
-
import { existsSync as
|
|
8
|
+
import { existsSync as existsSync4 } from "fs";
|
|
9
9
|
import { mkdir, writeFile } from "fs/promises";
|
|
10
|
-
import { join as
|
|
10
|
+
import { join as join4 } from "path";
|
|
11
11
|
import pc from "picocolors";
|
|
12
12
|
|
|
13
13
|
// src/utils.ts
|
|
14
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
15
|
+
import { createRequire as createRequire2 } from "module";
|
|
16
|
+
|
|
17
|
+
// src/lib/course-discovery.ts
|
|
14
18
|
import { existsSync } from "fs";
|
|
15
|
-
import { readFile } from "fs/promises";
|
|
16
|
-
import { createRequire } from "module";
|
|
17
19
|
import { dirname, join, resolve } from "path";
|
|
18
|
-
import {
|
|
19
|
-
import { z } from "zod";
|
|
20
|
-
import {
|
|
21
|
-
loadManifest,
|
|
22
|
-
formatErrorMessage,
|
|
23
|
-
isPathContained
|
|
24
|
-
} from "@lxpack/validators";
|
|
25
|
-
var require2 = createRequire(import.meta.url);
|
|
20
|
+
import { loadManifest } from "@lxpack/validators";
|
|
26
21
|
function findCourseDir(startDir = process.cwd()) {
|
|
27
22
|
let dir = resolve(startDir);
|
|
28
23
|
while (dir !== dirname(dir)) {
|
|
@@ -35,22 +30,29 @@ function findCourseDir(startDir = process.cwd()) {
|
|
|
35
30
|
"No course.yaml found. Run from a course directory or use lxpack init."
|
|
36
31
|
);
|
|
37
32
|
}
|
|
33
|
+
|
|
34
|
+
// src/lib/bundle-io.ts
|
|
35
|
+
import { existsSync as existsSync2 } from "fs";
|
|
36
|
+
import { readFile } from "fs/promises";
|
|
37
|
+
import { createRequire } from "module";
|
|
38
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
39
|
+
var require2 = createRequire(import.meta.url);
|
|
38
40
|
function getRuntimeAssetsDir() {
|
|
39
|
-
return
|
|
41
|
+
return dirname2(require2.resolve("@lxpack/runtime/client"));
|
|
40
42
|
}
|
|
41
43
|
function getEmbeddedStyles() {
|
|
42
44
|
return `:root { --lxpack-bg: #0f1419; } body { margin: 0; }`;
|
|
43
45
|
}
|
|
44
46
|
async function loadRuntimeStyles(assetsDir) {
|
|
45
47
|
try {
|
|
46
|
-
return await readFile(
|
|
48
|
+
return await readFile(join2(assetsDir, "styles.css"), "utf-8");
|
|
47
49
|
} catch {
|
|
48
50
|
return getEmbeddedStyles();
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
async function readRuntimeBundle(assetsDir = getRuntimeAssetsDir()) {
|
|
52
|
-
const clientPath =
|
|
53
|
-
if (!
|
|
54
|
+
const clientPath = join2(assetsDir, "client.js");
|
|
55
|
+
if (!existsSync2(clientPath)) {
|
|
54
56
|
throw new Error(
|
|
55
57
|
"Runtime bundle not found. Run `pnpm build` from the LXPack repo root, or reinstall @lxpack/runtime."
|
|
56
58
|
);
|
|
@@ -74,6 +76,13 @@ async function readComponentsBundle() {
|
|
|
74
76
|
return void 0;
|
|
75
77
|
}
|
|
76
78
|
}
|
|
79
|
+
|
|
80
|
+
// src/lib/lxpack-config.ts
|
|
81
|
+
import { existsSync as existsSync3 } from "fs";
|
|
82
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
83
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
84
|
+
import { z } from "zod";
|
|
85
|
+
import { formatErrorMessage, isPathContained } from "@lxpack/validators";
|
|
77
86
|
var lxpackConfigSchema = z.object({
|
|
78
87
|
exports: z.object({
|
|
79
88
|
defaultTarget: z.enum(["scorm12", "scorm2004", "standalone"]).optional()
|
|
@@ -83,10 +92,10 @@ var lxpackConfigSchema = z.object({
|
|
|
83
92
|
}).optional()
|
|
84
93
|
}).strict();
|
|
85
94
|
async function loadLxpackConfig(courseDir) {
|
|
86
|
-
const configPath =
|
|
87
|
-
if (!
|
|
95
|
+
const configPath = join3(courseDir, "lxpack.config.json");
|
|
96
|
+
if (!existsSync3(configPath)) return null;
|
|
88
97
|
try {
|
|
89
|
-
const content = await
|
|
98
|
+
const content = await readFile2(configPath, "utf-8");
|
|
90
99
|
const raw = JSON.parse(content);
|
|
91
100
|
const parsed = lxpackConfigSchema.safeParse(raw);
|
|
92
101
|
if (!parsed.success) {
|
|
@@ -101,44 +110,47 @@ async function loadLxpackConfig(courseDir) {
|
|
|
101
110
|
);
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
|
-
function escapeHtml(text) {
|
|
105
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
106
|
-
}
|
|
107
|
-
function formatCourseTitleForYaml(title) {
|
|
108
|
-
const doc = { title, version: "1.0.0" };
|
|
109
|
-
const yaml = stringifyYaml(doc);
|
|
110
|
-
const titleLine = yaml.split("\n").find((l) => l.startsWith("title:"));
|
|
111
|
-
if (!titleLine) {
|
|
112
|
-
return JSON.stringify(title);
|
|
113
|
-
}
|
|
114
|
-
return titleLine.replace(/^title:\s*/, "");
|
|
115
|
-
}
|
|
116
|
-
function getCliVersion() {
|
|
117
|
-
const pkg = require2("../package.json");
|
|
118
|
-
return pkg.version;
|
|
119
|
-
}
|
|
120
113
|
function resolvePathInCwd(relativePath) {
|
|
121
|
-
const cwd =
|
|
114
|
+
const cwd = resolve2(process.cwd());
|
|
122
115
|
if (relativePath.startsWith("/") || /^[a-zA-Z]:\\/.test(relativePath)) {
|
|
123
116
|
throw new Error(
|
|
124
117
|
"Use a relative path for the output directory (must stay inside the current working directory)"
|
|
125
118
|
);
|
|
126
119
|
}
|
|
127
|
-
const target =
|
|
120
|
+
const target = resolve2(cwd, relativePath);
|
|
128
121
|
if (!isPathContained(cwd, target)) {
|
|
129
122
|
throw new Error("Path must be inside the current working directory");
|
|
130
123
|
}
|
|
131
124
|
return target;
|
|
132
125
|
}
|
|
133
126
|
function resolveOutputDir(courseDir, outputDir) {
|
|
134
|
-
const root =
|
|
135
|
-
const target =
|
|
127
|
+
const root = resolve2(courseDir);
|
|
128
|
+
const target = resolve2(root, outputDir);
|
|
136
129
|
if (!isPathContained(root, target)) {
|
|
137
130
|
throw new Error("output.dir in lxpack.config.json must stay inside the course directory");
|
|
138
131
|
}
|
|
139
132
|
return target;
|
|
140
133
|
}
|
|
141
134
|
|
|
135
|
+
// src/lib/html.ts
|
|
136
|
+
import { escapeHtml } from "@lxpack/validators";
|
|
137
|
+
|
|
138
|
+
// src/utils.ts
|
|
139
|
+
var require3 = createRequire2(import.meta.url);
|
|
140
|
+
function formatCourseTitleForYaml(title) {
|
|
141
|
+
const doc = { title, version: "1.0.0" };
|
|
142
|
+
const yaml = stringifyYaml(doc);
|
|
143
|
+
const titleLine = yaml.split("\n").find((l) => l.startsWith("title:"));
|
|
144
|
+
if (!titleLine) {
|
|
145
|
+
return JSON.stringify(title);
|
|
146
|
+
}
|
|
147
|
+
return titleLine.replace(/^title:\s*/, "");
|
|
148
|
+
}
|
|
149
|
+
function getCliVersion() {
|
|
150
|
+
const pkg = require3("../package.json");
|
|
151
|
+
return pkg.version;
|
|
152
|
+
}
|
|
153
|
+
|
|
142
154
|
// src/commands/init.ts
|
|
143
155
|
var COURSE_YAML_TEMPLATE = `title: {{title}}
|
|
144
156
|
version: 1.0.0
|
|
@@ -274,7 +286,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
274
286
|
const targetDir = resolvePathInCwd(options.dir ?? projectName);
|
|
275
287
|
const title = formatTitle(projectName);
|
|
276
288
|
const yamlTitle = formatCourseTitleForYaml(title);
|
|
277
|
-
if (
|
|
289
|
+
if (existsSync4(join4(targetDir, "course.yaml")) && !options.force) {
|
|
278
290
|
console.error(
|
|
279
291
|
pc.red(
|
|
280
292
|
`Directory already contains a course. Use --force to overwrite: ${targetDir}`
|
|
@@ -284,29 +296,29 @@ async function initCommand(projectName, options = {}) {
|
|
|
284
296
|
}
|
|
285
297
|
const dirs = [
|
|
286
298
|
targetDir,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
299
|
+
join4(targetDir, "lessons"),
|
|
300
|
+
join4(targetDir, "interactions", "phishing-lab"),
|
|
301
|
+
join4(targetDir, "assets"),
|
|
302
|
+
join4(targetDir, "assessments"),
|
|
303
|
+
join4(targetDir, "theme"),
|
|
304
|
+
join4(targetDir, "components")
|
|
293
305
|
];
|
|
294
306
|
for (const dir of dirs) {
|
|
295
307
|
await mkdir(dir, { recursive: true });
|
|
296
308
|
}
|
|
297
309
|
await writeFile(
|
|
298
|
-
|
|
310
|
+
join4(targetDir, "course.yaml"),
|
|
299
311
|
COURSE_YAML_TEMPLATE.replace("{{title}}", yamlTitle)
|
|
300
312
|
);
|
|
301
|
-
await writeFile(
|
|
313
|
+
await writeFile(join4(targetDir, "lessons", "welcome.md"), WELCOME_MD);
|
|
302
314
|
await writeFile(
|
|
303
|
-
|
|
315
|
+
join4(targetDir, "interactions", "phishing-lab", "index.html"),
|
|
304
316
|
PHISHING_HTML
|
|
305
317
|
);
|
|
306
|
-
await writeFile(
|
|
307
|
-
await writeFile(
|
|
308
|
-
await writeFile(
|
|
309
|
-
await writeFile(
|
|
318
|
+
await writeFile(join4(targetDir, "assessments", "final.yaml"), FINAL_ASSESSMENT);
|
|
319
|
+
await writeFile(join4(targetDir, "lxpack.config.json"), LXPACK_CONFIG);
|
|
320
|
+
await writeFile(join4(targetDir, "theme", ".gitkeep"), "");
|
|
321
|
+
await writeFile(join4(targetDir, "components", ".gitkeep"), "");
|
|
310
322
|
console.log(pc.green(`\u2713 Created LXPack course: ${targetDir}`));
|
|
311
323
|
console.log();
|
|
312
324
|
console.log("Next steps:");
|
|
@@ -320,8 +332,35 @@ async function initCommand(projectName, options = {}) {
|
|
|
320
332
|
import Fastify from "fastify";
|
|
321
333
|
import fastifyStatic from "@fastify/static";
|
|
322
334
|
import pc2 from "picocolors";
|
|
323
|
-
import { validateCourse
|
|
324
|
-
|
|
335
|
+
import { validateCourse as validateCourse2 } from "@lxpack/validators";
|
|
336
|
+
|
|
337
|
+
// src/lib/validated-course.ts
|
|
338
|
+
import {
|
|
339
|
+
validateCourse,
|
|
340
|
+
buildRuntimeAssessmentBundleFromParsed
|
|
341
|
+
} from "@lxpack/validators";
|
|
342
|
+
async function loadValidatedCourseContext(courseDir) {
|
|
343
|
+
const validation = await validateCourse(courseDir);
|
|
344
|
+
if (!validation.valid || !validation.manifest) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
const parsed = validation.parsedAssessments ?? /* @__PURE__ */ new Map();
|
|
348
|
+
const assessmentBundle = buildRuntimeAssessmentBundleFromParsed(parsed);
|
|
349
|
+
return {
|
|
350
|
+
courseDir,
|
|
351
|
+
validation,
|
|
352
|
+
manifest: validation.manifest,
|
|
353
|
+
assessmentBundle
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function printValidationIssues(validation) {
|
|
357
|
+
for (const issue of validation.issues) {
|
|
358
|
+
console.error(` ${issue.path}: ${issue.message}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/commands/preview.ts
|
|
363
|
+
import { buildLearnerPageHtml, safeJsonForHtml } from "@lxpack/scorm";
|
|
325
364
|
async function loadPreviewStyles(assetsDir = getRuntimeAssetsDir()) {
|
|
326
365
|
return loadRuntimeStyles(assetsDir);
|
|
327
366
|
}
|
|
@@ -341,6 +380,12 @@ function buildPreviewConfig(manifest, assessmentBundle) {
|
|
|
341
380
|
async function createPreviewServer(courseDir, manifest, assessmentBundle) {
|
|
342
381
|
const runtimeDir = getRuntimeAssetsDir();
|
|
343
382
|
const app = Fastify({ logger: false });
|
|
383
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
384
|
+
const path = request.url.split("?")[0] ?? "";
|
|
385
|
+
if (path.startsWith("/course/assessments/")) {
|
|
386
|
+
return reply.code(404).send("Not found");
|
|
387
|
+
}
|
|
388
|
+
});
|
|
344
389
|
await app.register(fastifyStatic, {
|
|
345
390
|
root: courseDir,
|
|
346
391
|
prefix: "/course/",
|
|
@@ -360,52 +405,39 @@ async function createPreviewServer(courseDir, manifest, assessmentBundle) {
|
|
|
360
405
|
});
|
|
361
406
|
}
|
|
362
407
|
app.get("/", async (_req, reply) => {
|
|
363
|
-
const html =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
</head>
|
|
371
|
-
<body>
|
|
372
|
-
<div id="lxpack-app"></div>
|
|
373
|
-
<script type="application/json" id="lxpack-config">${config}</script>
|
|
374
|
-
<script>
|
|
375
|
-
window.__LXPACK_CONFIG__ = JSON.parse(document.getElementById('lxpack-config').textContent);
|
|
376
|
-
</script>
|
|
377
|
-
${componentsJs ? '<script type="module" src="/runtime/components.js"></script>' : ""}
|
|
378
|
-
<script type="module" src="/runtime/client.js"></script>
|
|
379
|
-
</body>
|
|
380
|
-
</html>`;
|
|
408
|
+
const html = buildLearnerPageHtml({
|
|
409
|
+
title: `${manifest.title} \u2014 Preview`,
|
|
410
|
+
runtimeCss: stylesCss,
|
|
411
|
+
configJson: config,
|
|
412
|
+
runtimeScript: "/runtime/client.js",
|
|
413
|
+
componentsScript: componentsJs ? "/runtime/components.js" : void 0
|
|
414
|
+
});
|
|
381
415
|
return reply.type("text/html").send(html);
|
|
382
416
|
});
|
|
383
417
|
app.get("/health", async () => ({ status: "ok" }));
|
|
384
418
|
return app;
|
|
385
419
|
}
|
|
386
420
|
async function startPreview(courseDir, _options = {}) {
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
console.error(
|
|
421
|
+
const ctx = await loadValidatedCourseContext(courseDir);
|
|
422
|
+
if (!ctx) {
|
|
423
|
+
const validation2 = await validateCourse2(courseDir);
|
|
424
|
+
if (!validation2.manifest) {
|
|
425
|
+
console.error(pc2.red("Cannot preview: course manifest is invalid"));
|
|
426
|
+
for (const issue of validation2.issues) {
|
|
427
|
+
console.error(` ${issue.path}: ${issue.message}`);
|
|
428
|
+
}
|
|
429
|
+
process.exit(1);
|
|
392
430
|
}
|
|
393
|
-
process.exit(1);
|
|
394
|
-
}
|
|
395
|
-
if (!validation.valid) {
|
|
396
431
|
console.error(pc2.red("Cannot preview: course validation failed"));
|
|
397
|
-
for (const issue of
|
|
432
|
+
for (const issue of validation2.issues) {
|
|
398
433
|
console.error(` ${issue.path}: ${issue.message}`);
|
|
399
434
|
}
|
|
400
435
|
process.exit(1);
|
|
401
436
|
}
|
|
402
|
-
const assessmentBundle =
|
|
403
|
-
courseDir,
|
|
404
|
-
validation.manifest
|
|
405
|
-
);
|
|
437
|
+
const { validation, manifest, assessmentBundle } = ctx;
|
|
406
438
|
const app = await createPreviewServer(
|
|
407
439
|
courseDir,
|
|
408
|
-
|
|
440
|
+
manifest,
|
|
409
441
|
assessmentBundle
|
|
410
442
|
);
|
|
411
443
|
return { app, validation };
|
|
@@ -452,11 +484,11 @@ function logPreviewStarted(host, port) {
|
|
|
452
484
|
}
|
|
453
485
|
|
|
454
486
|
// src/commands/validate.ts
|
|
455
|
-
import { validateCourse as
|
|
487
|
+
import { validateCourse as validateCourse3 } from "@lxpack/validators";
|
|
456
488
|
import pc3 from "picocolors";
|
|
457
489
|
async function validateCommand() {
|
|
458
490
|
const courseDir = findCourseDir();
|
|
459
|
-
const result = await
|
|
491
|
+
const result = await validateCourse3(courseDir);
|
|
460
492
|
if (result.manifest) {
|
|
461
493
|
console.log(
|
|
462
494
|
pc3.dim(`Course: ${result.manifest.title} v${result.manifest.version}`)
|
|
@@ -479,13 +511,35 @@ async function validateCommand() {
|
|
|
479
511
|
|
|
480
512
|
// src/commands/build.ts
|
|
481
513
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
482
|
-
import { join as
|
|
483
|
-
import {
|
|
484
|
-
import {
|
|
485
|
-
validateCourse as validateCourse3,
|
|
486
|
-
buildRuntimeAssessmentBundle as buildRuntimeAssessmentBundle2
|
|
487
|
-
} from "@lxpack/validators";
|
|
514
|
+
import { join as join5 } from "path";
|
|
515
|
+
import { courseSlug } from "@lxpack/scorm";
|
|
488
516
|
import pc4 from "picocolors";
|
|
517
|
+
|
|
518
|
+
// src/packagers/index.ts
|
|
519
|
+
import {
|
|
520
|
+
packageCourse,
|
|
521
|
+
packageScorm2004Dir,
|
|
522
|
+
packageStandaloneDir
|
|
523
|
+
} from "@lxpack/scorm";
|
|
524
|
+
var zipPackagers = {
|
|
525
|
+
scorm12: { package: packageCourse },
|
|
526
|
+
scorm2004: { package: packageCourse },
|
|
527
|
+
standalone: { package: packageCourse }
|
|
528
|
+
};
|
|
529
|
+
var dirPackagers = {
|
|
530
|
+
scorm12: { package: packageStandaloneDir },
|
|
531
|
+
scorm2004: { package: packageScorm2004Dir },
|
|
532
|
+
standalone: { package: packageStandaloneDir }
|
|
533
|
+
};
|
|
534
|
+
function getZipPackager(target) {
|
|
535
|
+
return zipPackagers[target];
|
|
536
|
+
}
|
|
537
|
+
function getDirPackager(target) {
|
|
538
|
+
return dirPackagers[target];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/commands/build.ts
|
|
542
|
+
import { validateCourse as validateCourse4 } from "@lxpack/validators";
|
|
489
543
|
var VALID_TARGETS = ["scorm12", "scorm2004", "standalone"];
|
|
490
544
|
async function buildCommand(options) {
|
|
491
545
|
const courseDir = findCourseDir();
|
|
@@ -499,19 +553,14 @@ async function buildCommand(options) {
|
|
|
499
553
|
);
|
|
500
554
|
process.exit(1);
|
|
501
555
|
}
|
|
502
|
-
const
|
|
503
|
-
if (!
|
|
556
|
+
const ctx = await loadValidatedCourseContext(courseDir);
|
|
557
|
+
if (!ctx) {
|
|
504
558
|
console.error(pc4.red("Cannot build: course validation failed"));
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
559
|
+
const validation = await validateCourse4(courseDir);
|
|
560
|
+
printValidationIssues(validation);
|
|
508
561
|
process.exit(1);
|
|
509
562
|
}
|
|
510
|
-
const manifest =
|
|
511
|
-
const assessmentBundle = await buildRuntimeAssessmentBundle2(
|
|
512
|
-
courseDir,
|
|
513
|
-
manifest
|
|
514
|
-
);
|
|
563
|
+
const { manifest, assessmentBundle } = ctx;
|
|
515
564
|
const [{ clientJs, css }, componentsBundleJs] = await Promise.all([
|
|
516
565
|
readRuntimeBundle(),
|
|
517
566
|
readComponentsBundle()
|
|
@@ -530,8 +579,8 @@ async function buildCommand(options) {
|
|
|
530
579
|
assessmentBundle
|
|
531
580
|
};
|
|
532
581
|
if (options.dir) {
|
|
533
|
-
const outputDir = options.output ??
|
|
534
|
-
const result = await
|
|
582
|
+
const outputDir = options.output ?? join5(outputRoot, target);
|
|
583
|
+
const result = await getDirPackager(target).package({
|
|
535
584
|
...packageOptions,
|
|
536
585
|
outputDir
|
|
537
586
|
});
|
|
@@ -540,8 +589,8 @@ async function buildCommand(options) {
|
|
|
540
589
|
console.log(` Files: ${result.fileCount}`);
|
|
541
590
|
} else {
|
|
542
591
|
const defaultName = target === "standalone" ? `${slug}-standalone.zip` : target === "scorm2004" ? `${slug}-scorm2004.zip` : `${slug}-scorm12.zip`;
|
|
543
|
-
const outputPath = options.output ??
|
|
544
|
-
const result = await
|
|
592
|
+
const outputPath = options.output ?? join5(outputRoot, defaultName);
|
|
593
|
+
const result = await getZipPackager(target).package({
|
|
545
594
|
...packageOptions,
|
|
546
595
|
outputPath
|
|
547
596
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lxpack/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI for building, validating, and packaging LXPack courses",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"picocolors": "^1.1.1",
|
|
40
40
|
"yaml": "^2.7.0",
|
|
41
41
|
"zod": "^3.24.2",
|
|
42
|
-
"@lxpack/
|
|
43
|
-
"@lxpack/
|
|
44
|
-
"@lxpack/
|
|
45
|
-
"@lxpack/
|
|
42
|
+
"@lxpack/components": "0.2.1",
|
|
43
|
+
"@lxpack/validators": "0.2.1",
|
|
44
|
+
"@lxpack/runtime": "0.2.1",
|
|
45
|
+
"@lxpack/scorm": "0.2.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.13.10",
|