@khanhcan148/mk 0.1.16 → 0.1.18

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/src/lib/auth.js CHANGED
@@ -155,7 +155,13 @@ export async function startDeviceFlow(opts = {}) {
155
155
  'Content-Type': 'application/x-www-form-urlencoded',
156
156
  Accept: 'application/json'
157
157
  },
158
- body: `client_id=${GITHUB_CLIENT_ID}&device_code=${device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`
158
+ // Use URLSearchParams to prevent parameter injection if device_code ever contains '&' chars
159
+ // (mirrors the same safe pattern used in Step 1 above for the initial device-code request).
160
+ body: new URLSearchParams({
161
+ client_id: GITHUB_CLIENT_ID,
162
+ device_code,
163
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
164
+ }).toString()
159
165
  });
160
166
 
161
167
  const tokenData = await tokenRes.json();
@@ -1,13 +1,28 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { readFileSync } from 'node:fs';
2
+ import { createReadStream } from 'node:fs';
3
3
 
4
4
  /**
5
- * Compute SHA-256 checksum of a file.
5
+ * Compute SHA-256 checksum of a file asynchronously using a streaming pipeline.
6
+ * Using createReadStream avoids loading the entire file into memory, which is
7
+ * important for large binary assets in the kit. Promise.all callers can parallelise
8
+ * multiple checksums without blocking the event loop.
9
+ *
6
10
  * @param {string} filePath - Absolute path to file
7
- * @returns {string} Checksum string prefixed with 'sha256:'
11
+ * @returns {Promise<string>} Checksum string prefixed with 'sha256:'
8
12
  */
9
13
  export function computeChecksum(filePath) {
10
- const content = readFileSync(filePath);
11
- const hash = createHash('sha256').update(content).digest('hex');
12
- return `sha256:${hash}`;
14
+ return new Promise((resolve, reject) => {
15
+ const hash = createHash('sha256');
16
+ const stream = createReadStream(filePath);
17
+ stream.on('data', (chunk) => hash.update(chunk));
18
+ stream.on('end', () => resolve(`sha256:${hash.digest('hex')}`));
19
+ // H6: wrap the raw fs.Error so the absolute path in err.path never reaches
20
+ // user stderr while preserving the causal chain for debuggers. The top-level
21
+ // message is the errno code (callers branch on err.code; err.cause retains
22
+ // the original for stack-trace inspection when needed).
23
+ stream.on('error', (e) => {
24
+ const code = e && e.code ? e.code : 'checksum read failed';
25
+ reject(new Error(code, e ? { cause: e } : undefined));
26
+ });
27
+ });
13
28
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Default in-flight worker cap for `pLimit` over file-descriptor-bound tasks.
3
+ *
4
+ * 16 is 8× typical disk parallelism and well under macOS default `ulimit -n 256`.
5
+ * Chosen empirically — higher values don't meaningfully speed up SHA-256 of
6
+ * kit-sized files, and lower values serialise too aggressively on SSD.
7
+ */
8
+ export const DEFAULT_CONCURRENCY_CAP = 16;
9
+
10
+ /**
11
+ * Bounded-concurrency helper for fan-out over async tasks.
12
+ *
13
+ * M3 — `Promise.all(items.map(computeChecksum))` opens N file descriptors at
14
+ * once. On installs with hundreds of files this blows past the default
15
+ * `ulimit -n` (256 on macOS) and causes EMFILE. `pLimit` caps the in-flight
16
+ * worker pool; results preserve input order via explicit index assignment.
17
+ *
18
+ * @template T
19
+ * @param {Array<() => Promise<T>>} tasks Array of thunks. Each thunk is
20
+ * invoked at most once by exactly one worker.
21
+ * @param {number} cap Max concurrent workers (>=1).
22
+ * @returns {Promise<T[]>} Results indexed to match `tasks`.
23
+ */
24
+ export async function pLimit(tasks, cap) {
25
+ const results = new Array(tasks.length);
26
+ let next = 0;
27
+ const workerCount = Math.max(1, Math.min(cap, tasks.length));
28
+ const workers = Array.from({ length: workerCount }, async () => {
29
+ while (next < tasks.length) {
30
+ const idx = next++;
31
+ results[idx] = await tasks[idx]();
32
+ }
33
+ });
34
+ await Promise.all(workers);
35
+ return results;
36
+ }
@@ -8,6 +8,14 @@ export const KIT_SUBDIRS = ['agents', 'skills', 'workflows', 'hooks'];
8
8
  */
9
9
  export const MANIFEST_FILENAME = '.mk-manifest.json';
10
10
 
11
+ /**
12
+ * Skills that are internal to the kit and must NOT be distributed to end users.
13
+ * Each entry is matched as a full directory segment: `/skills/<name>/`.
14
+ * The trailing `/` in the match pattern prevents false positives on substring names
15
+ * (e.g. `mk-selftest-extended` is NOT matched by `mk-selftest`).
16
+ */
17
+ export const KIT_INTERNAL_SKILLS = ['mk-selftest'];
18
+
11
19
  /**
12
20
  * Files/patterns to exclude during copy
13
21
  */