coursecode 0.1.9 → 0.1.10
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/bin/cli.js +9 -8
- package/framework/css/01-base.css +24 -0
- package/framework/css/design-tokens.css +8 -0
- package/lib/cloud-certs.js +44 -55
- package/lib/cloud.js +11 -14
- package/package.json +3 -2
package/bin/cli.js
CHANGED
|
@@ -21,23 +21,23 @@ import path from 'path';
|
|
|
21
21
|
import fs from 'fs';
|
|
22
22
|
|
|
23
23
|
// =============================================================================
|
|
24
|
-
// CORPORATE NETWORK: System CA cert
|
|
24
|
+
// CORPORATE NETWORK: System CA cert injection
|
|
25
25
|
//
|
|
26
26
|
// On corporate machines with SSL-inspecting proxies (e.g. Zscaler), the proxy
|
|
27
27
|
// presents its own CA certificate. Node.js ships its own CA bundle and ignores
|
|
28
28
|
// the OS trust store, so TLS verification fails.
|
|
29
29
|
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// The NODE_EXTRA_CA_CERTS guard prevents an infinite re-exec loop.
|
|
30
|
+
// Windows: win-ca injects certs directly into Node's TLS context (in-process,
|
|
31
|
+
// no subprocess, no re-exec). Works regardless of PowerShell policy.
|
|
32
|
+
// macOS/Linux: exports OS certs to a temp PEM file and re-execs with
|
|
33
|
+
// NODE_EXTRA_CA_CERTS. The guard prevents an infinite loop.
|
|
35
34
|
// =============================================================================
|
|
36
35
|
|
|
37
36
|
if (!process.env.NODE_EXTRA_CA_CERTS) {
|
|
38
|
-
const {
|
|
39
|
-
const certPath = await
|
|
37
|
+
const { injectSystemCerts } = await import('../lib/cloud-certs.js');
|
|
38
|
+
const certPath = await injectSystemCerts();
|
|
40
39
|
if (certPath) {
|
|
40
|
+
// macOS/Linux: re-exec with NODE_EXTRA_CA_CERTS pointing to PEM file
|
|
41
41
|
const { execFileSync } = await import('child_process');
|
|
42
42
|
execFileSync(process.execPath, process.argv.slice(1), {
|
|
43
43
|
env: { ...process.env, NODE_EXTRA_CA_CERTS: certPath },
|
|
@@ -45,6 +45,7 @@ if (!process.env.NODE_EXTRA_CA_CERTS) {
|
|
|
45
45
|
});
|
|
46
46
|
process.exit(0);
|
|
47
47
|
}
|
|
48
|
+
// Windows: win-ca already injected certs in-process — continue normally
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -136,6 +136,30 @@ em, i {
|
|
|
136
136
|
color: var(--color-gray-800);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/* Styled Scrollbars (cross-platform) */
|
|
140
|
+
html {
|
|
141
|
+
scrollbar-width: thin;
|
|
142
|
+
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
::-webkit-scrollbar {
|
|
146
|
+
width: 8px;
|
|
147
|
+
height: 8px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
::-webkit-scrollbar-track {
|
|
151
|
+
background: var(--scrollbar-track);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
::-webkit-scrollbar-thumb {
|
|
155
|
+
background: var(--scrollbar-thumb);
|
|
156
|
+
border-radius: 4px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
::-webkit-scrollbar-thumb:hover {
|
|
160
|
+
background: var(--scrollbar-thumb-hover);
|
|
161
|
+
}
|
|
162
|
+
|
|
139
163
|
/* Link Styles */
|
|
140
164
|
a {
|
|
141
165
|
color: var(--color-primary);
|
|
@@ -441,6 +441,11 @@
|
|
|
441
441
|
--sidebar-scrollbar-thumb-hover: var(--color-gray-400); /* Sidebar scrollbar thumb (hover) */
|
|
442
442
|
--sidebar-backdrop: var(--bg-overlay); /* Sidebar overlay backdrop color */
|
|
443
443
|
|
|
444
|
+
/* Scrollbar Tokens (global, content area) */
|
|
445
|
+
--scrollbar-thumb: var(--color-gray-300); /* Scrollbar thumb */
|
|
446
|
+
--scrollbar-thumb-hover: var(--color-gray-400); /* Scrollbar thumb (hover) */
|
|
447
|
+
--scrollbar-track: transparent; /* Scrollbar track background */
|
|
448
|
+
|
|
444
449
|
/* Footer Tokens */
|
|
445
450
|
--footer-bg: var(--bg-surface); /* Footer background */
|
|
446
451
|
--footer-text: var(--text-primary); /* Footer text color */
|
|
@@ -678,6 +683,9 @@
|
|
|
678
683
|
--sidebar-scrollbar-thumb: var(--color-gray-500);
|
|
679
684
|
--sidebar-scrollbar-thumb-hover: var(--color-gray-400);
|
|
680
685
|
--sidebar-backdrop: color-mix(in srgb, var(--palette-black) 38%, transparent);
|
|
686
|
+
--scrollbar-thumb: var(--color-gray-500);
|
|
687
|
+
--scrollbar-thumb-hover: var(--color-gray-400);
|
|
688
|
+
--scrollbar-track: transparent;
|
|
681
689
|
--footer-bg: var(--bg-surface);
|
|
682
690
|
--footer-text: var(--text-primary);
|
|
683
691
|
--footer-border: var(--border-default);
|
package/lib/cloud-certs.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cloud-certs.js — System CA certificate
|
|
2
|
+
* cloud-certs.js — System CA certificate injection for corporate network compatibility.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* On corporate machines with SSL-inspecting proxies (e.g. Zscaler), the proxy
|
|
5
|
+
* presents its own CA certificate. Node.js ships its own CA bundle and ignores
|
|
6
|
+
* the OS trust store, causing TLS verification failures.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Platform strategy:
|
|
9
|
+
* - Windows: `win-ca` (native N-API addon, calls Windows CryptoAPI directly)
|
|
10
|
+
* - macOS: `security` CLI exports system keychains to PEM
|
|
11
|
+
* - Linux: reads well-known CA bundle file paths
|
|
12
|
+
*
|
|
13
|
+
* On macOS/Linux, returns a PEM file path for NODE_EXTRA_CA_CERTS.
|
|
14
|
+
* On Windows, injects certs directly into Node's TLS context (no file needed).
|
|
15
|
+
* Never throws — silent no-op on non-corporate machines.
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import { execFile } from 'child_process';
|
|
12
19
|
import { promisify } from 'util';
|
|
20
|
+
import { createRequire } from 'module';
|
|
13
21
|
import fs from 'fs';
|
|
14
22
|
import os from 'os';
|
|
15
23
|
import path from 'path';
|
|
@@ -17,25 +25,34 @@ import crypto from 'crypto';
|
|
|
17
25
|
|
|
18
26
|
const execFileAsync = promisify(execFile);
|
|
19
27
|
|
|
20
|
-
/** Cached result — only
|
|
21
|
-
let
|
|
28
|
+
/** Cached result — only run once per process lifetime. */
|
|
29
|
+
let _applied = false;
|
|
22
30
|
|
|
23
31
|
/**
|
|
24
|
-
*
|
|
32
|
+
* Inject the OS system root certificates into Node's TLS context.
|
|
33
|
+
*
|
|
34
|
+
* On Windows, win-ca patches Node's TLS in-process (no file needed, no re-exec).
|
|
35
|
+
* On macOS/Linux, returns a PEM file path for NODE_EXTRA_CA_CERTS re-exec.
|
|
25
36
|
*
|
|
26
|
-
* @returns {Promise<string|null>}
|
|
37
|
+
* @returns {Promise<string|null>} PEM file path (macOS/Linux) or null (Windows/unavailable).
|
|
27
38
|
*/
|
|
28
|
-
export async function
|
|
29
|
-
if (
|
|
39
|
+
export async function injectSystemCerts() {
|
|
40
|
+
if (_applied) return null;
|
|
41
|
+
_applied = true;
|
|
30
42
|
|
|
31
43
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
_cachedCertPath = null;
|
|
44
|
+
if (process.platform === 'win32') {
|
|
45
|
+
injectWindowsCerts();
|
|
35
46
|
return null;
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
//
|
|
49
|
+
// macOS/Linux: export to PEM file for NODE_EXTRA_CA_CERTS
|
|
50
|
+
const pem = process.platform === 'darwin'
|
|
51
|
+
? await readMacosCerts()
|
|
52
|
+
: readLinuxCerts();
|
|
53
|
+
|
|
54
|
+
if (!pem || !pem.trim()) return null;
|
|
55
|
+
|
|
39
56
|
const hash = crypto.createHash('sha1').update(pem).digest('hex').slice(0, 8);
|
|
40
57
|
const certPath = path.join(os.tmpdir(), `coursecode-ca-${hash}.pem`);
|
|
41
58
|
|
|
@@ -43,31 +60,26 @@ export async function exportSystemCerts() {
|
|
|
43
60
|
fs.writeFileSync(certPath, pem, { mode: 0o600 });
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
_cachedCertPath = certPath;
|
|
47
63
|
return certPath;
|
|
48
64
|
} catch {
|
|
49
|
-
_cachedCertPath = null;
|
|
50
65
|
return null;
|
|
51
66
|
}
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
70
|
+
* Windows: inject system root certs via win-ca.
|
|
71
|
+
*
|
|
72
|
+
* win-ca is a native N-API addon that calls Windows CryptoAPI directly.
|
|
73
|
+
* No PowerShell, no subprocesses, no temp files. Works regardless of
|
|
74
|
+
* execution policy, AppLocker, or PowerShell availability.
|
|
75
|
+
*
|
|
76
|
+
* The { inject: '+' } mode patches tls.createSecureContext() so system
|
|
77
|
+
* certs are used *in addition to* Node's built-in CA bundle.
|
|
57
78
|
*/
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return readMacosCerts();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (platform === 'win32') {
|
|
66
|
-
return readWindowsCerts();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Linux/other: read well-known bundle paths directly
|
|
70
|
-
return readLinuxCerts();
|
|
79
|
+
function injectWindowsCerts() {
|
|
80
|
+
const require = createRequire(import.meta.url);
|
|
81
|
+
const winCa = require('win-ca/api');
|
|
82
|
+
winCa({ inject: '+' });
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
/**
|
|
@@ -75,7 +87,6 @@ async function readSystemCerts() {
|
|
|
75
87
|
* This includes all roots installed via Apple MDM / System Preferences.
|
|
76
88
|
*/
|
|
77
89
|
async function readMacosCerts() {
|
|
78
|
-
// Export from all common keychains — ignore errors on missing keychains
|
|
79
90
|
const keychains = [
|
|
80
91
|
'/Library/Keychains/SystemRootCertificates.keychain',
|
|
81
92
|
'/System/Library/Keychains/SystemRootCertificates.keychain',
|
|
@@ -97,28 +108,6 @@ async function readMacosCerts() {
|
|
|
97
108
|
return pems.join('\n');
|
|
98
109
|
}
|
|
99
110
|
|
|
100
|
-
/**
|
|
101
|
-
* Windows: export LocalMachine\Root via PowerShell.
|
|
102
|
-
* This includes certs deployed via Group Policy / MDM.
|
|
103
|
-
*/
|
|
104
|
-
async function readWindowsCerts() {
|
|
105
|
-
const script = [
|
|
106
|
-
'$certs = Get-ChildItem -Path Cert:\\LocalMachine\\Root',
|
|
107
|
-
'$pems = $certs | ForEach-Object {',
|
|
108
|
-
' $bytes = $_.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)',
|
|
109
|
-
' $b64 = [System.Convert]::ToBase64String($bytes, "InsertLineBreaks")',
|
|
110
|
-
' "-----BEGIN CERTIFICATE-----`n" + $b64 + "`n-----END CERTIFICATE-----"',
|
|
111
|
-
'}',
|
|
112
|
-
'$pems -join "`n"',
|
|
113
|
-
].join('; ');
|
|
114
|
-
|
|
115
|
-
const { stdout } = await execFileAsync('powershell', [
|
|
116
|
-
'-NoProfile', '-NonInteractive', '-Command', script,
|
|
117
|
-
], { maxBuffer: 16 * 1024 * 1024 });
|
|
118
|
-
|
|
119
|
-
return stdout;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
111
|
/**
|
|
123
112
|
* Linux: read the system CA bundle from well-known locations.
|
|
124
113
|
* No subprocess needed — just read the file directly.
|
package/lib/cloud.js
CHANGED
|
@@ -893,21 +893,18 @@ export async function status() {
|
|
|
893
893
|
// =============================================================================
|
|
894
894
|
|
|
895
895
|
/**
|
|
896
|
-
* Zip a directory's contents using
|
|
897
|
-
* Falls back to a tar+gzip approach if zip isn't available.
|
|
896
|
+
* Zip a directory's contents using archiver (cross-platform, no native tools needed).
|
|
898
897
|
*/
|
|
899
|
-
function zipDirectory(sourceDir, outputPath) {
|
|
898
|
+
async function zipDirectory(sourceDir, outputPath) {
|
|
899
|
+
const archiver = (await import('archiver')).default;
|
|
900
900
|
return new Promise((resolve, reject) => {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
);
|
|
901
|
+
const output = fs.createWriteStream(outputPath);
|
|
902
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
903
|
+
|
|
904
|
+
output.on('close', () => resolve());
|
|
905
|
+
archive.on('error', reject);
|
|
906
|
+
archive.pipe(output);
|
|
907
|
+
archive.directory(sourceDir, false);
|
|
908
|
+
archive.finalize();
|
|
912
909
|
});
|
|
913
910
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coursecode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Multi-format course authoring framework with CLI tools (SCORM 2004, SCORM 1.2, cmi5, LTI 1.3)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
101
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
102
|
+
"archiver": "^7.0.1",
|
|
102
103
|
"commander": "^14.0.3",
|
|
103
104
|
"lz-string": "^1.5.0",
|
|
104
105
|
"mammoth": "^1.8.0",
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"pdf2json": "^4.0.2",
|
|
109
110
|
"pdf2md": "^1.0.2",
|
|
110
111
|
"puppeteer-core": "^24.37.2",
|
|
112
|
+
"win-ca": "^3.5.1",
|
|
111
113
|
"ws": "^8.18.0"
|
|
112
114
|
},
|
|
113
115
|
"devDependencies": {
|
|
@@ -115,7 +117,6 @@
|
|
|
115
117
|
"@vitest/coverage-v8": "^4.0.18",
|
|
116
118
|
"@xapi/cmi5": "^1.4.0",
|
|
117
119
|
"acorn": "^8.15.0",
|
|
118
|
-
"archiver": "^7.0.1",
|
|
119
120
|
"eslint": "^10.0.0",
|
|
120
121
|
"globals": "^17.3.0",
|
|
121
122
|
"jose": "^6.1.3",
|