skilld-harness 3.1.0 → 3.2.0

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 CHANGED
@@ -40,6 +40,51 @@ An installed Skill can include cleanup warnings after promotion.
40
40
  Pass `fetch` to `createSkillHarness` when the host owns HTTP access.
41
41
  The default adapter uses the Node global fetch implementation.
42
42
 
43
+ ## Local sandbox
44
+
45
+ `createSkillHarness` needs a sandbox. Import `skilld-harness/sandbox-local` to
46
+ run a Skill on the computer that starts it, with no hosted sandbox account.
47
+
48
+ ```ts
49
+ import { createOpenCode } from '@ai-sdk/harness-opencode'
50
+ import { createSkillHarness } from 'skilld-harness'
51
+ import { createLocalSandbox } from 'skilld-harness/sandbox-local'
52
+
53
+ const skillHarness = createSkillHarness({
54
+ harness: createOpenCode({
55
+ provider: 'opencode-go',
56
+ openCodeConfig: { agent: { general: { model: 'opencode-go/glm-5.3-flash' } } },
57
+ }),
58
+ sandbox: createLocalSandbox(),
59
+ })
60
+ ```
61
+
62
+ The session runs in a new temporary directory and exposes one port on
63
+ `127.0.0.1` for bridge-backed Harness adapters. `destroy` kills every process
64
+ the session started and removes the directory.
65
+
66
+ | Option | Default | Effect |
67
+ | --- | --- | --- |
68
+ | `root` | a new temporary directory | Session root directory. |
69
+ | `port` | a free port from the operating system | Bridge port. |
70
+ | `keepRoot` | `false` | Keep the session root after `destroy`. |
71
+
72
+ The local sandbox needs POSIX `sh` at `/bin/sh` and GNU `find`, because the
73
+ Harness inventories output with `find -printf`. It runs on Linux. It does not
74
+ support macOS, whose `find` has no `-printf`, or Windows.
75
+
76
+ A Skill that carries a large reference tree will exceed the default output
77
+ policy, which stops at 64 files. Raise it on `createSkillHarness`:
78
+
79
+ ```ts
80
+ createSkillHarness({ harness, sandbox, outputPolicy: { maxOutputFiles: 512 } })
81
+ ```
82
+
83
+ **It applies no isolation.** Every process reaches the whole computer and the
84
+ caller's environment. Use it for your own Skills on your own computer or on a
85
+ self-hosted runner. Use a hosted sandbox provider when the Harness must contain
86
+ what it runs.
87
+
43
88
  ## Visible Skills
44
89
 
45
90
  Import `skilld-harness/skills` to load the published Skill instructions.
package/dist/index.mjs CHANGED
@@ -301,6 +301,62 @@ const promoteSkill = async (rootDir, name, files, attempts) => {
301
301
  cause
302
302
  });
303
303
  };
304
+ const searchCommand = /\b(?:git grep|grep|rg)\b/;
305
+ const inlineCode = /`([^`\n]+)`/g;
306
+ const fencedBlock = /```[^\n]*\n[\s\S]*?```/g;
307
+ const denial = /\b(no|not|none|never|absent|missing|lacks|without|instead|rather|empty|undeclared|unavailable|nothing)\b|\bdoes ?n[o']t\b/i;
308
+ const creation = /\b(?:copy|create|write|generate)\b/i;
309
+ function fencedBlocks(markdown) {
310
+ return markdown.match(fencedBlock) ?? [];
311
+ }
312
+ function proseParagraphs(markdown) {
313
+ return markdown.replace(fencedBlock, "").split(/\n[ \t]*\n/).map((paragraph) => paragraph.replace(/\n/g, " ").trim()).filter((paragraph) => paragraph.length > 0);
314
+ }
315
+ const pathExtension = /\.(?:md|mdx|json|jsonc|ts|tsx|mts|cts|js|mjs|cjs|jsx|vue|svelte|txt|yml|yaml|toml|html|css|scss|rs|py|go|sh|lock|env|csv|xml)$/i;
316
+ const versionShape = /^v?\d+(?:\.\d+)*$/i;
317
+ const productName = /^[A-Za-z][A-Za-z0-9_]*\.[A-Za-z0-9]+$/;
318
+ function pathCandidate(token) {
319
+ if (token.length === 0 || /\s/.test(token)) return null;
320
+ if (/^[-$#<{[]/.test(token)) return null;
321
+ if (token.includes("://") || /[()|;&"'@:]/.test(token)) return null;
322
+ const trimmed = token.replace(/^\.\//, "").replace(/\/+$/, "");
323
+ if (trimmed.length === 0 || trimmed.startsWith("..")) return null;
324
+ if (versionShape.test(trimmed) || productName.test(trimmed) || /\/\d+$/.test(trimmed)) return null;
325
+ if (!trimmed.includes("/") && !pathExtension.test(trimmed)) return null;
326
+ return trimmed;
327
+ }
328
+ function literalPrefix(candidate) {
329
+ const wildcard = candidate.search(/[*?]/);
330
+ if (wildcard === -1) return candidate;
331
+ return candidate.slice(0, wildcard).replace(/[^/]*$/, "").replace(/\/+$/, "");
332
+ }
333
+ function resolves(candidate, known) {
334
+ if (known.has(candidate)) return true;
335
+ const prefix = literalPrefix(candidate);
336
+ if (prefix.length === 0) return true;
337
+ for (const path of known) {
338
+ if (path.startsWith(`${candidate}.`)) return true;
339
+ if (path === prefix || path.startsWith(`${prefix}/`)) return true;
340
+ }
341
+ return false;
342
+ }
343
+ function checkProjectSkill(input) {
344
+ const issues = [];
345
+ const known = /* @__PURE__ */ new Set([...input.projectPaths, ...input.outputPaths]);
346
+ const unknown = /* @__PURE__ */ new Set();
347
+ for (const paragraph of proseParagraphs(input.markdown)) {
348
+ if (denial.test(paragraph) || creation.test(paragraph)) continue;
349
+ for (const match of paragraph.matchAll(inlineCode)) {
350
+ const candidate = pathCandidate(match[1].trim());
351
+ if (candidate !== null && !resolves(candidate, known)) unknown.add(candidate);
352
+ }
353
+ }
354
+ for (const path of [...unknown].sort()) issues.push(`SKILL.md points at a path the project does not contain: ${path}.`);
355
+ const inline = [...input.markdown.matchAll(inlineCode)].map((match) => match[1]);
356
+ const commands = [...fencedBlocks(input.markdown), ...inline].join("\n");
357
+ if (!searchCommand.test(commands)) issues.push("SKILL.md must give the Agent at least one search command it can repeat.");
358
+ return issues;
359
+ }
304
360
  const allowedFrontmatter = /* @__PURE__ */ new Set([
305
361
  "name",
306
362
  "description",
@@ -322,12 +378,17 @@ function decodeText(content) {
322
378
  }
323
379
  }
324
380
  const isStringMap = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
325
- const validateGeneratedSkill = (name, files) => {
381
+ const validateGeneratedSkill = (name, files, project) => {
326
382
  const issues = [];
327
383
  const skillFiles = files.filter((file) => file.path === "SKILL.md");
328
384
  if (skillFiles.length !== 1) return invalid(["Output must contain exactly one SKILL.md file."]);
329
385
  const source = decodeText(skillFiles[0].content);
330
386
  if (source === null) return invalid(["SKILL.md must contain valid UTF-8 text."]);
387
+ if (project !== void 0) issues.push(...checkProjectSkill({
388
+ markdown: source,
389
+ projectPaths: project,
390
+ outputPaths: files.map((file) => file.path)
391
+ }));
331
392
  const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
332
393
  if (!match) return invalid(["SKILL.md must start with YAML frontmatter."]);
333
394
  if (match[2]?.trim().length === 0) issues.push("SKILL.md instructions must not be empty.");
@@ -1192,7 +1253,7 @@ function createSkillHarness(options) {
1192
1253
  const collected = await collectSandboxOutput(active.sandbox, outputPath, policy, runOptions.signal);
1193
1254
  if (collected._tag === "Err") return collected;
1194
1255
  if (prepared.value.skillName === "review-skill") return validateSkillReview(collected.value);
1195
- const validated = validateGeneratedSkill(prepared.value.outputName, collected.value);
1256
+ const validated = validateGeneratedSkill(prepared.value.outputName, collected.value, prepared.value.skillName === "generate-project-skill" ? prepared.value.source.files.map((file) => file.path) : void 0);
1196
1257
  if (validated._tag === "Err") return validated;
1197
1258
  if (!prepared.value.destination) return err({
1198
1259
  _tag: "InvalidInput",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["invalid","invalid","unavailable"],"sources":["../src/internal/result.ts","../src/internal/input.ts","../src/internal/output/collect.ts","../src/internal/output/promote.ts","../src/internal/output/validate.ts","../src/internal/source/host.ts","../src/internal/source/archive.ts","../src/internal/source/npm.ts","../src/harness.ts"],"sourcesContent":["export type Result<T, E>\n = | { readonly _tag: 'Ok', readonly value: T }\n | { readonly _tag: 'Err', readonly error: E }\n\nexport const ok = <T>(value: T): Result<T, never> => ({ _tag: 'Ok', value })\nexport const err = <E>(error: E): Result<never, E> => ({ _tag: 'Err', error })\n","import type { SkillOutputPolicy, SkillRun, SkillRunError } from '../types.ts'\nimport type { Result } from './result.ts'\nimport { resolve } from 'node:path'\nimport { isSkillName, resolveWithin } from './paths.ts'\nimport { err, ok } from './result.ts'\n\nfunction invalid(message: string): Result<never, SkillRunError> {\n return err({ _tag: 'InvalidInput', message })\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nexport function parseSkillRun(input: SkillRun): Result<SkillRun, SkillRunError> {\n if (!input || typeof input !== 'object' || !('_tag' in input))\n return invalid('Skill run input must be an object with a tag.')\n\n if (input._tag === 'PackageSkill') {\n if (!input.source || !input.destination)\n return invalid('Package Skill input is incomplete.')\n if (!isNonEmptyString(input.destination.rootDir) || !isSkillName(input.destination.name))\n return invalid('Package Skill destination is invalid.')\n if (input.source._tag === 'NpmPackage') {\n if (!isNonEmptyString(input.source.spec))\n return invalid('npm package spec is required.')\n return ok({\n _tag: 'PackageSkill',\n source: { _tag: 'NpmPackage', spec: input.source.spec },\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n if (input.source._tag === 'LocalPackage') {\n if (!isNonEmptyString(input.source.rootDir) || !isNonEmptyString(input.source.packageDir))\n return invalid('Local package paths are required.')\n if (resolveWithin(input.source.rootDir, input.source.packageDir) === null)\n return invalid('Local package directory must stay inside its root directory.')\n return ok({\n _tag: 'PackageSkill',\n source: {\n _tag: 'LocalPackage',\n rootDir: resolve(input.source.rootDir),\n packageDir: input.source.packageDir,\n },\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n return invalid('Package source tag is invalid.')\n }\n\n if (input._tag === 'ProjectSkill') {\n if (!isNonEmptyString(input.projectDir) || !input.destination)\n return invalid('Project Skill input is incomplete.')\n if (!isNonEmptyString(input.destination.rootDir) || !isSkillName(input.destination.name))\n return invalid('Project Skill destination is invalid.')\n return ok({\n _tag: 'ProjectSkill',\n projectDir: resolve(input.projectDir),\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n\n if (input._tag === 'ReviewSkill') {\n if (!isNonEmptyString(input.skillDir))\n return invalid('Skill review directory is required.')\n return ok({ _tag: 'ReviewSkill', skillDir: resolve(input.skillDir) })\n }\n\n return invalid('Skill run tag is invalid.')\n}\n\nexport function parseOutputPolicy(policy: SkillOutputPolicy): SkillOutputPolicy {\n for (const [name, value] of Object.entries(policy)) {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new TypeError(`${name} must be a positive integer.`)\n }\n return Object.freeze(policy)\n}\n","import type { SkillOutputPolicy, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { HarnessAgentSandboxConfig } from '@ai-sdk/harness/agent'\nimport { normalizeOutputPath } from '../paths.ts'\nimport { err, ok } from '../result.ts'\n\nexport type SandboxSession = Parameters<NonNullable<HarnessAgentSandboxConfig['onSession']>>[0]['session']\n\nexport interface CollectedFile {\n readonly path: string\n readonly content: Uint8Array\n}\n\nconst invalid = (message: string, issues: ReadonlyArray<string>): Result<never, SkillRunError> =>\n err({ _tag: 'InvalidSkill', message, issues })\n\nconst inventory = async (\n sandbox: SandboxSession,\n outputDir: string,\n signal?: AbortSignal,\n): Promise<Result<ReadonlyArray<{ type: string, path: string, size: number }>, SkillRunError>> => {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n const command = 'find -P \"$SKILLD_OUTPUT\" -mindepth 1 -printf \\'%y\\\\0%P\\\\0%s\\\\0\\''\n const result = await Promise.resolve().then(() => sandbox.run({\n command,\n env: { LC_ALL: 'C', SKILLD_OUTPUT: outputDir },\n abortSignal: signal,\n })).catch(error => error as Error)\n if (result instanceof Error)\n return invalid('Harness output cannot be inspected.', [result.message])\n if (result.exitCode !== 0)\n return invalid('Harness output directory is missing.', [result.stderr.trim() || 'No output directory.'])\n\n const fields = result.stdout.split('\\0')\n if (fields.at(-1) === '')\n fields.pop()\n if (fields.length % 3 !== 0)\n return invalid('Harness output inventory is invalid.', ['File inventory has an incomplete entry.'])\n\n const entries: Array<{ type: string, path: string, size: number }> = []\n for (let index = 0; index < fields.length; index += 3) {\n const type = fields[index]\n const path = fields[index + 1]\n const size = Number(fields[index + 2])\n if (!type || !path || !Number.isSafeInteger(size) || size < 0)\n return invalid('Harness output inventory is invalid.', ['File inventory has invalid fields.'])\n entries.push({ type, path, size })\n }\n return ok(entries)\n}\n\nexport const collectSandboxOutput = async (\n sandbox: SandboxSession,\n outputDir: string,\n policy: SkillOutputPolicy,\n signal?: AbortSignal,\n): Promise<Result<ReadonlyArray<CollectedFile>, SkillRunError>> => {\n const before = await inventory(sandbox, outputDir, signal)\n if (before._tag === 'Err')\n return before\n\n const files: CollectedFile[] = []\n const seen = new Set<string>()\n let totalBytes = 0\n for (const entry of before.value) {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n const path = normalizeOutputPath(entry.path)\n if (path === null)\n return invalid('Harness output contains an invalid path.', [entry.path])\n if (entry.type === 'd')\n continue\n if (entry.type !== 'f')\n return invalid('Harness output contains a linked or special entry.', [path])\n if (seen.has(path))\n return invalid('Harness output contains duplicate paths.', [path])\n if (files.length >= policy.maxOutputFiles)\n return invalid('Harness output contains too many files.', [`Limit: ${policy.maxOutputFiles}`])\n if (entry.size > policy.maxOutputFileBytes)\n return invalid('Harness output contains an oversized file.', [path])\n if (totalBytes + entry.size > policy.maxOutputBytes)\n return invalid('Harness output exceeds the total byte limit.', [`Limit: ${policy.maxOutputBytes}`])\n\n const content = await Promise.resolve().then(() => sandbox.readBinaryFile({ path: `${outputDir}/${path}`, abortSignal: signal })).catch(error => error as Error)\n if (content instanceof Error) {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n return invalid('Harness output cannot be read.', [path, content.message])\n }\n if (content === null || content.byteLength !== entry.size)\n return invalid('Harness output changed during collection.', [path])\n seen.add(path)\n files.push({ path, content: Uint8Array.from(content) })\n totalBytes += content.byteLength\n }\n\n const after = await inventory(sandbox, outputDir, signal)\n if (after._tag === 'Err')\n return after\n if (JSON.stringify(before.value) !== JSON.stringify(after.value))\n return invalid('Harness output changed during collection.', ['File inventory changed.'])\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok(files)\n}\n","import type { GeneratedSkill, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { CollectedFile } from './collect.ts'\nimport { randomUUID } from 'node:crypto'\nimport { lstat, mkdir, open, realpath, rename, rm, rmdir, unlink, writeFile } from 'node:fs/promises'\nimport { dirname, join, resolve } from 'node:path'\nimport { err, ok } from '../result.ts'\n\nfunction pathError(message: string, path: string): Result<never, SkillRunError> {\n return err({ _tag: 'UnsafeOutputPath', message, path })\n}\n\nconst missing = Symbol('missing')\n\nasync function statOrMissing(path: string) {\n return lstat(path).catch(error => (error as NodeJS.ErrnoException).code === 'ENOENT' ? missing : Promise.reject(error))\n}\n\nexport const promoteSkill = async (\n rootDir: string,\n name: string,\n files: ReadonlyArray<CollectedFile>,\n attempts: ReadonlyArray<SourceAttempt>,\n): Promise<Result<GeneratedSkill, SkillRunError>> => {\n const root = resolve(rootDir)\n let rootStat = await statOrMissing(root).catch(error => error as Error)\n if (rootStat instanceof Error)\n return pathError('Output root cannot be inspected.', root)\n let createdRoot = false\n if (rootStat === missing) {\n const parent = dirname(root)\n const parentStat = await statOrMissing(parent).catch(error => error as Error)\n if (parentStat instanceof Error || parentStat === missing || !parentStat.isDirectory() || parentStat.isSymbolicLink())\n return pathError('Output root parent must be an existing directory.', parent)\n const canonicalParent = await realpath(parent).catch(error => error as Error)\n if (canonicalParent instanceof Error || canonicalParent !== parent)\n return pathError('Output root parent must not pass through a symbolic link.', parent)\n const created = await mkdir(root, { mode: 0o700 }).then(() => true, error => error as NodeJS.ErrnoException)\n if (created instanceof Error && created.code !== 'EEXIST')\n return pathError('Output root cannot be created.', root)\n createdRoot = created === true\n rootStat = await statOrMissing(root).catch(error => error as Error)\n }\n if (rootStat instanceof Error || rootStat === missing || !rootStat.isDirectory() || rootStat.isSymbolicLink())\n return pathError('Output root must be a directory, not a symbolic link.', root)\n const canonicalRoot = await realpath(root).catch(error => error as Error)\n if (canonicalRoot instanceof Error)\n return pathError('Output root cannot be resolved.', root)\n if (canonicalRoot !== root)\n return pathError('Output root must not pass through a symbolic link.', root)\n\n const target = join(root, name)\n const targetStat = await statOrMissing(target).catch(error => error as Error)\n if (targetStat instanceof Error)\n return pathError('Output path cannot be inspected.', target)\n if (targetStat !== missing && (!targetStat.isDirectory() || targetStat.isSymbolicLink()))\n return pathError('Output path must be a directory, not a symbolic link.', target)\n\n const lockPath = join(root, `.skilld-${name}.lock`)\n const lock = await open(lockPath, 'wx').catch(error => error as NodeJS.ErrnoException)\n if (lock instanceof Error) {\n if (lock.code === 'EEXIST')\n return err({ _tag: 'OutputBusy', message: 'Another Skill run owns this output path.', path: target })\n return err({ _tag: 'PromotionFailed', message: 'Output lock could not be created.', path: target, cause: lock })\n }\n\n const nonce = randomUUID()\n const staging = join(root, `.skilld-${name}-${nonce}.next`)\n const backup = join(root, `.skilld-${name}-${nonce}.previous`)\n let movedCurrent = false\n let promoted = false\n let failureMessage = 'Skill output could not be promoted.'\n let failureCause: unknown\n\n try {\n await mkdir(staging, { mode: 0o700 })\n for (const file of files) {\n const destination = join(staging, file.path)\n await mkdir(dirname(destination), { recursive: true, mode: 0o700 })\n await writeFile(destination, file.content, { flag: 'wx', mode: 0o600 })\n }\n\n if (targetStat !== missing) {\n await rename(target, backup)\n movedCurrent = true\n }\n await rename(staging, target)\n promoted = true\n }\n catch (cause) {\n failureCause = cause\n if (movedCurrent && !promoted) {\n const current = await statOrMissing(target).catch(error => error as Error)\n if (current instanceof Error) {\n failureMessage = 'Skill output rollback could not inspect its destination.'\n failureCause = new AggregateError([cause, current])\n }\n if (current === missing) {\n const rollback = await rename(backup, target).catch(error => error as Error)\n if (rollback instanceof Error) {\n failureMessage = 'Skill output rollback failed.'\n failureCause = new AggregateError([cause, rollback])\n }\n }\n }\n }\n\n const lockCleanupErrors: unknown[] = []\n await lock.close().catch(error => lockCleanupErrors.push(error))\n await unlink(lockPath).catch(error => lockCleanupErrors.push(error))\n\n if (promoted) {\n const warnings: string[] = []\n if (lockCleanupErrors.length > 0) {\n const detail = lockCleanupErrors.map(error => error instanceof Error ? error.message : String(error)).join('; ')\n warnings.push(`Output lock cleanup failed at ${lockPath}: ${detail}`)\n }\n if (movedCurrent) {\n const backupCleanup = await rm(backup, { recursive: true }).catch(error => error as Error)\n if (backupCleanup instanceof Error)\n warnings.push(`Previous Skill cleanup failed at ${backup}: ${backupCleanup.message}`)\n }\n return ok({\n _tag: 'GeneratedSkill',\n name,\n outputDir: target,\n files: files.map(file => ({ path: file.path, bytes: file.content.byteLength })),\n sourceAttempts: attempts,\n warnings,\n })\n }\n\n const cleanupErrors = [...lockCleanupErrors]\n await rm(staging, { recursive: true, force: true }).catch(error => cleanupErrors.push(error))\n if (createdRoot)\n await rmdir(root).catch(error => cleanupErrors.push(error))\n const causes = failureCause === undefined ? cleanupErrors : [failureCause, ...cleanupErrors]\n const cause = causes.length <= 1 ? causes[0] : new AggregateError(causes, 'Skill output cleanup failed.')\n return err({ _tag: 'PromotionFailed', message: failureMessage, path: target, cause })\n}\n","import type { SkillReview, SkillReviewFinding, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { CollectedFile } from './collect.ts'\nimport { parseDocument } from 'yaml'\nimport { isSkillName, normalizeOutputPath } from '../paths.ts'\nimport { err, ok } from '../result.ts'\n\nconst allowedFrontmatter = new Set([\n 'name',\n 'description',\n 'license',\n 'compatibility',\n 'metadata',\n 'allowed-tools',\n])\n\nconst invalid = (issues: ReadonlyArray<string>): Result<never, SkillRunError> =>\n err({ _tag: 'InvalidSkill', message: 'Skill output failed deterministic checks.', issues })\n\nfunction decodeText(content: Uint8Array): string | null {\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(content)\n }\n catch {\n return null\n }\n}\n\nconst isStringMap = (value: unknown): boolean =>\n value !== null\n && typeof value === 'object'\n && !Array.isArray(value)\n && Object.values(value).every(item => typeof item === 'string')\n\nexport const validateGeneratedSkill = (\n name: string,\n files: ReadonlyArray<CollectedFile>,\n): Result<void, SkillRunError> => {\n const issues: string[] = []\n const skillFiles = files.filter(file => file.path === 'SKILL.md')\n if (skillFiles.length !== 1)\n return invalid(['Output must contain exactly one SKILL.md file.'])\n\n const source = decodeText(skillFiles[0]!.content)\n if (source === null)\n return invalid(['SKILL.md must contain valid UTF-8 text.'])\n const match = source.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n([\\s\\S]*)$/)\n if (!match)\n return invalid(['SKILL.md must start with YAML frontmatter.'])\n if (match[2]?.trim().length === 0)\n issues.push('SKILL.md instructions must not be empty.')\n\n const document = parseDocument(match[1]!, { uniqueKeys: true })\n for (const error of document.errors)\n issues.push(`Frontmatter: ${error.message}`)\n for (const warning of document.warnings)\n issues.push(`Frontmatter: ${warning.message}`)\n\n const value = document.errors.length === 0 ? document.toJS() as unknown : null\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n issues.push('Frontmatter must be a mapping.')\n return invalid(issues)\n }\n\n const frontmatter = value as Record<string, unknown>\n for (const key of Object.keys(frontmatter)) {\n if (!allowedFrontmatter.has(key))\n issues.push(`Frontmatter field is not supported: ${key}`)\n }\n if (frontmatter.name !== name)\n issues.push('Frontmatter name must match the Skill directory name.')\n if (typeof frontmatter.name !== 'string' || !isSkillName(frontmatter.name))\n issues.push('Frontmatter name is invalid.')\n if (typeof frontmatter.description !== 'string' || frontmatter.description.trim().length === 0 || frontmatter.description.length > 1024)\n issues.push('Frontmatter description must contain 1 to 1024 characters.')\n if (frontmatter.license !== undefined && typeof frontmatter.license !== 'string')\n issues.push('Frontmatter license must be a string.')\n if (frontmatter.compatibility !== undefined && (typeof frontmatter.compatibility !== 'string' || frontmatter.compatibility.length > 500))\n issues.push('Frontmatter compatibility must be a string of at most 500 characters.')\n if (frontmatter.metadata !== undefined && !isStringMap(frontmatter.metadata))\n issues.push('Frontmatter metadata must map strings to strings.')\n if (frontmatter['allowed-tools'] !== undefined && typeof frontmatter['allowed-tools'] !== 'string')\n issues.push('Frontmatter allowed-tools must be a string.')\n\n return issues.length === 0 ? ok(undefined) : invalid(issues)\n}\n\nconst findingLevels = new Set(['error', 'warning', 'note'])\n\nexport const validateSkillReview = (\n files: ReadonlyArray<CollectedFile>,\n): Result<SkillReview, SkillRunError> => {\n if (files.length !== 1 || files[0]?.path !== 'review.json')\n return invalid(['Review output must contain only review.json.'])\n\n let value: unknown\n try {\n const source = decodeText(files[0].content)\n if (source === null)\n return invalid(['review.json must contain valid UTF-8 text.'])\n value = JSON.parse(source)\n }\n catch {\n return invalid(['review.json must contain valid JSON.'])\n }\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return invalid(['review.json must contain an object.'])\n\n const review = value as Record<string, unknown>\n if (Object.keys(review).some(key => key !== 'summary' && key !== 'findings'))\n return invalid(['review.json contains unsupported fields.'])\n if (typeof review.summary !== 'string' || review.summary.trim().length === 0)\n return invalid(['Review summary is required.'])\n if (!Array.isArray(review.findings))\n return invalid(['Review findings must be an array.'])\n\n const findings: SkillReviewFinding[] = []\n for (const item of review.findings) {\n if (!item || typeof item !== 'object' || Array.isArray(item))\n return invalid(['Each review finding must be an object.'])\n const finding = item as Record<string, unknown>\n if (Object.keys(finding).some(key => !['level', 'path', 'message', 'fix'].includes(key)))\n return invalid(['A review finding contains unsupported fields.'])\n if (typeof finding.level !== 'string' || !findingLevels.has(finding.level))\n return invalid(['A review finding level is invalid.'])\n if (typeof finding.path !== 'string' || normalizeOutputPath(finding.path) === null)\n return invalid(['A review finding path is invalid.'])\n if (typeof finding.message !== 'string' || finding.message.trim().length === 0 || typeof finding.fix !== 'string' || finding.fix.trim().length === 0)\n return invalid(['A review finding is incomplete.'])\n findings.push(finding as unknown as SkillReviewFinding)\n }\n\n return ok({ _tag: 'SkillReview', summary: review.summary, findings })\n}\n","import type { SkillOutputPolicy, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport { constants } from 'node:fs'\nimport { lstat, open, opendir, realpath } from 'node:fs/promises'\nimport { join, relative, resolve, sep } from 'node:path'\nimport { err, ok } from '../result.ts'\n\nexport interface PreparedFile {\n readonly path: string\n readonly content: Uint8Array\n}\n\nexport interface PreparedSource {\n readonly files: ReadonlyArray<PreparedFile>\n readonly attempts: ReadonlyArray<SourceAttempt>\n readonly npmResolution?: {\n readonly package: string\n readonly version: string\n }\n}\n\nconst ignoredNames = new Set([\n '.git',\n '.hg',\n '.next',\n '.nuxt',\n '.output',\n '.skilld',\n '.turbo',\n 'coverage',\n 'dist',\n 'node_modules',\n 'target',\n])\n\nexport async function collectHostDirectory(directory: string, policy: SkillOutputPolicy, sourceLabel = directory, signal?: AbortSignal): Promise<Result<PreparedSource, SkillRunError>> {\n const root = resolve(directory)\n const unavailable = (message: string, cause?: unknown): Result<never, SkillRunError> => err({\n _tag: 'SourceUnavailable',\n message,\n attempts: [{ source: sourceLabel, status: 'skipped', reason: message }],\n cause,\n })\n const cancelled = (): Result<never, SkillRunError> => err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n if (signal?.aborted)\n return cancelled()\n const rootStat = await lstat(root).catch(error => error as NodeJS.ErrnoException)\n if (rootStat instanceof Error)\n return unavailable('Source directory is unavailable.', rootStat)\n if (!rootStat.isDirectory() || rootStat.isSymbolicLink())\n return unavailable('Source path must be a directory, not a symbolic link.')\n const canonicalRoot = await realpath(root).catch(error => error as NodeJS.ErrnoException)\n if (canonicalRoot instanceof Error)\n return unavailable('Source directory cannot be resolved.', canonicalRoot)\n if (canonicalRoot !== root)\n return unavailable('Source path must not pass through a symbolic link.')\n\n const files: PreparedFile[] = []\n let totalBytes = 0\n\n const walk = async (current: string): Promise<Result<void, SkillRunError>> => {\n if (signal?.aborted)\n return cancelled()\n const canonicalCurrent = await realpath(current).catch(error => error as NodeJS.ErrnoException)\n if (canonicalCurrent instanceof Error || canonicalCurrent !== current)\n return unavailable('Source directory changed during collection.', canonicalCurrent instanceof Error ? canonicalCurrent : undefined)\n const directoryHandle = await opendir(current).catch(error => error as NodeJS.ErrnoException)\n if (directoryHandle instanceof Error)\n return unavailable('Source directory cannot be read.', directoryHandle)\n\n for await (const entry of directoryHandle) {\n if (signal?.aborted)\n return cancelled()\n if (ignoredNames.has(entry.name))\n continue\n const absolute = join(current, entry.name)\n const stat = await lstat(absolute).catch(error => error as NodeJS.ErrnoException)\n if (stat instanceof Error)\n return unavailable('Source entry cannot be read.', stat)\n if (stat.isSymbolicLink())\n return unavailable('Source contains a symbolic link.')\n if (stat.isDirectory()) {\n const nested = await walk(absolute)\n if (nested._tag === 'Err')\n return nested\n continue\n }\n if (!stat.isFile())\n return unavailable('Source contains a special file.')\n\n const path = relative(root, absolute).split(sep).join('/')\n const handle = await open(absolute, constants.O_RDONLY | constants.O_NOFOLLOW).catch(error => error as NodeJS.ErrnoException)\n if (handle instanceof Error)\n return unavailable('Source file cannot be opened without following links.', handle)\n const openedStat = await handle.stat().catch(error => error as NodeJS.ErrnoException)\n if (openedStat instanceof Error) {\n await handle.close()\n return unavailable('Source file cannot be inspected.', openedStat)\n }\n if (!openedStat.isFile() || openedStat.dev !== stat.dev || openedStat.ino !== stat.ino) {\n await handle.close()\n return unavailable('Source file changed during collection.')\n }\n if (openedStat.size > policy.maxSourceFileBytes) {\n await handle.close()\n continue\n }\n if (files.length >= policy.maxSourceFiles) {\n await handle.close()\n return unavailable('Source contains too many files.')\n }\n if (totalBytes + openedStat.size > policy.maxSourceBytes) {\n await handle.close()\n return unavailable('Source exceeds the total byte limit.')\n }\n\n const content = await handle.readFile().catch(error => error as NodeJS.ErrnoException)\n await handle.close()\n if (content instanceof Error)\n return unavailable('Source file cannot be read.', content)\n if (content.byteLength !== openedStat.size)\n return unavailable('Source file changed during collection.')\n if (signal?.aborted)\n return cancelled()\n files.push({ path, content })\n totalBytes += content.byteLength\n }\n return ok(undefined)\n }\n\n const walked = await walk(root)\n if (walked._tag === 'Err')\n return walked\n if (files.length === 0)\n return unavailable('Source directory has no usable files.')\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok({ files, attempts: [{ source: sourceLabel, status: 'used' }] })\n}\n","import type { SkillOutputPolicy, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { PreparedFile } from './host.ts'\nimport { Readable } from 'node:stream'\nimport { createGunzip } from 'node:zlib'\nimport { normalizeOutputPath } from '../paths.ts'\nimport { err, ok } from '../result.ts'\n\nconst blockBytes = 512\n\nexport type ArchiveLayout = 'npm' | 'github'\n\ninterface PendingEntry {\n readonly kind: 'directory' | 'file' | 'metadata' | 'skip'\n readonly path?: string\n readonly metadataType?: 'long-path' | 'pax'\n readonly size: number\n remaining: number\n padding: number\n readonly chunks: Array<Buffer<ArrayBufferLike>>\n}\n\nfunction unavailable(message: string, cause?: unknown): Result<never, SkillRunError> {\n return err({ _tag: 'SourceUnavailable', message, attempts: [], cause })\n}\n\nfunction readString(buffer: Uint8Array, start: number, length: number): string {\n const bytes = buffer.subarray(start, start + length)\n const zero = bytes.indexOf(0)\n return Buffer.from(zero === -1 ? bytes : bytes.subarray(0, zero)).toString('utf8').trim()\n}\n\nfunction readOctal(buffer: Uint8Array, start: number, length: number): number | null {\n const value = readString(buffer, start, length).replace(/^\\s+|\\s+$/g, '')\n if (!/^[0-7]+$/.test(value))\n return null\n const parsed = Number.parseInt(value, 8)\n return Number.isSafeInteger(parsed) ? parsed : null\n}\n\nfunction hasValidChecksum(header: Uint8Array): boolean {\n const expected = readOctal(header, 148, 8)\n if (expected === null)\n return false\n let actual = 0\n for (let index = 0; index < header.byteLength; index += 1)\n actual += index >= 148 && index < 156 ? 32 : header[index]!\n return actual === expected\n}\n\nfunction parsePaxPath(content: Uint8Array): string | null {\n const text = Buffer.from(content).toString('utf8')\n let offset = 0\n let path: string | null = null\n while (offset < text.length) {\n const space = text.indexOf(' ', offset)\n if (space === -1)\n return null\n const length = Number.parseInt(text.slice(offset, space), 10)\n if (!Number.isSafeInteger(length) || length <= 0 || offset + length > text.length)\n return null\n const record = text.slice(space + 1, offset + length - 1)\n const equals = record.indexOf('=')\n if (equals !== -1 && record.slice(0, equals) === 'path')\n path = record.slice(equals + 1)\n offset += length\n }\n return path\n}\n\nfunction archivePath(value: string, layout: ArchiveLayout): string | null {\n const normalized = normalizeOutputPath(value)\n if (normalized === null)\n return null\n const segments = normalized.split('/')\n if (layout === 'npm') {\n if (segments[0] !== 'package' || segments.length < 2)\n return null\n }\n else if (segments.length < 2) {\n return null\n }\n return normalizeOutputPath(segments.slice(1).join('/'))\n}\n\nfunction entryContent(entry: PendingEntry): Buffer<ArrayBufferLike> {\n return entry.chunks.length === 1\n ? entry.chunks[0]!\n : Buffer.concat(entry.chunks, entry.size)\n}\n\nexport async function extractArchive(\n compressed: AsyncIterable<Uint8Array>,\n policy: SkillOutputPolicy,\n layout: ArchiveLayout,\n): Promise<Result<ReadonlyArray<PreparedFile>, SkillRunError>> {\n const source = Readable.from(compressed)\n const gunzip = createGunzip()\n source.on('error', error => gunzip.destroy(error))\n source.pipe(gunzip)\n const files: PreparedFile[] = []\n const seen = new Set<string>()\n const maxArchiveBytes = policy.maxSourceBytes + (policy.maxSourceFiles + 2) * blockBytes * 2\n let archiveBytes = 0\n let fileEntries = 0\n let nextPath: string | null = null\n let pending: Buffer<ArrayBufferLike> = Buffer.alloc(0)\n let entry: PendingEntry | null = null\n let totalBytes = 0\n let ended = false\n\n try {\n for await (const value of gunzip) {\n const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value as Uint8Array)\n archiveBytes += chunk.byteLength\n if (archiveBytes > maxArchiveBytes)\n return unavailable('Source archive exceeds the uncompressed byte limit.')\n pending = pending.byteLength === 0 ? chunk : Buffer.concat([pending, chunk])\n\n while (pending.byteLength > 0) {\n if (ended) {\n if (pending.some(byte => byte !== 0))\n return unavailable('Source archive has data after its end marker.')\n pending = Buffer.alloc(0)\n break\n }\n\n if (entry !== null) {\n if (entry.remaining > 0) {\n const consumed = Math.min(entry.remaining, pending.byteLength)\n if (entry.kind === 'file' || entry.kind === 'metadata')\n entry.chunks.push(pending.subarray(0, consumed))\n pending = pending.subarray(consumed)\n entry.remaining -= consumed\n if (entry.remaining > 0)\n break\n }\n if (entry.padding > 0) {\n const consumed = Math.min(entry.padding, pending.byteLength)\n pending = pending.subarray(consumed)\n entry.padding -= consumed\n if (entry.padding > 0)\n break\n }\n\n if (entry.kind === 'metadata') {\n const content = entryContent(entry)\n nextPath = entry.metadataType === 'pax'\n ? parsePaxPath(content)\n : content.toString('utf8').replace(/\\0.*$/s, '').trim()\n if (nextPath === null || nextPath.length === 0)\n return unavailable('Source archive has invalid path metadata.')\n }\n else if (entry.kind === 'file' && entry.path) {\n files.push({ path: entry.path, content: Uint8Array.from(entryContent(entry)) })\n totalBytes += entry.size\n }\n entry = null\n continue\n }\n\n if (pending.byteLength < blockBytes)\n break\n const header = pending.subarray(0, blockBytes)\n pending = pending.subarray(blockBytes)\n if (header.every(byte => byte === 0)) {\n ended = true\n continue\n }\n if (!hasValidChecksum(header))\n return unavailable('Source archive has an invalid header checksum.')\n\n const size = readOctal(header, 124, 12)\n if (size === null)\n return unavailable('Source archive has an invalid entry size.')\n const name = readString(header, 0, 100)\n const prefix = readString(header, 345, 155)\n const headerPath = prefix.length > 0 ? `${prefix}/${name}` : name\n const type = String.fromCharCode(header[156] ?? 0)\n const padding = (blockBytes - size % blockBytes) % blockBytes\n\n if (type === 'x' || type === 'L') {\n if (size > policy.maxSourceFileBytes)\n return unavailable('Source archive path metadata exceeds the file byte limit.')\n entry = {\n kind: 'metadata',\n metadataType: type === 'x' ? 'pax' : 'long-path',\n size,\n remaining: size,\n padding,\n chunks: [],\n }\n continue\n }\n if (type === '5') {\n nextPath = null\n entry = { kind: 'directory', size, remaining: size, padding, chunks: [] }\n continue\n }\n if (type !== '0' && type !== '\\0')\n return unavailable('Source archive contains a linked or special entry.')\n\n const path = archivePath(nextPath ?? headerPath, layout)\n nextPath = null\n if (path === null)\n return unavailable('Source archive contains an invalid path.')\n if (seen.has(path))\n return unavailable('Source archive contains duplicate paths.')\n seen.add(path)\n fileEntries += 1\n if (fileEntries > policy.maxSourceFiles)\n return unavailable('Source archive contains too many files.')\n if (size <= policy.maxSourceFileBytes && totalBytes + size > policy.maxSourceBytes)\n return unavailable('Source archive exceeds the total byte limit.')\n\n entry = {\n kind: size > policy.maxSourceFileBytes ? 'skip' : 'file',\n path,\n size,\n remaining: size,\n padding,\n chunks: [],\n }\n }\n }\n }\n catch (cause) {\n const message = cause instanceof Error ? cause.message : ''\n return unavailable(message.startsWith('Source archive ')\n ? message\n : 'Source archive is not valid gzip data.', cause)\n }\n\n if (entry !== null || pending.some(byte => byte !== 0) || !ended)\n return unavailable('Source archive ended before its final entry.')\n if (files.length === 0)\n return unavailable('Source archive has no usable files.')\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok(files)\n}\n","import type { Hash } from 'node:crypto'\nimport type { SkillOutputPolicy, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { PreparedFile, PreparedSource } from './host.ts'\nimport { createHash } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { err, ok } from '../result.ts'\nimport { extractArchive } from './archive.ts'\n\ninterface NpmDist {\n readonly tarball: string\n readonly integrity?: string\n readonly shasum?: string\n}\n\ninterface NpmRepository {\n readonly type?: string\n readonly url?: string\n}\n\ninterface NpmVersion {\n readonly version: string\n readonly dist: NpmDist\n readonly gitHead?: string\n readonly repository?: NpmRepository | string\n}\n\ninterface IntegrityVerifier {\n readonly update: (content: Uint8Array) => void\n readonly valid: () => boolean\n}\n\nconst packageSpecPattern = /^(@[^/@]+\\/[^/@]+|[^/@]+)(?:@(\\S+))?$/\nconst exactVersionPattern = /^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[\\da-z-]+(?:\\.[\\da-z-]+)*)?(?:\\+[\\da-z-]+(?:\\.[\\da-z-]+)*)?$/i\nconst gitCommitPattern = /^[0-9a-f]{40}$/i\nconst npmRequestTimeoutMs = 30_000\nconst npmRedirectLimit = 3\nconst packumentByteLimit = 10 * 1024 * 1024\n\nexport type FetchClient = (input: string | URL | Request, init?: RequestInit) => Promise<Response>\n\nfunction unavailable(message: string, attempts: ReadonlyArray<SourceAttempt>, cause?: unknown): Result<never, SkillRunError> {\n return err({ _tag: 'SourceUnavailable', message, attempts, cause })\n}\n\nfunction parsePackageSpec(spec: string): { name: string, version: string } | null {\n const match = spec.match(packageSpecPattern)\n return match?.[1] ? { name: match[1], version: match[2] ?? 'latest' } : null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parseRepository(value: unknown): NpmRepository | string | undefined {\n if (typeof value === 'string')\n return value\n if (!isRecord(value))\n return undefined\n return {\n type: typeof value.type === 'string' ? value.type : undefined,\n url: typeof value.url === 'string' ? value.url : undefined,\n }\n}\n\nfunction parseVersion(packument: unknown, requested: string): { resolvedVersion: string, version: NpmVersion, repository?: NpmRepository | string } | null {\n if (!isRecord(packument) || !isRecord(packument['dist-tags']) || !isRecord(packument.versions))\n return null\n const tagged = packument['dist-tags'][requested]\n const resolvedVersion = typeof tagged === 'string' ? tagged : requested\n const value = packument.versions[resolvedVersion]\n if (!isRecord(value) || typeof value.version !== 'string' || !isRecord(value.dist) || typeof value.dist.tarball !== 'string')\n return null\n return {\n resolvedVersion,\n repository: parseRepository(packument.repository),\n version: {\n version: value.version,\n gitHead: typeof value.gitHead === 'string' ? value.gitHead : undefined,\n repository: parseRepository(value.repository),\n dist: {\n tarball: value.dist.tarball,\n integrity: typeof value.dist.integrity === 'string' ? value.dist.integrity : undefined,\n shasum: typeof value.dist.shasum === 'string' ? value.dist.shasum : undefined,\n },\n },\n }\n}\n\nasync function cancelBody(response: Response): Promise<void> {\n if (!response.body)\n return\n // A failed cleanup cannot replace the source failure.\n await response.body.cancel().catch(() => undefined)\n}\n\nfunction declaredBytes(response: Response): number | null {\n const value = response.headers.get('content-length')\n if (value === null || !/^\\d+$/.test(value))\n return null\n const bytes = Number(value)\n return Number.isSafeInteger(bytes) ? bytes : null\n}\n\nasync function* responseChunks(\n response: Response,\n maxBytes: number,\n limitMessage: string,\n onChunk: (content: Uint8Array) => void = () => {},\n): AsyncGenerator<Uint8Array> {\n const declared = declaredBytes(response)\n if (declared !== null && declared > maxBytes) {\n await cancelBody(response)\n throw new Error(limitMessage)\n }\n if (!response.body)\n return\n\n const reader = response.body.getReader()\n let complete = false\n let totalBytes = 0\n try {\n while (true) {\n const result = await reader.read()\n if (result.done) {\n complete = true\n return\n }\n totalBytes += result.value.byteLength\n if (totalBytes > maxBytes)\n throw new Error(limitMessage)\n onChunk(result.value)\n yield result.value\n }\n }\n finally {\n if (!complete) {\n // A failed cleanup cannot replace the source failure.\n await reader.cancel().catch(() => undefined)\n }\n reader.releaseLock()\n }\n}\n\nasync function readBoundedResponse(response: Response, maxBytes: number): Promise<Result<Uint8Array, SkillRunError>> {\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n try {\n for await (const chunk of responseChunks(response, maxBytes, 'npm response exceeds the byte limit.')) {\n chunks.push(chunk)\n totalBytes += chunk.byteLength\n }\n }\n catch (cause) {\n return unavailable(cause instanceof Error ? cause.message : 'npm response cannot be read.', [], cause)\n }\n return ok(Uint8Array.from(Buffer.concat(chunks, totalBytes)))\n}\n\nasync function fetchWithRedirects(fetchClient: FetchClient, url: string, init: RequestInit = {}, signal?: AbortSignal): Promise<Result<Response, SkillRunError>> {\n const timeout = AbortSignal.timeout(npmRequestTimeoutMs)\n const requestSignal = signal ? AbortSignal.any([signal, timeout]) : timeout\n let current = url\n for (let redirects = 0; redirects <= npmRedirectLimit; redirects += 1) {\n const response = await fetchClient(current, { ...init, redirect: 'manual', signal: requestSignal }).catch(error => error as Error)\n if (response instanceof Error)\n return unavailable(timeout.aborted ? 'npm request timed out.' : 'npm request failed.', [], response)\n if (![301, 302, 303, 307, 308].includes(response.status))\n return ok(response)\n if (redirects === npmRedirectLimit) {\n await cancelBody(response)\n return unavailable('npm request exceeded the redirect limit.', [])\n }\n const location = response.headers.get('location')\n if (!location) {\n await cancelBody(response)\n return unavailable('npm redirect is missing its location.', [])\n }\n const redirected = await Promise.resolve().then(() => new URL(location, current)).catch(() => null)\n if (redirected === null) {\n await cancelBody(response)\n return unavailable('npm redirect location is invalid.', [])\n }\n if (redirected.protocol !== 'https:') {\n await cancelBody(response)\n return unavailable('npm redirect URL must use HTTPS.', [])\n }\n await cancelBody(response)\n current = redirected.href\n }\n return unavailable('npm request exceeded the redirect limit.', [])\n}\n\nfunction createIntegrityVerifier(dist: NpmDist): IntegrityVerifier | null {\n const values = dist.integrity?.split(/\\s+/).filter(Boolean) ?? []\n let expected = values.flatMap((value) => {\n const match = value.match(/^(sha(?:1|256|384|512))-([A-Za-z0-9+/=]+)$/)\n return match?.[1] && match[2] ? [{ algorithm: match[1], digest: match[2] }] : []\n })\n if (expected.length > 0) {\n const strength = { sha1: 1, sha256: 2, sha384: 3, sha512: 4 } as const\n const strongest = Math.max(...expected.map(value => strength[value.algorithm as keyof typeof strength]))\n expected = expected.filter(value => strength[value.algorithm as keyof typeof strength] === strongest)\n }\n if (values.length === 0 && dist.shasum && /^[0-9a-f]{40}$/i.test(dist.shasum))\n expected.push({ algorithm: 'sha1', digest: Buffer.from(dist.shasum, 'hex').toString('base64') })\n if (expected.length === 0)\n return null\n\n const hashes = new Map<string, Hash>()\n for (const value of expected)\n hashes.set(value.algorithm, createHash(value.algorithm))\n return {\n update(content) {\n for (const hash of hashes.values())\n hash.update(content)\n },\n valid() {\n const actual = new Map([...hashes].map(([algorithm, hash]) => [algorithm, hash.digest('base64')]))\n return expected.some(value => actual.get(value.algorithm) === value.digest)\n },\n }\n}\n\nfunction repositoryUrl(repository: NpmRepository | string | undefined): string | null {\n if (typeof repository === 'string')\n return repository\n return typeof repository?.url === 'string' ? repository.url : null\n}\n\nfunction githubArchiveUrl(repository: NpmRepository | string | undefined, gitHead: string | undefined): string | null {\n if (!gitHead || !gitCommitPattern.test(gitHead))\n return null\n const source = repositoryUrl(repository)\n if (source === null)\n return null\n const match = source.match(/^(?:(?:git\\+)?https?:\\/\\/(?:git@)?github\\.com\\/|git:\\/\\/github\\.com\\/|git@github\\.com:)([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?\\/?$/)\n if (!match?.[1] || !match[2])\n return null\n return `https://codeload.github.com/${encodeURIComponent(match[1])}/${encodeURIComponent(match[2])}/tar.gz/${gitHead}`\n}\n\nfunction skipped(source: string, reason: string): SourceAttempt {\n return { source, status: 'skipped', reason }\n}\n\nfunction used(source: string): SourceAttempt {\n return { source, status: 'used' }\n}\n\nfunction withAttempts(error: SkillRunError, attempts: ReadonlyArray<SourceAttempt>): Result<never, SkillRunError> {\n return error._tag === 'SourceUnavailable'\n ? unavailable(error.message, attempts, error.cause)\n : err(error)\n}\n\nfunction remainingPolicy(policy: SkillOutputPolicy, files: ReadonlyArray<PreparedFile>): SkillOutputPolicy | null {\n const usedBytes = files.reduce((total, file) => total + file.content.byteLength, 0)\n const maxSourceFiles = policy.maxSourceFiles - files.length\n const maxSourceBytes = policy.maxSourceBytes - usedBytes\n if (maxSourceFiles <= 0 || maxSourceBytes <= 0)\n return null\n return { ...policy, maxSourceFiles, maxSourceBytes }\n}\n\nasync function extractResponse(\n response: Response,\n policy: SkillOutputPolicy,\n layout: 'github' | 'npm',\n verifier?: IntegrityVerifier,\n): Promise<Result<ReadonlyArray<PreparedFile>, SkillRunError>> {\n return extractArchive(\n responseChunks(\n response,\n policy.maxSourceBytes,\n 'Source archive exceeds the compressed byte limit.',\n content => verifier?.update(content),\n ),\n policy,\n layout,\n )\n}\n\nexport async function prepareNpmPackage(spec: string, policy: SkillOutputPolicy, fetchClient: FetchClient, signal?: AbortSignal): Promise<Result<PreparedSource, SkillRunError>> {\n const parsed = parsePackageSpec(spec)\n if (parsed === null)\n return unavailable('npm package spec is invalid.', [])\n\n const attempts: SourceAttempt[] = []\n const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(parsed.name)}`\n const registryResponse = await fetchWithRedirects(fetchClient, registryUrl, {\n headers: { accept: 'application/vnd.npm.install-v1+json' },\n }, signal)\n if (registryResponse._tag === 'Err')\n return withAttempts(registryResponse.error, [skipped(registryUrl, registryResponse.error.message)])\n const response = registryResponse.value\n if (!response.ok) {\n await cancelBody(response)\n const message = `npm registry returned HTTP ${response.status}.`\n return unavailable(message, [skipped(registryUrl, message)])\n }\n\n const packumentBytes = await readBoundedResponse(response, packumentByteLimit)\n if (packumentBytes._tag === 'Err')\n return withAttempts(packumentBytes.error, [skipped(registryUrl, packumentBytes.error.message)])\n\n let packument: unknown\n try {\n packument = JSON.parse(Buffer.from(packumentBytes.value).toString('utf8')) as unknown\n }\n catch (cause) {\n const message = 'npm registry returned invalid package data.'\n return unavailable(message, [skipped(registryUrl, message)], cause)\n }\n\n const resolution = parseVersion(packument, parsed.version)\n if (resolution === null || !exactVersionPattern.test(resolution.resolvedVersion) || resolution.version.version !== resolution.resolvedVersion) {\n const message = 'npm package did not resolve to an exact version.'\n return unavailable(message, [skipped(registryUrl, message)])\n }\n const { repository, resolvedVersion, version } = resolution\n if (!version.dist.tarball.startsWith('https://')) {\n const message = 'npm package archive URL must use HTTPS.'\n return unavailable(message, [skipped(registryUrl, message)])\n }\n attempts.push(used(registryUrl))\n\n const fetchedArchive = await fetchWithRedirects(fetchClient, version.dist.tarball, {}, signal)\n if (fetchedArchive._tag === 'Err')\n return withAttempts(fetchedArchive.error, [...attempts, skipped(version.dist.tarball, fetchedArchive.error.message)])\n const archiveResponse = fetchedArchive.value\n if (!archiveResponse.ok) {\n await cancelBody(archiveResponse)\n const message = `npm package archive returned HTTP ${archiveResponse.status}.`\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n\n const verifier = createIntegrityVerifier(version.dist)\n if (verifier === null) {\n await cancelBody(archiveResponse)\n const message = 'npm package archive has no supported integrity value.'\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n const extracted = await extractResponse(archiveResponse, policy, 'npm', verifier)\n if (extracted._tag === 'Err')\n return withAttempts(extracted.error, [...attempts, skipped(version.dist.tarball, extracted.error.message)])\n if (!verifier.valid()) {\n const message = 'npm package archive integrity check failed.'\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n attempts.push(used(version.dist.tarball))\n let files = [...extracted.value]\n\n const repositoryArchive = githubArchiveUrl(version.repository ?? repository, version.gitHead)\n const available = remainingPolicy(policy, files)\n if (repositoryArchive !== null && available === null) {\n attempts.push(skipped(repositoryArchive, 'Prepared package files reached the source limit.'))\n }\n else if (repositoryArchive !== null && available !== null) {\n const fetchedRepository = await fetchWithRedirects(fetchClient, repositoryArchive, {}, signal)\n if (fetchedRepository._tag === 'Err') {\n attempts.push(skipped(repositoryArchive, fetchedRepository.error.message))\n }\n else if (!fetchedRepository.value.ok) {\n const message = `GitHub source archive returned HTTP ${fetchedRepository.value.status}.`\n await cancelBody(fetchedRepository.value)\n attempts.push(skipped(repositoryArchive, message))\n }\n else {\n const repositoryFiles = await extractResponse(fetchedRepository.value, available, 'github')\n if (repositoryFiles._tag === 'Err') {\n attempts.push(skipped(repositoryArchive, repositoryFiles.error.message))\n }\n else {\n files = [\n ...files,\n ...repositoryFiles.value.map(file => ({ ...file, path: posix.join('repository', file.path) })),\n ]\n attempts.push(used(repositoryArchive))\n }\n }\n }\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok({\n files,\n attempts,\n npmResolution: { package: parsed.name, version: resolvedVersion },\n })\n}\n","import type { HarnessV1Skill } from '@ai-sdk/harness'\nimport type { SandboxSession } from './internal/output/collect.ts'\nimport type { Result } from './internal/result.ts'\nimport type { PreparedSource } from './internal/source/host.ts'\nimport type { FetchClient } from './internal/source/npm.ts'\nimport type {\n CreateSkillHarnessOptions,\n SkillDestination,\n SkillHarness,\n SkillOutputPolicy,\n SkillRun,\n SkillRunError,\n SkillRunResult,\n} from './types.ts'\nimport { lstat, realpath } from 'node:fs/promises'\nimport { join, posix, resolve } from 'node:path'\nimport { HarnessAgent } from '@ai-sdk/harness/agent'\nimport { parseOutputPolicy, parseSkillRun } from './internal/input.ts'\nimport { collectSandboxOutput } from './internal/output/collect.ts'\nimport { promoteSkill } from './internal/output/promote.ts'\nimport { validateGeneratedSkill, validateSkillReview } from './internal/output/validate.ts'\nimport { resolveWithin } from './internal/paths.ts'\nimport { err, ok } from './internal/result.ts'\nimport { collectHostDirectory } from './internal/source/host.ts'\nimport { prepareNpmPackage } from './internal/source/npm.ts'\nimport { DEFAULT_OUTPUT_POLICY, loadSkilldMaintainedSkill } from './skills.ts'\n\ninterface PreparedRun {\n readonly skillName: 'generate-package-skill' | 'generate-project-skill' | 'review-skill'\n readonly outputName: string\n readonly source: PreparedSource\n readonly current?: PreparedSource\n readonly destination?: SkillDestination\n}\n\ninterface ActiveSandbox {\n readonly sandbox: SandboxSession\n readonly workDir: string\n}\n\nfunction cancelled(): SkillRunResult {\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n}\n\nasync function prepareCurrentSkill(destination: SkillDestination, policy: SkillOutputPolicy, signal?: AbortSignal): Promise<Result<PreparedSource | undefined, SkillRunError>> {\n const root = resolve(destination.rootDir)\n const rootStat = await lstat(root).catch(error => error as NodeJS.ErrnoException)\n if (rootStat instanceof Error) {\n if (rootStat.code === 'ENOENT')\n return ok(undefined)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root cannot be inspected.', path: root })\n }\n if (!rootStat.isDirectory() || rootStat.isSymbolicLink())\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root must be a directory, not a symbolic link.', path: root })\n const canonicalRoot = await realpath(root).catch(error => error as Error)\n if (canonicalRoot instanceof Error || canonicalRoot !== root)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root must not pass through a symbolic link.', path: root })\n\n const target = join(root, destination.name)\n const targetStat = await lstat(target).catch(error => error as NodeJS.ErrnoException)\n if (targetStat instanceof Error) {\n if (targetStat.code === 'ENOENT')\n return ok(undefined)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output path cannot be inspected.', path: target })\n }\n if (!targetStat.isDirectory() || targetStat.isSymbolicLink())\n return err({ _tag: 'UnsafeOutputPath', message: 'Output path must be a directory, not a symbolic link.', path: target })\n return collectHostDirectory(target, policy, 'current Skill', signal)\n}\n\nasync function prepareRun(input: SkillRun, policy: SkillOutputPolicy, fetchClient: FetchClient, signal?: AbortSignal): Promise<Result<PreparedRun, SkillRunError>> {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n\n const current = input._tag === 'ReviewSkill'\n ? ok(undefined)\n : await prepareCurrentSkill(input.destination, policy, signal)\n if (current._tag === 'Err')\n return current\n\n if (input._tag === 'ProjectSkill') {\n const source = await collectHostDirectory(input.projectDir, policy, input.projectDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-project-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n }\n\n if (input._tag === 'ReviewSkill') {\n const source = await collectHostDirectory(input.skillDir, policy, input.skillDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'review-skill', outputName: 'review', source: source.value })\n }\n\n if (input.source._tag === 'NpmPackage') {\n const source = await prepareNpmPackage(input.source.spec, policy, fetchClient, signal)\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-package-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n }\n\n const packageDir = resolveWithin(input.source.rootDir, input.source.packageDir)\n if (packageDir === null)\n return err({ _tag: 'InvalidInput', message: 'Local package directory must stay inside its root directory.' })\n const manifest = await lstat(join(packageDir, 'package.json')).catch(error => error as NodeJS.ErrnoException)\n if (manifest instanceof Error || !manifest.isFile() || manifest.isSymbolicLink()) {\n return err({\n _tag: 'SourceUnavailable',\n message: 'Local package directory must contain package.json.',\n attempts: [{ source: packageDir, status: 'skipped', reason: 'package.json is unavailable.' }],\n })\n }\n\n const source = await collectHostDirectory(packageDir, policy, packageDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-package-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n}\n\nfunction requestContent(skill: HarnessV1Skill): string {\n const request = skill.files?.find(file => file.path === 'assets/harness-request.md')\n if (!request)\n throw new Error(`Harness request asset is missing for ${skill.name}.`)\n return request.content\n}\n\nfunction renderRequest(template: string, sourcePath: string, currentSkillPath: string, outputPath: string, skillName: string): string {\n return template\n .replaceAll('{{SOURCE_PATH}}', sourcePath)\n .replaceAll('{{CURRENT_SKILL_PATH}}', currentSkillPath)\n .replaceAll('{{OUTPUT_PATH}}', outputPath)\n .replaceAll('{{SKILL_NAME}}', skillName)\n}\n\nasync function writePreparedSource(active: ActiveSandbox, prepared: PreparedRun, signal?: AbortSignal): Promise<void> {\n const sourcePath = posix.join(active.workDir, 'input/source')\n const reset = await active.sandbox.run({\n command: 'rm -rf -- \"$SKILLD_INPUT\" \"$SKILLD_OUTPUT\" && mkdir -p -- \"$SKILLD_INPUT\"',\n env: {\n SKILLD_INPUT: posix.join(active.workDir, 'input'),\n SKILLD_OUTPUT: posix.join(active.workDir, 'skilld-output'),\n },\n abortSignal: signal,\n })\n if (reset.exitCode !== 0)\n throw new Error(reset.stderr.trim() || 'Harness work directory cannot be prepared.')\n for (const file of prepared.source.files) {\n await active.sandbox.writeBinaryFile({\n path: posix.join(sourcePath, file.path),\n content: file.content,\n abortSignal: signal,\n })\n }\n for (const file of prepared.current?.files ?? []) {\n await active.sandbox.writeBinaryFile({\n path: posix.join(active.workDir, 'input/current-skill', file.path),\n content: file.content,\n abortSignal: signal,\n })\n }\n await active.sandbox.writeTextFile({\n path: posix.join(active.workDir, 'input/source-manifest.json'),\n content: `${JSON.stringify({\n sourceAttempts: prepared.source.attempts,\n npmResolution: prepared.source.npmResolution,\n hasCurrentSkill: prepared.current !== undefined,\n }, null, 2)}\\n`,\n abortSignal: signal,\n })\n}\n\nfunction toAgentError(cause: unknown, signal?: AbortSignal): SkillRunResult {\n return signal?.aborted\n ? cancelled()\n : err({ _tag: 'AgentFailed', message: 'Harness Agent failed during the Skill run.', cause })\n}\n\nexport function createSkillHarness(options: CreateSkillHarnessOptions): SkillHarness {\n const policy = parseOutputPolicy({ ...DEFAULT_OUTPUT_POLICY, ...options.outputPolicy })\n const fetchClient: FetchClient = options.fetch ?? globalThis.fetch.bind(globalThis)\n const sandboxConfig = { ...options.sandboxConfig }\n const harness = options.harness\n const sandbox = options.sandbox\n\n return {\n run: async (input, runOptions = {}): Promise<SkillRunResult> => {\n const parsed = parseSkillRun(input)\n if (parsed._tag === 'Err')\n return parsed\n\n const prepared = await prepareRun(parsed.value, policy, fetchClient, runOptions.signal)\n if (prepared._tag === 'Err')\n return prepared\n\n const skill = await loadSkilldMaintainedSkill(prepared.value.skillName)\n let active: ActiveSandbox | undefined\n const userOnSession = sandboxConfig.onSession\n const agent = new HarnessAgent({\n harness,\n sandbox,\n skills: [skill],\n permissionMode: 'allow-all',\n sandboxConfig: {\n ...sandboxConfig,\n onSession: async (sessionOptions) => {\n active = { sandbox: sessionOptions.session, workDir: sessionOptions.sessionWorkDir }\n await writePreparedSource(active, prepared.value, runOptions.signal)\n await userOnSession?.(sessionOptions)\n },\n },\n })\n\n const sessionResult = await agent.createSession({ abortSignal: runOptions.signal }).then(ok, cause => err(cause))\n if (sessionResult._tag === 'Err')\n return toAgentError(sessionResult.error, runOptions.signal)\n const session = sessionResult.value\n\n try {\n if (!active)\n return err({ _tag: 'AgentFailed', message: 'Harness did not provide its sandbox session.' })\n const sourcePath = posix.join(active.workDir, 'input/source')\n const currentSkillPath = posix.join(active.workDir, 'input/current-skill')\n const outputPath = posix.join(active.workDir, 'skilld-output', prepared.value.outputName)\n const prompt = renderRequest(requestContent(skill), sourcePath, currentSkillPath, outputPath, prepared.value.outputName)\n const generated = await agent.generate({ session, prompt, abortSignal: runOptions.signal }).then(ok, cause => err(cause))\n if (generated._tag === 'Err')\n return toAgentError(generated.error, runOptions.signal)\n if (runOptions.signal?.aborted)\n return cancelled()\n\n const collected = await collectSandboxOutput(active.sandbox, outputPath, policy, runOptions.signal)\n if (collected._tag === 'Err')\n return collected\n\n if (prepared.value.skillName === 'review-skill')\n return validateSkillReview(collected.value)\n\n const validated = validateGeneratedSkill(prepared.value.outputName, collected.value)\n if (validated._tag === 'Err')\n return validated\n if (!prepared.value.destination)\n return err({ _tag: 'InvalidInput', message: 'Skill destination is required.' })\n return promoteSkill(\n prepared.value.destination.rootDir,\n prepared.value.destination.name,\n collected.value,\n prepared.value.source.attempts,\n )\n }\n finally {\n await session.destroy()\n }\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["invalid","invalid","unavailable"],"sources":["../src/internal/result.ts","../src/internal/input.ts","../src/internal/output/collect.ts","../src/internal/output/promote.ts","../src/internal/output/project.ts","../src/internal/output/validate.ts","../src/internal/source/host.ts","../src/internal/source/archive.ts","../src/internal/source/npm.ts","../src/harness.ts"],"sourcesContent":["export type Result<T, E>\n = | { readonly _tag: 'Ok', readonly value: T }\n | { readonly _tag: 'Err', readonly error: E }\n\nexport const ok = <T>(value: T): Result<T, never> => ({ _tag: 'Ok', value })\nexport const err = <E>(error: E): Result<never, E> => ({ _tag: 'Err', error })\n","import type { SkillOutputPolicy, SkillRun, SkillRunError } from '../types.ts'\nimport type { Result } from './result.ts'\nimport { resolve } from 'node:path'\nimport { isSkillName, resolveWithin } from './paths.ts'\nimport { err, ok } from './result.ts'\n\nfunction invalid(message: string): Result<never, SkillRunError> {\n return err({ _tag: 'InvalidInput', message })\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nexport function parseSkillRun(input: SkillRun): Result<SkillRun, SkillRunError> {\n if (!input || typeof input !== 'object' || !('_tag' in input))\n return invalid('Skill run input must be an object with a tag.')\n\n if (input._tag === 'PackageSkill') {\n if (!input.source || !input.destination)\n return invalid('Package Skill input is incomplete.')\n if (!isNonEmptyString(input.destination.rootDir) || !isSkillName(input.destination.name))\n return invalid('Package Skill destination is invalid.')\n if (input.source._tag === 'NpmPackage') {\n if (!isNonEmptyString(input.source.spec))\n return invalid('npm package spec is required.')\n return ok({\n _tag: 'PackageSkill',\n source: { _tag: 'NpmPackage', spec: input.source.spec },\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n if (input.source._tag === 'LocalPackage') {\n if (!isNonEmptyString(input.source.rootDir) || !isNonEmptyString(input.source.packageDir))\n return invalid('Local package paths are required.')\n if (resolveWithin(input.source.rootDir, input.source.packageDir) === null)\n return invalid('Local package directory must stay inside its root directory.')\n return ok({\n _tag: 'PackageSkill',\n source: {\n _tag: 'LocalPackage',\n rootDir: resolve(input.source.rootDir),\n packageDir: input.source.packageDir,\n },\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n return invalid('Package source tag is invalid.')\n }\n\n if (input._tag === 'ProjectSkill') {\n if (!isNonEmptyString(input.projectDir) || !input.destination)\n return invalid('Project Skill input is incomplete.')\n if (!isNonEmptyString(input.destination.rootDir) || !isSkillName(input.destination.name))\n return invalid('Project Skill destination is invalid.')\n return ok({\n _tag: 'ProjectSkill',\n projectDir: resolve(input.projectDir),\n destination: { rootDir: resolve(input.destination.rootDir), name: input.destination.name },\n })\n }\n\n if (input._tag === 'ReviewSkill') {\n if (!isNonEmptyString(input.skillDir))\n return invalid('Skill review directory is required.')\n return ok({ _tag: 'ReviewSkill', skillDir: resolve(input.skillDir) })\n }\n\n return invalid('Skill run tag is invalid.')\n}\n\nexport function parseOutputPolicy(policy: SkillOutputPolicy): SkillOutputPolicy {\n for (const [name, value] of Object.entries(policy)) {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new TypeError(`${name} must be a positive integer.`)\n }\n return Object.freeze(policy)\n}\n","import type { SkillOutputPolicy, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { HarnessAgentSandboxConfig } from '@ai-sdk/harness/agent'\nimport { normalizeOutputPath } from '../paths.ts'\nimport { err, ok } from '../result.ts'\n\nexport type SandboxSession = Parameters<NonNullable<HarnessAgentSandboxConfig['onSession']>>[0]['session']\n\nexport interface CollectedFile {\n readonly path: string\n readonly content: Uint8Array\n}\n\nconst invalid = (message: string, issues: ReadonlyArray<string>): Result<never, SkillRunError> =>\n err({ _tag: 'InvalidSkill', message, issues })\n\nconst inventory = async (\n sandbox: SandboxSession,\n outputDir: string,\n signal?: AbortSignal,\n): Promise<Result<ReadonlyArray<{ type: string, path: string, size: number }>, SkillRunError>> => {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n const command = 'find -P \"$SKILLD_OUTPUT\" -mindepth 1 -printf \\'%y\\\\0%P\\\\0%s\\\\0\\''\n const result = await Promise.resolve().then(() => sandbox.run({\n command,\n env: { LC_ALL: 'C', SKILLD_OUTPUT: outputDir },\n abortSignal: signal,\n })).catch(error => error as Error)\n if (result instanceof Error)\n return invalid('Harness output cannot be inspected.', [result.message])\n if (result.exitCode !== 0)\n return invalid('Harness output directory is missing.', [result.stderr.trim() || 'No output directory.'])\n\n const fields = result.stdout.split('\\0')\n if (fields.at(-1) === '')\n fields.pop()\n if (fields.length % 3 !== 0)\n return invalid('Harness output inventory is invalid.', ['File inventory has an incomplete entry.'])\n\n const entries: Array<{ type: string, path: string, size: number }> = []\n for (let index = 0; index < fields.length; index += 3) {\n const type = fields[index]\n const path = fields[index + 1]\n const size = Number(fields[index + 2])\n if (!type || !path || !Number.isSafeInteger(size) || size < 0)\n return invalid('Harness output inventory is invalid.', ['File inventory has invalid fields.'])\n entries.push({ type, path, size })\n }\n return ok(entries)\n}\n\nexport const collectSandboxOutput = async (\n sandbox: SandboxSession,\n outputDir: string,\n policy: SkillOutputPolicy,\n signal?: AbortSignal,\n): Promise<Result<ReadonlyArray<CollectedFile>, SkillRunError>> => {\n const before = await inventory(sandbox, outputDir, signal)\n if (before._tag === 'Err')\n return before\n\n const files: CollectedFile[] = []\n const seen = new Set<string>()\n let totalBytes = 0\n for (const entry of before.value) {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n const path = normalizeOutputPath(entry.path)\n if (path === null)\n return invalid('Harness output contains an invalid path.', [entry.path])\n if (entry.type === 'd')\n continue\n if (entry.type !== 'f')\n return invalid('Harness output contains a linked or special entry.', [path])\n if (seen.has(path))\n return invalid('Harness output contains duplicate paths.', [path])\n if (files.length >= policy.maxOutputFiles)\n return invalid('Harness output contains too many files.', [`Limit: ${policy.maxOutputFiles}`])\n if (entry.size > policy.maxOutputFileBytes)\n return invalid('Harness output contains an oversized file.', [path])\n if (totalBytes + entry.size > policy.maxOutputBytes)\n return invalid('Harness output exceeds the total byte limit.', [`Limit: ${policy.maxOutputBytes}`])\n\n const content = await Promise.resolve().then(() => sandbox.readBinaryFile({ path: `${outputDir}/${path}`, abortSignal: signal })).catch(error => error as Error)\n if (content instanceof Error) {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n return invalid('Harness output cannot be read.', [path, content.message])\n }\n if (content === null || content.byteLength !== entry.size)\n return invalid('Harness output changed during collection.', [path])\n seen.add(path)\n files.push({ path, content: Uint8Array.from(content) })\n totalBytes += content.byteLength\n }\n\n const after = await inventory(sandbox, outputDir, signal)\n if (after._tag === 'Err')\n return after\n if (JSON.stringify(before.value) !== JSON.stringify(after.value))\n return invalid('Harness output changed during collection.', ['File inventory changed.'])\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok(files)\n}\n","import type { GeneratedSkill, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { CollectedFile } from './collect.ts'\nimport { randomUUID } from 'node:crypto'\nimport { lstat, mkdir, open, realpath, rename, rm, rmdir, unlink, writeFile } from 'node:fs/promises'\nimport { dirname, join, resolve } from 'node:path'\nimport { err, ok } from '../result.ts'\n\nfunction pathError(message: string, path: string): Result<never, SkillRunError> {\n return err({ _tag: 'UnsafeOutputPath', message, path })\n}\n\nconst missing = Symbol('missing')\n\nasync function statOrMissing(path: string) {\n return lstat(path).catch(error => (error as NodeJS.ErrnoException).code === 'ENOENT' ? missing : Promise.reject(error))\n}\n\nexport const promoteSkill = async (\n rootDir: string,\n name: string,\n files: ReadonlyArray<CollectedFile>,\n attempts: ReadonlyArray<SourceAttempt>,\n): Promise<Result<GeneratedSkill, SkillRunError>> => {\n const root = resolve(rootDir)\n let rootStat = await statOrMissing(root).catch(error => error as Error)\n if (rootStat instanceof Error)\n return pathError('Output root cannot be inspected.', root)\n let createdRoot = false\n if (rootStat === missing) {\n const parent = dirname(root)\n const parentStat = await statOrMissing(parent).catch(error => error as Error)\n if (parentStat instanceof Error || parentStat === missing || !parentStat.isDirectory() || parentStat.isSymbolicLink())\n return pathError('Output root parent must be an existing directory.', parent)\n const canonicalParent = await realpath(parent).catch(error => error as Error)\n if (canonicalParent instanceof Error || canonicalParent !== parent)\n return pathError('Output root parent must not pass through a symbolic link.', parent)\n const created = await mkdir(root, { mode: 0o700 }).then(() => true, error => error as NodeJS.ErrnoException)\n if (created instanceof Error && created.code !== 'EEXIST')\n return pathError('Output root cannot be created.', root)\n createdRoot = created === true\n rootStat = await statOrMissing(root).catch(error => error as Error)\n }\n if (rootStat instanceof Error || rootStat === missing || !rootStat.isDirectory() || rootStat.isSymbolicLink())\n return pathError('Output root must be a directory, not a symbolic link.', root)\n const canonicalRoot = await realpath(root).catch(error => error as Error)\n if (canonicalRoot instanceof Error)\n return pathError('Output root cannot be resolved.', root)\n if (canonicalRoot !== root)\n return pathError('Output root must not pass through a symbolic link.', root)\n\n const target = join(root, name)\n const targetStat = await statOrMissing(target).catch(error => error as Error)\n if (targetStat instanceof Error)\n return pathError('Output path cannot be inspected.', target)\n if (targetStat !== missing && (!targetStat.isDirectory() || targetStat.isSymbolicLink()))\n return pathError('Output path must be a directory, not a symbolic link.', target)\n\n const lockPath = join(root, `.skilld-${name}.lock`)\n const lock = await open(lockPath, 'wx').catch(error => error as NodeJS.ErrnoException)\n if (lock instanceof Error) {\n if (lock.code === 'EEXIST')\n return err({ _tag: 'OutputBusy', message: 'Another Skill run owns this output path.', path: target })\n return err({ _tag: 'PromotionFailed', message: 'Output lock could not be created.', path: target, cause: lock })\n }\n\n const nonce = randomUUID()\n const staging = join(root, `.skilld-${name}-${nonce}.next`)\n const backup = join(root, `.skilld-${name}-${nonce}.previous`)\n let movedCurrent = false\n let promoted = false\n let failureMessage = 'Skill output could not be promoted.'\n let failureCause: unknown\n\n try {\n await mkdir(staging, { mode: 0o700 })\n for (const file of files) {\n const destination = join(staging, file.path)\n await mkdir(dirname(destination), { recursive: true, mode: 0o700 })\n await writeFile(destination, file.content, { flag: 'wx', mode: 0o600 })\n }\n\n if (targetStat !== missing) {\n await rename(target, backup)\n movedCurrent = true\n }\n await rename(staging, target)\n promoted = true\n }\n catch (cause) {\n failureCause = cause\n if (movedCurrent && !promoted) {\n const current = await statOrMissing(target).catch(error => error as Error)\n if (current instanceof Error) {\n failureMessage = 'Skill output rollback could not inspect its destination.'\n failureCause = new AggregateError([cause, current])\n }\n if (current === missing) {\n const rollback = await rename(backup, target).catch(error => error as Error)\n if (rollback instanceof Error) {\n failureMessage = 'Skill output rollback failed.'\n failureCause = new AggregateError([cause, rollback])\n }\n }\n }\n }\n\n const lockCleanupErrors: unknown[] = []\n await lock.close().catch(error => lockCleanupErrors.push(error))\n await unlink(lockPath).catch(error => lockCleanupErrors.push(error))\n\n if (promoted) {\n const warnings: string[] = []\n if (lockCleanupErrors.length > 0) {\n const detail = lockCleanupErrors.map(error => error instanceof Error ? error.message : String(error)).join('; ')\n warnings.push(`Output lock cleanup failed at ${lockPath}: ${detail}`)\n }\n if (movedCurrent) {\n const backupCleanup = await rm(backup, { recursive: true }).catch(error => error as Error)\n if (backupCleanup instanceof Error)\n warnings.push(`Previous Skill cleanup failed at ${backup}: ${backupCleanup.message}`)\n }\n return ok({\n _tag: 'GeneratedSkill',\n name,\n outputDir: target,\n files: files.map(file => ({ path: file.path, bytes: file.content.byteLength })),\n sourceAttempts: attempts,\n warnings,\n })\n }\n\n const cleanupErrors = [...lockCleanupErrors]\n await rm(staging, { recursive: true, force: true }).catch(error => cleanupErrors.push(error))\n if (createdRoot)\n await rmdir(root).catch(error => cleanupErrors.push(error))\n const causes = failureCause === undefined ? cleanupErrors : [failureCause, ...cleanupErrors]\n const cause = causes.length <= 1 ? causes[0] : new AggregateError(causes, 'Skill output cleanup failed.')\n return err({ _tag: 'PromotionFailed', message: failureMessage, path: target, cause })\n}\n","/** A search command the Agent can repeat, matched on whole words. */\nconst searchCommand = /\\b(?:git grep|grep|rg)\\b/\n\n/** Inline code spans, which is where a Skill writes its file pointers. */\nconst inlineCode = /`([^`\\n]+)`/g\n\n/** Fenced blocks, which hold commands rather than pointers. */\nconst fencedBlock = /```[^\\n]*\\n[\\s\\S]*?```/g\n\n/**\n * A Skill may report a file the project lacks, such as a missing entry point.\n * A sentence that denies a path states a fact about the project, not a pointer.\n * A sentence that tells the Agent to create a file gives a task, not a pointer.\n * The check reads whole paragraphs, so a hard-wrapped denial still covers the\n * code spans in its own sentence.\n */\nconst denial = /\\b(no|not|none|never|absent|missing|lacks|without|instead|rather|empty|undeclared|unavailable|nothing)\\b|\\bdoes ?n[o']t\\b/i\n\nconst creation = /\\b(?:copy|create|write|generate)\\b/i\n\nfunction fencedBlocks(markdown: string): ReadonlyArray<string> {\n return markdown.match(fencedBlock) ?? []\n}\n\n/**\n * Prose paragraphs, with fenced commands removed so a command never reads as a\n * pointer. A soft-wrapped sentence keeps its words together: the lines of a\n * paragraph join before any check runs.\n */\nfunction proseParagraphs(markdown: string): ReadonlyArray<string> {\n return markdown\n .replace(fencedBlock, '')\n .split(/\\n[ \\t]*\\n/)\n .map(paragraph => paragraph.replace(/\\n/g, ' ').trim())\n .filter(paragraph => paragraph.length > 0)\n}\n\n/**\n * Extensions a real file pointer carries. A token ending elsewhere, such as\n * the `.0` in a version, carries no path shape.\n */\nconst pathExtension = /\\.(?:md|mdx|json|jsonc|ts|tsx|mts|cts|js|mjs|cjs|jsx|vue|svelte|txt|yml|yaml|toml|html|css|scss|rs|py|go|sh|lock|env|csv|xml)$/i\n\n/** A release number such as `20.19.0` or `v20` states a version, never a path. */\nconst versionShape = /^v?\\d+(?:\\.\\d+)*$/i\n\n/** A product name such as `Node.js` or `node.js` names a tool, never a file. */\nconst productName = /^[A-Za-z][A-Za-z0-9_]*\\.[A-Za-z0-9]+$/\n\n/**\n * A token counts as a file pointer only when it cannot be anything else.\n * A false pointer fails a correct Skill, so every rule here excludes rather than includes.\n */\nfunction pathCandidate(token: string): string | null {\n if (token.length === 0 || /\\s/.test(token))\n return null\n // A flag, a variable, a comment, or a placeholder is never a pointer.\n if (/^[-$#<{[]/.test(token))\n return null\n // A URL, a package specifier, or a shell construct is never a pointer.\n if (token.includes('://') || /[()|;&\"'@:]/.test(token))\n return null\n const trimmed = token.replace(/^\\.\\//, '').replace(/\\/+$/, '')\n if (trimmed.length === 0 || trimmed.startsWith('..'))\n return null\n // A version, a product name, or a numbered reference such as `HTTP/2` is never a pointer.\n if (versionShape.test(trimmed) || productName.test(trimmed) || /\\/\\d+$/.test(trimmed))\n return null\n // A pointer carries a slash-qualified path or a known file extension.\n if (!trimmed.includes('/') && !pathExtension.test(trimmed))\n return null\n return trimmed\n}\n\n/** The literal prefix of a glob, which is the part a real path must start with. */\nfunction literalPrefix(candidate: string): string {\n const wildcard = candidate.search(/[*?]/)\n if (wildcard === -1)\n return candidate\n const cut = candidate.slice(0, wildcard).replace(/[^/]*$/, '')\n return cut.replace(/\\/+$/, '')\n}\n\nfunction resolves(candidate: string, known: ReadonlySet<string>): boolean {\n if (known.has(candidate))\n return true\n const prefix = literalPrefix(candidate)\n if (prefix.length === 0)\n return true\n for (const path of known) {\n // A known template such as `.env.example` implies the file it is copied to.\n if (path.startsWith(`${candidate}.`))\n return true\n // A directory pointer resolves when the project holds a file under it.\n if (path === prefix || path.startsWith(`${prefix}/`))\n return true\n }\n return false\n}\n\nexport interface ProjectSkillInput {\n /** The generated SKILL.md text. */\n readonly markdown: string\n /** Project-relative paths the Harness prepared for the Agent. */\n readonly projectPaths: ReadonlyArray<string>\n /** Output-relative paths the Skill writes beside SKILL.md. */\n readonly outputPaths: ReadonlyArray<string>\n}\n\n/**\n * Check that a project Skill points at files the project contains and gives a\n * search the Agent can repeat. The Agent cannot verify its own pointers.\n *\n * The Harness hides generated directories from the Agent, so a pointer at one is\n * invention like any other. A sentence that states a path is missing, or that\n * tells the Agent to create one, passes: it reports a fact or gives a task, not\n * a pointer. The sentence may wrap across prose lines within its paragraph.\n */\nexport function checkProjectSkill(input: ProjectSkillInput): ReadonlyArray<string> {\n const issues: string[] = []\n const known = new Set([...input.projectPaths, ...input.outputPaths])\n const unknown = new Set<string>()\n\n for (const paragraph of proseParagraphs(input.markdown)) {\n if (denial.test(paragraph) || creation.test(paragraph))\n continue\n for (const match of paragraph.matchAll(inlineCode)) {\n const candidate = pathCandidate(match[1]!.trim())\n if (candidate !== null && !resolves(candidate, known))\n unknown.add(candidate)\n }\n }\n\n for (const path of [...unknown].sort())\n issues.push(`SKILL.md points at a path the project does not contain: ${path}.`)\n\n const inline = [...input.markdown.matchAll(inlineCode)].map(match => match[1]!)\n const commands = [...fencedBlocks(input.markdown), ...inline].join('\\n')\n if (!searchCommand.test(commands))\n issues.push('SKILL.md must give the Agent at least one search command it can repeat.')\n\n return issues\n}\n","import type { SkillReview, SkillReviewFinding, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { CollectedFile } from './collect.ts'\nimport { parseDocument } from 'yaml'\nimport { isSkillName, normalizeOutputPath } from '../paths.ts'\nimport { checkProjectSkill } from './project.ts'\nimport { err, ok } from '../result.ts'\n\nconst allowedFrontmatter = new Set([\n 'name',\n 'description',\n 'license',\n 'compatibility',\n 'metadata',\n 'allowed-tools',\n])\n\nconst invalid = (issues: ReadonlyArray<string>): Result<never, SkillRunError> =>\n err({ _tag: 'InvalidSkill', message: 'Skill output failed deterministic checks.', issues })\n\nfunction decodeText(content: Uint8Array): string | null {\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(content)\n }\n catch {\n return null\n }\n}\n\nconst isStringMap = (value: unknown): boolean =>\n value !== null\n && typeof value === 'object'\n && !Array.isArray(value)\n && Object.values(value).every(item => typeof item === 'string')\n\nexport const validateGeneratedSkill = (\n name: string,\n files: ReadonlyArray<CollectedFile>,\n project?: ReadonlyArray<string>,\n): Result<void, SkillRunError> => {\n const issues: string[] = []\n const skillFiles = files.filter(file => file.path === 'SKILL.md')\n if (skillFiles.length !== 1)\n return invalid(['Output must contain exactly one SKILL.md file.'])\n\n const source = decodeText(skillFiles[0]!.content)\n if (source === null)\n return invalid(['SKILL.md must contain valid UTF-8 text.'])\n if (project !== undefined) {\n issues.push(...checkProjectSkill({\n markdown: source,\n projectPaths: project,\n outputPaths: files.map(file => file.path),\n }))\n }\n const match = source.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n([\\s\\S]*)$/)\n if (!match)\n return invalid(['SKILL.md must start with YAML frontmatter.'])\n if (match[2]?.trim().length === 0)\n issues.push('SKILL.md instructions must not be empty.')\n\n const document = parseDocument(match[1]!, { uniqueKeys: true })\n for (const error of document.errors)\n issues.push(`Frontmatter: ${error.message}`)\n for (const warning of document.warnings)\n issues.push(`Frontmatter: ${warning.message}`)\n\n const value = document.errors.length === 0 ? document.toJS() as unknown : null\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n issues.push('Frontmatter must be a mapping.')\n return invalid(issues)\n }\n\n const frontmatter = value as Record<string, unknown>\n for (const key of Object.keys(frontmatter)) {\n if (!allowedFrontmatter.has(key))\n issues.push(`Frontmatter field is not supported: ${key}`)\n }\n if (frontmatter.name !== name)\n issues.push('Frontmatter name must match the Skill directory name.')\n if (typeof frontmatter.name !== 'string' || !isSkillName(frontmatter.name))\n issues.push('Frontmatter name is invalid.')\n if (typeof frontmatter.description !== 'string' || frontmatter.description.trim().length === 0 || frontmatter.description.length > 1024)\n issues.push('Frontmatter description must contain 1 to 1024 characters.')\n if (frontmatter.license !== undefined && typeof frontmatter.license !== 'string')\n issues.push('Frontmatter license must be a string.')\n if (frontmatter.compatibility !== undefined && (typeof frontmatter.compatibility !== 'string' || frontmatter.compatibility.length > 500))\n issues.push('Frontmatter compatibility must be a string of at most 500 characters.')\n if (frontmatter.metadata !== undefined && !isStringMap(frontmatter.metadata))\n issues.push('Frontmatter metadata must map strings to strings.')\n if (frontmatter['allowed-tools'] !== undefined && typeof frontmatter['allowed-tools'] !== 'string')\n issues.push('Frontmatter allowed-tools must be a string.')\n\n return issues.length === 0 ? ok(undefined) : invalid(issues)\n}\n\nconst findingLevels = new Set(['error', 'warning', 'note'])\n\nexport const validateSkillReview = (\n files: ReadonlyArray<CollectedFile>,\n): Result<SkillReview, SkillRunError> => {\n if (files.length !== 1 || files[0]?.path !== 'review.json')\n return invalid(['Review output must contain only review.json.'])\n\n let value: unknown\n try {\n const source = decodeText(files[0].content)\n if (source === null)\n return invalid(['review.json must contain valid UTF-8 text.'])\n value = JSON.parse(source)\n }\n catch {\n return invalid(['review.json must contain valid JSON.'])\n }\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return invalid(['review.json must contain an object.'])\n\n const review = value as Record<string, unknown>\n if (Object.keys(review).some(key => key !== 'summary' && key !== 'findings'))\n return invalid(['review.json contains unsupported fields.'])\n if (typeof review.summary !== 'string' || review.summary.trim().length === 0)\n return invalid(['Review summary is required.'])\n if (!Array.isArray(review.findings))\n return invalid(['Review findings must be an array.'])\n\n const findings: SkillReviewFinding[] = []\n for (const item of review.findings) {\n if (!item || typeof item !== 'object' || Array.isArray(item))\n return invalid(['Each review finding must be an object.'])\n const finding = item as Record<string, unknown>\n if (Object.keys(finding).some(key => !['level', 'path', 'message', 'fix'].includes(key)))\n return invalid(['A review finding contains unsupported fields.'])\n if (typeof finding.level !== 'string' || !findingLevels.has(finding.level))\n return invalid(['A review finding level is invalid.'])\n if (typeof finding.path !== 'string' || normalizeOutputPath(finding.path) === null)\n return invalid(['A review finding path is invalid.'])\n if (typeof finding.message !== 'string' || finding.message.trim().length === 0 || typeof finding.fix !== 'string' || finding.fix.trim().length === 0)\n return invalid(['A review finding is incomplete.'])\n findings.push(finding as unknown as SkillReviewFinding)\n }\n\n return ok({ _tag: 'SkillReview', summary: review.summary, findings })\n}\n","import type { SkillOutputPolicy, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport { constants } from 'node:fs'\nimport { lstat, open, opendir, realpath } from 'node:fs/promises'\nimport { join, relative, resolve, sep } from 'node:path'\nimport { err, ok } from '../result.ts'\n\nexport interface PreparedFile {\n readonly path: string\n readonly content: Uint8Array\n}\n\nexport interface PreparedSource {\n readonly files: ReadonlyArray<PreparedFile>\n readonly attempts: ReadonlyArray<SourceAttempt>\n readonly npmResolution?: {\n readonly package: string\n readonly version: string\n }\n}\n\nconst ignoredNames = new Set([\n '.git',\n '.hg',\n '.next',\n '.nuxt',\n '.output',\n '.skilld',\n '.turbo',\n 'coverage',\n 'dist',\n 'node_modules',\n 'target',\n])\n\nexport async function collectHostDirectory(directory: string, policy: SkillOutputPolicy, sourceLabel = directory, signal?: AbortSignal): Promise<Result<PreparedSource, SkillRunError>> {\n const root = resolve(directory)\n const unavailable = (message: string, cause?: unknown): Result<never, SkillRunError> => err({\n _tag: 'SourceUnavailable',\n message,\n attempts: [{ source: sourceLabel, status: 'skipped', reason: message }],\n cause,\n })\n const cancelled = (): Result<never, SkillRunError> => err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n if (signal?.aborted)\n return cancelled()\n const rootStat = await lstat(root).catch(error => error as NodeJS.ErrnoException)\n if (rootStat instanceof Error)\n return unavailable('Source directory is unavailable.', rootStat)\n if (!rootStat.isDirectory() || rootStat.isSymbolicLink())\n return unavailable('Source path must be a directory, not a symbolic link.')\n const canonicalRoot = await realpath(root).catch(error => error as NodeJS.ErrnoException)\n if (canonicalRoot instanceof Error)\n return unavailable('Source directory cannot be resolved.', canonicalRoot)\n if (canonicalRoot !== root)\n return unavailable('Source path must not pass through a symbolic link.')\n\n const files: PreparedFile[] = []\n let totalBytes = 0\n\n const walk = async (current: string): Promise<Result<void, SkillRunError>> => {\n if (signal?.aborted)\n return cancelled()\n const canonicalCurrent = await realpath(current).catch(error => error as NodeJS.ErrnoException)\n if (canonicalCurrent instanceof Error || canonicalCurrent !== current)\n return unavailable('Source directory changed during collection.', canonicalCurrent instanceof Error ? canonicalCurrent : undefined)\n const directoryHandle = await opendir(current).catch(error => error as NodeJS.ErrnoException)\n if (directoryHandle instanceof Error)\n return unavailable('Source directory cannot be read.', directoryHandle)\n\n for await (const entry of directoryHandle) {\n if (signal?.aborted)\n return cancelled()\n if (ignoredNames.has(entry.name))\n continue\n const absolute = join(current, entry.name)\n const stat = await lstat(absolute).catch(error => error as NodeJS.ErrnoException)\n if (stat instanceof Error)\n return unavailable('Source entry cannot be read.', stat)\n if (stat.isSymbolicLink())\n return unavailable('Source contains a symbolic link.')\n if (stat.isDirectory()) {\n const nested = await walk(absolute)\n if (nested._tag === 'Err')\n return nested\n continue\n }\n if (!stat.isFile())\n return unavailable('Source contains a special file.')\n\n const path = relative(root, absolute).split(sep).join('/')\n const handle = await open(absolute, constants.O_RDONLY | constants.O_NOFOLLOW).catch(error => error as NodeJS.ErrnoException)\n if (handle instanceof Error)\n return unavailable('Source file cannot be opened without following links.', handle)\n const openedStat = await handle.stat().catch(error => error as NodeJS.ErrnoException)\n if (openedStat instanceof Error) {\n await handle.close()\n return unavailable('Source file cannot be inspected.', openedStat)\n }\n if (!openedStat.isFile() || openedStat.dev !== stat.dev || openedStat.ino !== stat.ino) {\n await handle.close()\n return unavailable('Source file changed during collection.')\n }\n if (openedStat.size > policy.maxSourceFileBytes) {\n await handle.close()\n continue\n }\n if (files.length >= policy.maxSourceFiles) {\n await handle.close()\n return unavailable('Source contains too many files.')\n }\n if (totalBytes + openedStat.size > policy.maxSourceBytes) {\n await handle.close()\n return unavailable('Source exceeds the total byte limit.')\n }\n\n const content = await handle.readFile().catch(error => error as NodeJS.ErrnoException)\n await handle.close()\n if (content instanceof Error)\n return unavailable('Source file cannot be read.', content)\n if (content.byteLength !== openedStat.size)\n return unavailable('Source file changed during collection.')\n if (signal?.aborted)\n return cancelled()\n files.push({ path, content })\n totalBytes += content.byteLength\n }\n return ok(undefined)\n }\n\n const walked = await walk(root)\n if (walked._tag === 'Err')\n return walked\n if (files.length === 0)\n return unavailable('Source directory has no usable files.')\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok({ files, attempts: [{ source: sourceLabel, status: 'used' }] })\n}\n","import type { SkillOutputPolicy, SkillRunError } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { PreparedFile } from './host.ts'\nimport { Readable } from 'node:stream'\nimport { createGunzip } from 'node:zlib'\nimport { normalizeOutputPath } from '../paths.ts'\nimport { err, ok } from '../result.ts'\n\nconst blockBytes = 512\n\nexport type ArchiveLayout = 'npm' | 'github'\n\ninterface PendingEntry {\n readonly kind: 'directory' | 'file' | 'metadata' | 'skip'\n readonly path?: string\n readonly metadataType?: 'long-path' | 'pax'\n readonly size: number\n remaining: number\n padding: number\n readonly chunks: Array<Buffer<ArrayBufferLike>>\n}\n\nfunction unavailable(message: string, cause?: unknown): Result<never, SkillRunError> {\n return err({ _tag: 'SourceUnavailable', message, attempts: [], cause })\n}\n\nfunction readString(buffer: Uint8Array, start: number, length: number): string {\n const bytes = buffer.subarray(start, start + length)\n const zero = bytes.indexOf(0)\n return Buffer.from(zero === -1 ? bytes : bytes.subarray(0, zero)).toString('utf8').trim()\n}\n\nfunction readOctal(buffer: Uint8Array, start: number, length: number): number | null {\n const value = readString(buffer, start, length).replace(/^\\s+|\\s+$/g, '')\n if (!/^[0-7]+$/.test(value))\n return null\n const parsed = Number.parseInt(value, 8)\n return Number.isSafeInteger(parsed) ? parsed : null\n}\n\nfunction hasValidChecksum(header: Uint8Array): boolean {\n const expected = readOctal(header, 148, 8)\n if (expected === null)\n return false\n let actual = 0\n for (let index = 0; index < header.byteLength; index += 1)\n actual += index >= 148 && index < 156 ? 32 : header[index]!\n return actual === expected\n}\n\nfunction parsePaxPath(content: Uint8Array): string | null {\n const text = Buffer.from(content).toString('utf8')\n let offset = 0\n let path: string | null = null\n while (offset < text.length) {\n const space = text.indexOf(' ', offset)\n if (space === -1)\n return null\n const length = Number.parseInt(text.slice(offset, space), 10)\n if (!Number.isSafeInteger(length) || length <= 0 || offset + length > text.length)\n return null\n const record = text.slice(space + 1, offset + length - 1)\n const equals = record.indexOf('=')\n if (equals !== -1 && record.slice(0, equals) === 'path')\n path = record.slice(equals + 1)\n offset += length\n }\n return path\n}\n\nfunction archivePath(value: string, layout: ArchiveLayout): string | null {\n const normalized = normalizeOutputPath(value)\n if (normalized === null)\n return null\n const segments = normalized.split('/')\n if (layout === 'npm') {\n if (segments[0] !== 'package' || segments.length < 2)\n return null\n }\n else if (segments.length < 2) {\n return null\n }\n return normalizeOutputPath(segments.slice(1).join('/'))\n}\n\nfunction entryContent(entry: PendingEntry): Buffer<ArrayBufferLike> {\n return entry.chunks.length === 1\n ? entry.chunks[0]!\n : Buffer.concat(entry.chunks, entry.size)\n}\n\nexport async function extractArchive(\n compressed: AsyncIterable<Uint8Array>,\n policy: SkillOutputPolicy,\n layout: ArchiveLayout,\n): Promise<Result<ReadonlyArray<PreparedFile>, SkillRunError>> {\n const source = Readable.from(compressed)\n const gunzip = createGunzip()\n source.on('error', error => gunzip.destroy(error))\n source.pipe(gunzip)\n const files: PreparedFile[] = []\n const seen = new Set<string>()\n const maxArchiveBytes = policy.maxSourceBytes + (policy.maxSourceFiles + 2) * blockBytes * 2\n let archiveBytes = 0\n let fileEntries = 0\n let nextPath: string | null = null\n let pending: Buffer<ArrayBufferLike> = Buffer.alloc(0)\n let entry: PendingEntry | null = null\n let totalBytes = 0\n let ended = false\n\n try {\n for await (const value of gunzip) {\n const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value as Uint8Array)\n archiveBytes += chunk.byteLength\n if (archiveBytes > maxArchiveBytes)\n return unavailable('Source archive exceeds the uncompressed byte limit.')\n pending = pending.byteLength === 0 ? chunk : Buffer.concat([pending, chunk])\n\n while (pending.byteLength > 0) {\n if (ended) {\n if (pending.some(byte => byte !== 0))\n return unavailable('Source archive has data after its end marker.')\n pending = Buffer.alloc(0)\n break\n }\n\n if (entry !== null) {\n if (entry.remaining > 0) {\n const consumed = Math.min(entry.remaining, pending.byteLength)\n if (entry.kind === 'file' || entry.kind === 'metadata')\n entry.chunks.push(pending.subarray(0, consumed))\n pending = pending.subarray(consumed)\n entry.remaining -= consumed\n if (entry.remaining > 0)\n break\n }\n if (entry.padding > 0) {\n const consumed = Math.min(entry.padding, pending.byteLength)\n pending = pending.subarray(consumed)\n entry.padding -= consumed\n if (entry.padding > 0)\n break\n }\n\n if (entry.kind === 'metadata') {\n const content = entryContent(entry)\n nextPath = entry.metadataType === 'pax'\n ? parsePaxPath(content)\n : content.toString('utf8').replace(/\\0.*$/s, '').trim()\n if (nextPath === null || nextPath.length === 0)\n return unavailable('Source archive has invalid path metadata.')\n }\n else if (entry.kind === 'file' && entry.path) {\n files.push({ path: entry.path, content: Uint8Array.from(entryContent(entry)) })\n totalBytes += entry.size\n }\n entry = null\n continue\n }\n\n if (pending.byteLength < blockBytes)\n break\n const header = pending.subarray(0, blockBytes)\n pending = pending.subarray(blockBytes)\n if (header.every(byte => byte === 0)) {\n ended = true\n continue\n }\n if (!hasValidChecksum(header))\n return unavailable('Source archive has an invalid header checksum.')\n\n const size = readOctal(header, 124, 12)\n if (size === null)\n return unavailable('Source archive has an invalid entry size.')\n const name = readString(header, 0, 100)\n const prefix = readString(header, 345, 155)\n const headerPath = prefix.length > 0 ? `${prefix}/${name}` : name\n const type = String.fromCharCode(header[156] ?? 0)\n const padding = (blockBytes - size % blockBytes) % blockBytes\n\n if (type === 'x' || type === 'L') {\n if (size > policy.maxSourceFileBytes)\n return unavailable('Source archive path metadata exceeds the file byte limit.')\n entry = {\n kind: 'metadata',\n metadataType: type === 'x' ? 'pax' : 'long-path',\n size,\n remaining: size,\n padding,\n chunks: [],\n }\n continue\n }\n if (type === '5') {\n nextPath = null\n entry = { kind: 'directory', size, remaining: size, padding, chunks: [] }\n continue\n }\n if (type !== '0' && type !== '\\0')\n return unavailable('Source archive contains a linked or special entry.')\n\n const path = archivePath(nextPath ?? headerPath, layout)\n nextPath = null\n if (path === null)\n return unavailable('Source archive contains an invalid path.')\n if (seen.has(path))\n return unavailable('Source archive contains duplicate paths.')\n seen.add(path)\n fileEntries += 1\n if (fileEntries > policy.maxSourceFiles)\n return unavailable('Source archive contains too many files.')\n if (size <= policy.maxSourceFileBytes && totalBytes + size > policy.maxSourceBytes)\n return unavailable('Source archive exceeds the total byte limit.')\n\n entry = {\n kind: size > policy.maxSourceFileBytes ? 'skip' : 'file',\n path,\n size,\n remaining: size,\n padding,\n chunks: [],\n }\n }\n }\n }\n catch (cause) {\n const message = cause instanceof Error ? cause.message : ''\n return unavailable(message.startsWith('Source archive ')\n ? message\n : 'Source archive is not valid gzip data.', cause)\n }\n\n if (entry !== null || pending.some(byte => byte !== 0) || !ended)\n return unavailable('Source archive ended before its final entry.')\n if (files.length === 0)\n return unavailable('Source archive has no usable files.')\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok(files)\n}\n","import type { Hash } from 'node:crypto'\nimport type { SkillOutputPolicy, SkillRunError, SourceAttempt } from '../../types.ts'\nimport type { Result } from '../result.ts'\nimport type { PreparedFile, PreparedSource } from './host.ts'\nimport { createHash } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { err, ok } from '../result.ts'\nimport { extractArchive } from './archive.ts'\n\ninterface NpmDist {\n readonly tarball: string\n readonly integrity?: string\n readonly shasum?: string\n}\n\ninterface NpmRepository {\n readonly type?: string\n readonly url?: string\n}\n\ninterface NpmVersion {\n readonly version: string\n readonly dist: NpmDist\n readonly gitHead?: string\n readonly repository?: NpmRepository | string\n}\n\ninterface IntegrityVerifier {\n readonly update: (content: Uint8Array) => void\n readonly valid: () => boolean\n}\n\nconst packageSpecPattern = /^(@[^/@]+\\/[^/@]+|[^/@]+)(?:@(\\S+))?$/\nconst exactVersionPattern = /^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(?:-[\\da-z-]+(?:\\.[\\da-z-]+)*)?(?:\\+[\\da-z-]+(?:\\.[\\da-z-]+)*)?$/i\nconst gitCommitPattern = /^[0-9a-f]{40}$/i\nconst npmRequestTimeoutMs = 30_000\nconst npmRedirectLimit = 3\nconst packumentByteLimit = 10 * 1024 * 1024\n\nexport type FetchClient = (input: string | URL | Request, init?: RequestInit) => Promise<Response>\n\nfunction unavailable(message: string, attempts: ReadonlyArray<SourceAttempt>, cause?: unknown): Result<never, SkillRunError> {\n return err({ _tag: 'SourceUnavailable', message, attempts, cause })\n}\n\nfunction parsePackageSpec(spec: string): { name: string, version: string } | null {\n const match = spec.match(packageSpecPattern)\n return match?.[1] ? { name: match[1], version: match[2] ?? 'latest' } : null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parseRepository(value: unknown): NpmRepository | string | undefined {\n if (typeof value === 'string')\n return value\n if (!isRecord(value))\n return undefined\n return {\n type: typeof value.type === 'string' ? value.type : undefined,\n url: typeof value.url === 'string' ? value.url : undefined,\n }\n}\n\nfunction parseVersion(packument: unknown, requested: string): { resolvedVersion: string, version: NpmVersion, repository?: NpmRepository | string } | null {\n if (!isRecord(packument) || !isRecord(packument['dist-tags']) || !isRecord(packument.versions))\n return null\n const tagged = packument['dist-tags'][requested]\n const resolvedVersion = typeof tagged === 'string' ? tagged : requested\n const value = packument.versions[resolvedVersion]\n if (!isRecord(value) || typeof value.version !== 'string' || !isRecord(value.dist) || typeof value.dist.tarball !== 'string')\n return null\n return {\n resolvedVersion,\n repository: parseRepository(packument.repository),\n version: {\n version: value.version,\n gitHead: typeof value.gitHead === 'string' ? value.gitHead : undefined,\n repository: parseRepository(value.repository),\n dist: {\n tarball: value.dist.tarball,\n integrity: typeof value.dist.integrity === 'string' ? value.dist.integrity : undefined,\n shasum: typeof value.dist.shasum === 'string' ? value.dist.shasum : undefined,\n },\n },\n }\n}\n\nasync function cancelBody(response: Response): Promise<void> {\n if (!response.body)\n return\n // A failed cleanup cannot replace the source failure.\n await response.body.cancel().catch(() => undefined)\n}\n\nfunction declaredBytes(response: Response): number | null {\n const value = response.headers.get('content-length')\n if (value === null || !/^\\d+$/.test(value))\n return null\n const bytes = Number(value)\n return Number.isSafeInteger(bytes) ? bytes : null\n}\n\nasync function* responseChunks(\n response: Response,\n maxBytes: number,\n limitMessage: string,\n onChunk: (content: Uint8Array) => void = () => {},\n): AsyncGenerator<Uint8Array> {\n const declared = declaredBytes(response)\n if (declared !== null && declared > maxBytes) {\n await cancelBody(response)\n throw new Error(limitMessage)\n }\n if (!response.body)\n return\n\n const reader = response.body.getReader()\n let complete = false\n let totalBytes = 0\n try {\n while (true) {\n const result = await reader.read()\n if (result.done) {\n complete = true\n return\n }\n totalBytes += result.value.byteLength\n if (totalBytes > maxBytes)\n throw new Error(limitMessage)\n onChunk(result.value)\n yield result.value\n }\n }\n finally {\n if (!complete) {\n // A failed cleanup cannot replace the source failure.\n await reader.cancel().catch(() => undefined)\n }\n reader.releaseLock()\n }\n}\n\nasync function readBoundedResponse(response: Response, maxBytes: number): Promise<Result<Uint8Array, SkillRunError>> {\n const chunks: Uint8Array[] = []\n let totalBytes = 0\n try {\n for await (const chunk of responseChunks(response, maxBytes, 'npm response exceeds the byte limit.')) {\n chunks.push(chunk)\n totalBytes += chunk.byteLength\n }\n }\n catch (cause) {\n return unavailable(cause instanceof Error ? cause.message : 'npm response cannot be read.', [], cause)\n }\n return ok(Uint8Array.from(Buffer.concat(chunks, totalBytes)))\n}\n\nasync function fetchWithRedirects(fetchClient: FetchClient, url: string, init: RequestInit = {}, signal?: AbortSignal): Promise<Result<Response, SkillRunError>> {\n const timeout = AbortSignal.timeout(npmRequestTimeoutMs)\n const requestSignal = signal ? AbortSignal.any([signal, timeout]) : timeout\n let current = url\n for (let redirects = 0; redirects <= npmRedirectLimit; redirects += 1) {\n const response = await fetchClient(current, { ...init, redirect: 'manual', signal: requestSignal }).catch(error => error as Error)\n if (response instanceof Error)\n return unavailable(timeout.aborted ? 'npm request timed out.' : 'npm request failed.', [], response)\n if (![301, 302, 303, 307, 308].includes(response.status))\n return ok(response)\n if (redirects === npmRedirectLimit) {\n await cancelBody(response)\n return unavailable('npm request exceeded the redirect limit.', [])\n }\n const location = response.headers.get('location')\n if (!location) {\n await cancelBody(response)\n return unavailable('npm redirect is missing its location.', [])\n }\n const redirected = await Promise.resolve().then(() => new URL(location, current)).catch(() => null)\n if (redirected === null) {\n await cancelBody(response)\n return unavailable('npm redirect location is invalid.', [])\n }\n if (redirected.protocol !== 'https:') {\n await cancelBody(response)\n return unavailable('npm redirect URL must use HTTPS.', [])\n }\n await cancelBody(response)\n current = redirected.href\n }\n return unavailable('npm request exceeded the redirect limit.', [])\n}\n\nfunction createIntegrityVerifier(dist: NpmDist): IntegrityVerifier | null {\n const values = dist.integrity?.split(/\\s+/).filter(Boolean) ?? []\n let expected = values.flatMap((value) => {\n const match = value.match(/^(sha(?:1|256|384|512))-([A-Za-z0-9+/=]+)$/)\n return match?.[1] && match[2] ? [{ algorithm: match[1], digest: match[2] }] : []\n })\n if (expected.length > 0) {\n const strength = { sha1: 1, sha256: 2, sha384: 3, sha512: 4 } as const\n const strongest = Math.max(...expected.map(value => strength[value.algorithm as keyof typeof strength]))\n expected = expected.filter(value => strength[value.algorithm as keyof typeof strength] === strongest)\n }\n if (values.length === 0 && dist.shasum && /^[0-9a-f]{40}$/i.test(dist.shasum))\n expected.push({ algorithm: 'sha1', digest: Buffer.from(dist.shasum, 'hex').toString('base64') })\n if (expected.length === 0)\n return null\n\n const hashes = new Map<string, Hash>()\n for (const value of expected)\n hashes.set(value.algorithm, createHash(value.algorithm))\n return {\n update(content) {\n for (const hash of hashes.values())\n hash.update(content)\n },\n valid() {\n const actual = new Map([...hashes].map(([algorithm, hash]) => [algorithm, hash.digest('base64')]))\n return expected.some(value => actual.get(value.algorithm) === value.digest)\n },\n }\n}\n\nfunction repositoryUrl(repository: NpmRepository | string | undefined): string | null {\n if (typeof repository === 'string')\n return repository\n return typeof repository?.url === 'string' ? repository.url : null\n}\n\nfunction githubArchiveUrl(repository: NpmRepository | string | undefined, gitHead: string | undefined): string | null {\n if (!gitHead || !gitCommitPattern.test(gitHead))\n return null\n const source = repositoryUrl(repository)\n if (source === null)\n return null\n const match = source.match(/^(?:(?:git\\+)?https?:\\/\\/(?:git@)?github\\.com\\/|git:\\/\\/github\\.com\\/|git@github\\.com:)([\\w.-]+)\\/([\\w.-]+?)(?:\\.git)?\\/?$/)\n if (!match?.[1] || !match[2])\n return null\n return `https://codeload.github.com/${encodeURIComponent(match[1])}/${encodeURIComponent(match[2])}/tar.gz/${gitHead}`\n}\n\nfunction skipped(source: string, reason: string): SourceAttempt {\n return { source, status: 'skipped', reason }\n}\n\nfunction used(source: string): SourceAttempt {\n return { source, status: 'used' }\n}\n\nfunction withAttempts(error: SkillRunError, attempts: ReadonlyArray<SourceAttempt>): Result<never, SkillRunError> {\n return error._tag === 'SourceUnavailable'\n ? unavailable(error.message, attempts, error.cause)\n : err(error)\n}\n\nfunction remainingPolicy(policy: SkillOutputPolicy, files: ReadonlyArray<PreparedFile>): SkillOutputPolicy | null {\n const usedBytes = files.reduce((total, file) => total + file.content.byteLength, 0)\n const maxSourceFiles = policy.maxSourceFiles - files.length\n const maxSourceBytes = policy.maxSourceBytes - usedBytes\n if (maxSourceFiles <= 0 || maxSourceBytes <= 0)\n return null\n return { ...policy, maxSourceFiles, maxSourceBytes }\n}\n\nasync function extractResponse(\n response: Response,\n policy: SkillOutputPolicy,\n layout: 'github' | 'npm',\n verifier?: IntegrityVerifier,\n): Promise<Result<ReadonlyArray<PreparedFile>, SkillRunError>> {\n return extractArchive(\n responseChunks(\n response,\n policy.maxSourceBytes,\n 'Source archive exceeds the compressed byte limit.',\n content => verifier?.update(content),\n ),\n policy,\n layout,\n )\n}\n\nexport async function prepareNpmPackage(spec: string, policy: SkillOutputPolicy, fetchClient: FetchClient, signal?: AbortSignal): Promise<Result<PreparedSource, SkillRunError>> {\n const parsed = parsePackageSpec(spec)\n if (parsed === null)\n return unavailable('npm package spec is invalid.', [])\n\n const attempts: SourceAttempt[] = []\n const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(parsed.name)}`\n const registryResponse = await fetchWithRedirects(fetchClient, registryUrl, {\n headers: { accept: 'application/vnd.npm.install-v1+json' },\n }, signal)\n if (registryResponse._tag === 'Err')\n return withAttempts(registryResponse.error, [skipped(registryUrl, registryResponse.error.message)])\n const response = registryResponse.value\n if (!response.ok) {\n await cancelBody(response)\n const message = `npm registry returned HTTP ${response.status}.`\n return unavailable(message, [skipped(registryUrl, message)])\n }\n\n const packumentBytes = await readBoundedResponse(response, packumentByteLimit)\n if (packumentBytes._tag === 'Err')\n return withAttempts(packumentBytes.error, [skipped(registryUrl, packumentBytes.error.message)])\n\n let packument: unknown\n try {\n packument = JSON.parse(Buffer.from(packumentBytes.value).toString('utf8')) as unknown\n }\n catch (cause) {\n const message = 'npm registry returned invalid package data.'\n return unavailable(message, [skipped(registryUrl, message)], cause)\n }\n\n const resolution = parseVersion(packument, parsed.version)\n if (resolution === null || !exactVersionPattern.test(resolution.resolvedVersion) || resolution.version.version !== resolution.resolvedVersion) {\n const message = 'npm package did not resolve to an exact version.'\n return unavailable(message, [skipped(registryUrl, message)])\n }\n const { repository, resolvedVersion, version } = resolution\n if (!version.dist.tarball.startsWith('https://')) {\n const message = 'npm package archive URL must use HTTPS.'\n return unavailable(message, [skipped(registryUrl, message)])\n }\n attempts.push(used(registryUrl))\n\n const fetchedArchive = await fetchWithRedirects(fetchClient, version.dist.tarball, {}, signal)\n if (fetchedArchive._tag === 'Err')\n return withAttempts(fetchedArchive.error, [...attempts, skipped(version.dist.tarball, fetchedArchive.error.message)])\n const archiveResponse = fetchedArchive.value\n if (!archiveResponse.ok) {\n await cancelBody(archiveResponse)\n const message = `npm package archive returned HTTP ${archiveResponse.status}.`\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n\n const verifier = createIntegrityVerifier(version.dist)\n if (verifier === null) {\n await cancelBody(archiveResponse)\n const message = 'npm package archive has no supported integrity value.'\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n const extracted = await extractResponse(archiveResponse, policy, 'npm', verifier)\n if (extracted._tag === 'Err')\n return withAttempts(extracted.error, [...attempts, skipped(version.dist.tarball, extracted.error.message)])\n if (!verifier.valid()) {\n const message = 'npm package archive integrity check failed.'\n return unavailable(message, [...attempts, skipped(version.dist.tarball, message)])\n }\n attempts.push(used(version.dist.tarball))\n let files = [...extracted.value]\n\n const repositoryArchive = githubArchiveUrl(version.repository ?? repository, version.gitHead)\n const available = remainingPolicy(policy, files)\n if (repositoryArchive !== null && available === null) {\n attempts.push(skipped(repositoryArchive, 'Prepared package files reached the source limit.'))\n }\n else if (repositoryArchive !== null && available !== null) {\n const fetchedRepository = await fetchWithRedirects(fetchClient, repositoryArchive, {}, signal)\n if (fetchedRepository._tag === 'Err') {\n attempts.push(skipped(repositoryArchive, fetchedRepository.error.message))\n }\n else if (!fetchedRepository.value.ok) {\n const message = `GitHub source archive returned HTTP ${fetchedRepository.value.status}.`\n await cancelBody(fetchedRepository.value)\n attempts.push(skipped(repositoryArchive, message))\n }\n else {\n const repositoryFiles = await extractResponse(fetchedRepository.value, available, 'github')\n if (repositoryFiles._tag === 'Err') {\n attempts.push(skipped(repositoryArchive, repositoryFiles.error.message))\n }\n else {\n files = [\n ...files,\n ...repositoryFiles.value.map(file => ({ ...file, path: posix.join('repository', file.path) })),\n ]\n attempts.push(used(repositoryArchive))\n }\n }\n }\n\n files.sort((left, right) => left.path.localeCompare(right.path))\n return ok({\n files,\n attempts,\n npmResolution: { package: parsed.name, version: resolvedVersion },\n })\n}\n","import type { HarnessV1Skill } from '@ai-sdk/harness'\nimport type { SandboxSession } from './internal/output/collect.ts'\nimport type { Result } from './internal/result.ts'\nimport type { PreparedSource } from './internal/source/host.ts'\nimport type { FetchClient } from './internal/source/npm.ts'\nimport type {\n CreateSkillHarnessOptions,\n SkillDestination,\n SkillHarness,\n SkillOutputPolicy,\n SkillRun,\n SkillRunError,\n SkillRunResult,\n} from './types.ts'\nimport { lstat, realpath } from 'node:fs/promises'\nimport { join, posix, resolve } from 'node:path'\nimport { HarnessAgent } from '@ai-sdk/harness/agent'\nimport { parseOutputPolicy, parseSkillRun } from './internal/input.ts'\nimport { collectSandboxOutput } from './internal/output/collect.ts'\nimport { promoteSkill } from './internal/output/promote.ts'\nimport { validateGeneratedSkill, validateSkillReview } from './internal/output/validate.ts'\nimport { resolveWithin } from './internal/paths.ts'\nimport { err, ok } from './internal/result.ts'\nimport { collectHostDirectory } from './internal/source/host.ts'\nimport { prepareNpmPackage } from './internal/source/npm.ts'\nimport { DEFAULT_OUTPUT_POLICY, loadSkilldMaintainedSkill } from './skills.ts'\n\ninterface PreparedRun {\n readonly skillName: 'generate-package-skill' | 'generate-project-skill' | 'review-skill'\n readonly outputName: string\n readonly source: PreparedSource\n readonly current?: PreparedSource\n readonly destination?: SkillDestination\n}\n\ninterface ActiveSandbox {\n readonly sandbox: SandboxSession\n readonly workDir: string\n}\n\nfunction cancelled(): SkillRunResult {\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n}\n\nasync function prepareCurrentSkill(destination: SkillDestination, policy: SkillOutputPolicy, signal?: AbortSignal): Promise<Result<PreparedSource | undefined, SkillRunError>> {\n const root = resolve(destination.rootDir)\n const rootStat = await lstat(root).catch(error => error as NodeJS.ErrnoException)\n if (rootStat instanceof Error) {\n if (rootStat.code === 'ENOENT')\n return ok(undefined)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root cannot be inspected.', path: root })\n }\n if (!rootStat.isDirectory() || rootStat.isSymbolicLink())\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root must be a directory, not a symbolic link.', path: root })\n const canonicalRoot = await realpath(root).catch(error => error as Error)\n if (canonicalRoot instanceof Error || canonicalRoot !== root)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output root must not pass through a symbolic link.', path: root })\n\n const target = join(root, destination.name)\n const targetStat = await lstat(target).catch(error => error as NodeJS.ErrnoException)\n if (targetStat instanceof Error) {\n if (targetStat.code === 'ENOENT')\n return ok(undefined)\n return err({ _tag: 'UnsafeOutputPath', message: 'Output path cannot be inspected.', path: target })\n }\n if (!targetStat.isDirectory() || targetStat.isSymbolicLink())\n return err({ _tag: 'UnsafeOutputPath', message: 'Output path must be a directory, not a symbolic link.', path: target })\n return collectHostDirectory(target, policy, 'current Skill', signal)\n}\n\nasync function prepareRun(input: SkillRun, policy: SkillOutputPolicy, fetchClient: FetchClient, signal?: AbortSignal): Promise<Result<PreparedRun, SkillRunError>> {\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n\n const current = input._tag === 'ReviewSkill'\n ? ok(undefined)\n : await prepareCurrentSkill(input.destination, policy, signal)\n if (current._tag === 'Err')\n return current\n\n if (input._tag === 'ProjectSkill') {\n const source = await collectHostDirectory(input.projectDir, policy, input.projectDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-project-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n }\n\n if (input._tag === 'ReviewSkill') {\n const source = await collectHostDirectory(input.skillDir, policy, input.skillDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'review-skill', outputName: 'review', source: source.value })\n }\n\n if (input.source._tag === 'NpmPackage') {\n const source = await prepareNpmPackage(input.source.spec, policy, fetchClient, signal)\n if (signal?.aborted)\n return err({ _tag: 'Cancelled', message: 'Skill run was cancelled.' })\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-package-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n }\n\n const packageDir = resolveWithin(input.source.rootDir, input.source.packageDir)\n if (packageDir === null)\n return err({ _tag: 'InvalidInput', message: 'Local package directory must stay inside its root directory.' })\n const manifest = await lstat(join(packageDir, 'package.json')).catch(error => error as NodeJS.ErrnoException)\n if (manifest instanceof Error || !manifest.isFile() || manifest.isSymbolicLink()) {\n return err({\n _tag: 'SourceUnavailable',\n message: 'Local package directory must contain package.json.',\n attempts: [{ source: packageDir, status: 'skipped', reason: 'package.json is unavailable.' }],\n })\n }\n\n const source = await collectHostDirectory(packageDir, policy, packageDir, signal)\n return source._tag === 'Err'\n ? source\n : ok({ skillName: 'generate-package-skill', outputName: input.destination.name, source: source.value, current: current.value, destination: input.destination })\n}\n\nfunction requestContent(skill: HarnessV1Skill): string {\n const request = skill.files?.find(file => file.path === 'assets/harness-request.md')\n if (!request)\n throw new Error(`Harness request asset is missing for ${skill.name}.`)\n return request.content\n}\n\nfunction renderRequest(template: string, sourcePath: string, currentSkillPath: string, outputPath: string, skillName: string): string {\n return template\n .replaceAll('{{SOURCE_PATH}}', sourcePath)\n .replaceAll('{{CURRENT_SKILL_PATH}}', currentSkillPath)\n .replaceAll('{{OUTPUT_PATH}}', outputPath)\n .replaceAll('{{SKILL_NAME}}', skillName)\n}\n\nasync function writePreparedSource(active: ActiveSandbox, prepared: PreparedRun, signal?: AbortSignal): Promise<void> {\n const sourcePath = posix.join(active.workDir, 'input/source')\n const reset = await active.sandbox.run({\n command: 'rm -rf -- \"$SKILLD_INPUT\" \"$SKILLD_OUTPUT\" && mkdir -p -- \"$SKILLD_INPUT\"',\n env: {\n SKILLD_INPUT: posix.join(active.workDir, 'input'),\n SKILLD_OUTPUT: posix.join(active.workDir, 'skilld-output'),\n },\n abortSignal: signal,\n })\n if (reset.exitCode !== 0)\n throw new Error(reset.stderr.trim() || 'Harness work directory cannot be prepared.')\n for (const file of prepared.source.files) {\n await active.sandbox.writeBinaryFile({\n path: posix.join(sourcePath, file.path),\n content: file.content,\n abortSignal: signal,\n })\n }\n for (const file of prepared.current?.files ?? []) {\n await active.sandbox.writeBinaryFile({\n path: posix.join(active.workDir, 'input/current-skill', file.path),\n content: file.content,\n abortSignal: signal,\n })\n }\n await active.sandbox.writeTextFile({\n path: posix.join(active.workDir, 'input/source-manifest.json'),\n content: `${JSON.stringify({\n sourceAttempts: prepared.source.attempts,\n npmResolution: prepared.source.npmResolution,\n hasCurrentSkill: prepared.current !== undefined,\n }, null, 2)}\\n`,\n abortSignal: signal,\n })\n}\n\nfunction toAgentError(cause: unknown, signal?: AbortSignal): SkillRunResult {\n return signal?.aborted\n ? cancelled()\n : err({ _tag: 'AgentFailed', message: 'Harness Agent failed during the Skill run.', cause })\n}\n\nexport function createSkillHarness(options: CreateSkillHarnessOptions): SkillHarness {\n const policy = parseOutputPolicy({ ...DEFAULT_OUTPUT_POLICY, ...options.outputPolicy })\n const fetchClient: FetchClient = options.fetch ?? globalThis.fetch.bind(globalThis)\n const sandboxConfig = { ...options.sandboxConfig }\n const harness = options.harness\n const sandbox = options.sandbox\n\n return {\n run: async (input, runOptions = {}): Promise<SkillRunResult> => {\n const parsed = parseSkillRun(input)\n if (parsed._tag === 'Err')\n return parsed\n\n const prepared = await prepareRun(parsed.value, policy, fetchClient, runOptions.signal)\n if (prepared._tag === 'Err')\n return prepared\n\n const skill = await loadSkilldMaintainedSkill(prepared.value.skillName)\n let active: ActiveSandbox | undefined\n const userOnSession = sandboxConfig.onSession\n const agent = new HarnessAgent({\n harness,\n sandbox,\n skills: [skill],\n permissionMode: 'allow-all',\n sandboxConfig: {\n ...sandboxConfig,\n onSession: async (sessionOptions) => {\n active = { sandbox: sessionOptions.session, workDir: sessionOptions.sessionWorkDir }\n await writePreparedSource(active, prepared.value, runOptions.signal)\n await userOnSession?.(sessionOptions)\n },\n },\n })\n\n const sessionResult = await agent.createSession({ abortSignal: runOptions.signal }).then(ok, cause => err(cause))\n if (sessionResult._tag === 'Err')\n return toAgentError(sessionResult.error, runOptions.signal)\n const session = sessionResult.value\n\n try {\n if (!active)\n return err({ _tag: 'AgentFailed', message: 'Harness did not provide its sandbox session.' })\n const sourcePath = posix.join(active.workDir, 'input/source')\n const currentSkillPath = posix.join(active.workDir, 'input/current-skill')\n const outputPath = posix.join(active.workDir, 'skilld-output', prepared.value.outputName)\n const prompt = renderRequest(requestContent(skill), sourcePath, currentSkillPath, outputPath, prepared.value.outputName)\n const generated = await agent.generate({ session, prompt, abortSignal: runOptions.signal }).then(ok, cause => err(cause))\n if (generated._tag === 'Err')\n return toAgentError(generated.error, runOptions.signal)\n if (runOptions.signal?.aborted)\n return cancelled()\n\n const collected = await collectSandboxOutput(active.sandbox, outputPath, policy, runOptions.signal)\n if (collected._tag === 'Err')\n return collected\n\n if (prepared.value.skillName === 'review-skill')\n return validateSkillReview(collected.value)\n\n const validated = validateGeneratedSkill(\n prepared.value.outputName,\n collected.value,\n prepared.value.skillName === 'generate-project-skill'\n ? prepared.value.source.files.map(file => file.path)\n : undefined,\n )\n if (validated._tag === 'Err')\n return validated\n if (!prepared.value.destination)\n return err({ _tag: 'InvalidInput', message: 'Skill destination is required.' })\n return promoteSkill(\n prepared.value.destination.rootDir,\n prepared.value.destination.name,\n collected.value,\n prepared.value.source.attempts,\n )\n }\n finally {\n await session.destroy()\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { HarnessV1SandboxProvider } from "@ai-sdk/harness";
2
+ /**
3
+ * The local sandbox runs real processes on this computer.
4
+ * It is a working directory and a port, not a security boundary.
5
+ * Use a hosted sandbox provider when the Harness must contain what it runs.
6
+ */
7
+ export interface CreateLocalSandboxOptions {
8
+ /** Session root directory. The default is a new temporary directory. */
9
+ readonly root?: string;
10
+ /** Bridge port. The default asks the operating system for a free port. */
11
+ readonly port?: number;
12
+ /** Keep the session root after `destroy`. The default removes it. */
13
+ readonly keepRoot?: boolean;
14
+ }
15
+ /**
16
+ * Create a sandbox provider that runs Harness sessions on this computer.
17
+ *
18
+ * The session needs POSIX `sh` at `/bin/sh` and GNU `find`, because the Harness
19
+ * inventories output with `find -printf`. It runs on Linux, and not on macOS or
20
+ * Windows. It exposes one port on `127.0.0.1` for bridge-backed Harness
21
+ * adapters. It applies no isolation:
22
+ * every process reaches the whole computer and the caller's environment.
23
+ */
24
+ export declare function createLocalSandbox(options?: CreateLocalSandboxOptions): HarnessV1SandboxProvider;
25
+ //# sourceMappingURL=sandbox-local.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-local.d.mts","names":[],"sources":["../src/sandbox-local.ts"],"mappings":";;;;;;iBAciB;;WAEN;;WAEA;;WAEA;;;;;;;;;;;wBAoOK,mBAAmB,UAAS,4BAAiC"}
@@ -0,0 +1,197 @@
1
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { Readable } from "node:stream";
4
+ import { spawn } from "node:child_process";
5
+ import { createServer } from "node:net";
6
+ import { constants, tmpdir } from "node:os";
7
+ const { signals } = constants;
8
+ function killProcessGroup(pid) {
9
+ try {
10
+ process.kill(-pid, "SIGTERM");
11
+ } catch (cause) {
12
+ if (errorCode(cause) !== "ESRCH") throw cause;
13
+ }
14
+ }
15
+ function killChild(child) {
16
+ if (child.pid === void 0) return;
17
+ try {
18
+ killProcessGroup(child.pid);
19
+ } catch {
20
+ if (child.exitCode === null) child.kill("SIGTERM");
21
+ }
22
+ }
23
+ function errorCode(cause) {
24
+ return cause?.code;
25
+ }
26
+ async function freePort() {
27
+ return new Promise((resolve, reject) => {
28
+ const server = createServer();
29
+ server.once("error", reject);
30
+ server.listen(0, "127.0.0.1", () => {
31
+ const address = server.address();
32
+ if (address === null || typeof address === "string") {
33
+ server.close(() => reject(/* @__PURE__ */ new Error("The operating system did not report a free port.")));
34
+ return;
35
+ }
36
+ server.close(() => resolve(address.port));
37
+ });
38
+ });
39
+ }
40
+ async function readStream(stream) {
41
+ const reader = stream.getReader();
42
+ const chunks = [];
43
+ while (true) {
44
+ const result = await reader.read();
45
+ if (result.done) break;
46
+ chunks.push(result.value);
47
+ }
48
+ return Buffer.concat(chunks).toString("utf8");
49
+ }
50
+ function watchAbort(child, abortSignal) {
51
+ return new Promise((_, reject) => {
52
+ const onAbort = () => {
53
+ killChild(child);
54
+ reject(abortSignal.reason ?? /* @__PURE__ */ new Error("The sandbox process was aborted."));
55
+ };
56
+ if (abortSignal.aborted) {
57
+ onAbort();
58
+ return;
59
+ }
60
+ abortSignal.addEventListener("abort", onAbort, { once: true });
61
+ child.once("close", () => abortSignal.removeEventListener("abort", onAbort));
62
+ });
63
+ }
64
+ function startProcess(options, root, processes) {
65
+ const child = spawn("/bin/sh", ["-c", options.command], {
66
+ cwd: options.workingDirectory ?? root,
67
+ env: {
68
+ ...process.env,
69
+ ...options.env
70
+ },
71
+ detached: true
72
+ });
73
+ processes.live.add(child);
74
+ if (child.pid !== void 0) processes.groups.add(child.pid);
75
+ const exited = new Promise((resolve) => {
76
+ child.once("close", (code, signal) => {
77
+ processes.live.delete(child);
78
+ resolve({ exitCode: code ?? (signal === null ? 1 : 128 + (signals[signal] ?? 0)) });
79
+ });
80
+ });
81
+ const kill = async () => {
82
+ killChild(child);
83
+ await exited;
84
+ };
85
+ const aborted = options.abortSignal === void 0 ? void 0 : watchAbort(child, options.abortSignal);
86
+ aborted?.catch(() => {});
87
+ return {
88
+ exited,
89
+ handle: {
90
+ pid: child.pid,
91
+ stdout: Readable.toWeb(child.stdout),
92
+ stderr: Readable.toWeb(child.stderr),
93
+ wait: () => aborted === void 0 ? exited : Promise.race([exited, aborted]),
94
+ kill
95
+ }
96
+ };
97
+ }
98
+ function createSandboxSession(root, processes) {
99
+ const readBinaryFile = async ({ path }) => readFile(path).then((value) => Uint8Array.from(value), (cause) => errorCode(cause) === "ENOENT" ? null : Promise.reject(cause));
100
+ return {
101
+ description: `Local sandbox rooted at ${root}. POSIX sh, real processes, no isolation.`,
102
+ readBinaryFile,
103
+ async readFile({ path }) {
104
+ const content = await readBinaryFile({ path });
105
+ if (content === null) return null;
106
+ return new ReadableStream({ start(controller) {
107
+ controller.enqueue(content);
108
+ controller.close();
109
+ } });
110
+ },
111
+ async readTextFile({ path, encoding = "utf8", startLine, endLine }) {
112
+ const content = await readFile(path, { encoding }).catch((cause) => errorCode(cause) === "ENOENT" ? null : Promise.reject(cause));
113
+ if (content === null || startLine === void 0 && endLine === void 0) return content;
114
+ const lines = content.split("\n");
115
+ return lines.slice((startLine ?? 1) - 1, endLine ?? lines.length).join("\n");
116
+ },
117
+ async writeFile({ path, content }) {
118
+ await mkdir(dirname(path), { recursive: true });
119
+ await writeFile(path, Buffer.from(await readStream(content), "utf8"));
120
+ },
121
+ async writeBinaryFile({ path, content }) {
122
+ await mkdir(dirname(path), { recursive: true });
123
+ await writeFile(path, content);
124
+ },
125
+ async writeTextFile({ path, content, encoding = "utf8" }) {
126
+ await mkdir(dirname(path), { recursive: true });
127
+ await writeFile(path, content, { encoding });
128
+ },
129
+ async spawn(options) {
130
+ return startProcess(options, root, processes).handle;
131
+ },
132
+ async run(options) {
133
+ const { handle, exited } = startProcess(options, root, processes);
134
+ const [stdout, stderr, exit] = await Promise.all([
135
+ readStream(handle.stdout),
136
+ readStream(handle.stderr),
137
+ exited
138
+ ]);
139
+ return {
140
+ exitCode: exit.exitCode,
141
+ stdout,
142
+ stderr
143
+ };
144
+ }
145
+ };
146
+ }
147
+ function createLocalSandbox(options = {}) {
148
+ return {
149
+ specificationVersion: "harness-sandbox-v1",
150
+ providerId: "skilld-local",
151
+ async createSession() {
152
+ const root = options.root ?? await mkdtemp(join(tmpdir(), "skilld-local-sandbox-"));
153
+ await mkdir(root, { recursive: true });
154
+ const processes = {
155
+ live: /* @__PURE__ */ new Set(),
156
+ groups: /* @__PURE__ */ new Set()
157
+ };
158
+ const session = createSandboxSession(root, processes);
159
+ let ports = [options.port ?? await freePort()];
160
+ const stop = async () => {
161
+ for (const pid of processes.groups) killProcessGroup(pid);
162
+ processes.groups.clear();
163
+ processes.live.clear();
164
+ };
165
+ const endpoint = ({ port, protocol = "http" }) => ({ url: `${protocol}://127.0.0.1:${port}` });
166
+ return {
167
+ ...session,
168
+ id: root,
169
+ defaultWorkingDirectory: root,
170
+ get ports() {
171
+ return ports;
172
+ },
173
+ async getPortEndpoint(request) {
174
+ return endpoint(request);
175
+ },
176
+ async getPortUrl(request) {
177
+ return endpoint(request).url;
178
+ },
179
+ async setPorts(next) {
180
+ ports = [...next];
181
+ },
182
+ stop,
183
+ async destroy() {
184
+ await stop();
185
+ if (options.keepRoot !== true) await rm(root, {
186
+ recursive: true,
187
+ force: true
188
+ });
189
+ },
190
+ restricted: () => session
191
+ };
192
+ }
193
+ };
194
+ }
195
+ export { createLocalSandbox };
196
+
197
+ //# sourceMappingURL=sandbox-local.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-local.mjs","names":["spawnChildProcess"],"sources":["../src/sandbox-local.ts"],"sourcesContent":["import type { HarnessV1NetworkSandboxSession, HarnessV1PortEndpoint, HarnessV1SandboxProvider } from '@ai-sdk/harness'\nimport type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport { spawn as spawnChildProcess } from 'node:child_process'\nimport { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'\nimport { createServer } from 'node:net'\nimport { constants, tmpdir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport { Readable } from 'node:stream'\n\n/**\n * The local sandbox runs real processes on this computer.\n * It is a working directory and a port, not a security boundary.\n * Use a hosted sandbox provider when the Harness must contain what it runs.\n */\nexport interface CreateLocalSandboxOptions {\n /** Session root directory. The default is a new temporary directory. */\n readonly root?: string\n /** Bridge port. The default asks the operating system for a free port. */\n readonly port?: number\n /** Keep the session root after `destroy`. The default removes it. */\n readonly keepRoot?: boolean\n}\n\n/** The file and process surface the Harness hands to Skill code. */\ntype SandboxSession = ReturnType<HarnessV1NetworkSandboxSession['restricted']>\ntype SandboxProcessOptions = Parameters<SandboxSession['run']>[0]\ntype SandboxProcess = Awaited<ReturnType<SandboxSession['spawn']>>\n\nconst { signals } = constants\n\n/** The processes one session started, so that `stop` can reach all of them. */\ninterface SessionProcesses {\n /** Processes that have not closed yet. */\n readonly live: Set<ChildProcessWithoutNullStreams>\n /**\n * Group identifiers, kept after the leader exits. A command like\n * `agent &` leaves the shell dead and the agent alive in the same group.\n */\n readonly groups: Set<number>\n}\n\n/**\n * Terminate every process in a group.\n * A detached child leads a process group that shares its identifier.\n * The kernel holds the identifier while the group has a member, so it stays\n * ours to signal even after the leader exits.\n */\nfunction killProcessGroup(pid: number): void {\n try {\n process.kill(-pid, 'SIGTERM')\n }\n catch (cause) {\n // ESRCH means the group already exited. Anything else is unexpected.\n if (errorCode(cause) !== 'ESRCH')\n throw cause\n }\n}\n\n/** Terminate a child's group, and the child alone if it leads no group. */\nfunction killChild(child: ChildProcessWithoutNullStreams): void {\n if (child.pid === undefined)\n return\n try {\n killProcessGroup(child.pid)\n }\n catch {\n if (child.exitCode === null)\n child.kill('SIGTERM')\n }\n}\n\nfunction errorCode(cause: unknown): string | undefined {\n return (cause as NodeJS.ErrnoException | undefined)?.code\n}\n\nasync function freePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer()\n server.once('error', reject)\n server.listen(0, '127.0.0.1', () => {\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close(() => reject(new Error('The operating system did not report a free port.')))\n return\n }\n server.close(() => resolve(address.port))\n })\n })\n}\n\nasync function readStream(stream: ReadableStream<Uint8Array>): Promise<string> {\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n while (true) {\n const result = await reader.read()\n if (result.done)\n break\n chunks.push(result.value)\n }\n return Buffer.concat(chunks).toString('utf8')\n}\n\ninterface StartedProcess {\n readonly handle: SandboxProcess\n /** Resolves with the exit code even when the caller aborted the process. */\n readonly exited: Promise<{ exitCode: number }>\n}\n\n/**\n * Kill the process when the caller aborts, and reject the returned promise.\n * The listener is removed on close so that a shared signal does not collect\n * one listener for every process the session runs.\n */\nfunction watchAbort(child: ChildProcessWithoutNullStreams, abortSignal: AbortSignal): Promise<never> {\n return new Promise<never>((_, reject) => {\n const onAbort = (): void => {\n killChild(child)\n reject(abortSignal.reason ?? new Error('The sandbox process was aborted.'))\n }\n if (abortSignal.aborted) {\n onAbort()\n return\n }\n abortSignal.addEventListener('abort', onAbort, { once: true })\n child.once('close', () => abortSignal.removeEventListener('abort', onAbort))\n })\n}\n\nfunction startProcess(\n options: SandboxProcessOptions,\n root: string,\n processes: SessionProcesses,\n): StartedProcess {\n // The process leads its own group so that killing it also kills what it\n // started. The OpenCode bridge starts OpenCode, which starts more processes.\n const child = spawnChildProcess('/bin/sh', ['-c', options.command], {\n cwd: options.workingDirectory ?? root,\n env: { ...process.env, ...options.env },\n detached: true,\n })\n processes.live.add(child)\n if (child.pid !== undefined)\n processes.groups.add(child.pid)\n\n const exited = new Promise<{ exitCode: number }>((resolve) => {\n child.once('close', (code, signal) => {\n processes.live.delete(child)\n resolve({ exitCode: code ?? (signal === null ? 1 : 128 + (signals[signal] ?? 0)) })\n })\n })\n\n const kill = async (): Promise<void> => {\n killChild(child)\n await exited\n }\n\n const aborted = options.abortSignal === undefined\n ? undefined\n : watchAbort(child, options.abortSignal)\n // The abort rejection is reported through `wait`. Nothing else observes it.\n aborted?.catch(() => {})\n\n return {\n exited,\n handle: {\n pid: child.pid,\n stdout: Readable.toWeb(child.stdout) as ReadableStream<Uint8Array>,\n stderr: Readable.toWeb(child.stderr) as ReadableStream<Uint8Array>,\n wait: () => aborted === undefined ? exited : Promise.race([exited, aborted]),\n kill,\n },\n }\n}\n\nfunction createSandboxSession(root: string, processes: SessionProcesses): SandboxSession {\n const readBinaryFile = async ({ path }: { path: string }): Promise<Uint8Array | null> =>\n readFile(path).then(\n value => Uint8Array.from(value),\n cause => errorCode(cause) === 'ENOENT' ? null : Promise.reject(cause),\n )\n\n return {\n description: `Local sandbox rooted at ${root}. POSIX sh, real processes, no isolation.`,\n readBinaryFile,\n\n async readFile({ path }) {\n const content = await readBinaryFile({ path })\n if (content === null)\n return null\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(content)\n controller.close()\n },\n })\n },\n\n async readTextFile({ path, encoding = 'utf8', startLine, endLine }) {\n const content = await readFile(path, { encoding: encoding as BufferEncoding })\n .catch(cause => errorCode(cause) === 'ENOENT' ? null : Promise.reject(cause))\n if (content === null || (startLine === undefined && endLine === undefined))\n return content\n const lines = content.split('\\n')\n return lines.slice((startLine ?? 1) - 1, endLine ?? lines.length).join('\\n')\n },\n\n async writeFile({ path, content }) {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, Buffer.from(await readStream(content), 'utf8'))\n },\n\n async writeBinaryFile({ path, content }) {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, content)\n },\n\n async writeTextFile({ path, content, encoding = 'utf8' }) {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, content, { encoding: encoding as BufferEncoding })\n },\n\n async spawn(options) {\n return startProcess(options, root, processes).handle\n },\n\n async run(options) {\n // An aborted `run` reports the exit code of the killed process.\n // Only `spawn` rejects its wait, because its caller holds the handle.\n const { handle, exited } = startProcess(options, root, processes)\n const [stdout, stderr, exit] = await Promise.all([\n readStream(handle.stdout),\n readStream(handle.stderr),\n exited,\n ])\n return { exitCode: exit.exitCode, stdout, stderr }\n },\n }\n}\n\n/**\n * Create a sandbox provider that runs Harness sessions on this computer.\n *\n * The session needs POSIX `sh` at `/bin/sh` and GNU `find`, because the Harness\n * inventories output with `find -printf`. It runs on Linux, and not on macOS or\n * Windows. It exposes one port on `127.0.0.1` for bridge-backed Harness\n * adapters. It applies no isolation:\n * every process reaches the whole computer and the caller's environment.\n */\nexport function createLocalSandbox(options: CreateLocalSandboxOptions = {}): HarnessV1SandboxProvider {\n return {\n specificationVersion: 'harness-sandbox-v1',\n providerId: 'skilld-local',\n\n async createSession(): Promise<HarnessV1NetworkSandboxSession> {\n const root = options.root ?? await mkdtemp(join(tmpdir(), 'skilld-local-sandbox-'))\n await mkdir(root, { recursive: true })\n const processes: SessionProcesses = { live: new Set(), groups: new Set() }\n const session = createSandboxSession(root, processes)\n let ports: ReadonlyArray<number> = [options.port ?? await freePort()]\n\n const stop = async (): Promise<void> => {\n // Groups, not live children: a backgrounded process outlives its shell.\n for (const pid of processes.groups)\n killProcessGroup(pid)\n processes.groups.clear()\n processes.live.clear()\n }\n\n const endpoint = ({ port, protocol = 'http' }: { port: number, protocol?: 'http' | 'https' | 'ws' }): HarnessV1PortEndpoint =>\n ({ url: `${protocol}://127.0.0.1:${port}` })\n\n return {\n ...session,\n id: root,\n defaultWorkingDirectory: root,\n get ports() {\n return ports\n },\n async getPortEndpoint(request) {\n return endpoint(request)\n },\n async getPortUrl(request) {\n return endpoint(request).url\n },\n async setPorts(next) {\n ports = [...next]\n },\n stop,\n async destroy() {\n await stop()\n if (options.keepRoot !== true)\n await rm(root, { recursive: true, force: true })\n },\n restricted: () => session,\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -25,6 +25,8 @@ Ask for the destination only when the request does not provide one.
25
25
 
26
26
  For an npm package, use the installed or prepared package source first.
27
27
  Use current official documentation when the prepared source lacks required details.
28
+ Read a current Skill for the advice it proves, never for its file layout.
29
+ Decide the layout from the package, then delete any file the new `SKILL.md` does not link.
28
30
  For each version-specific rule, cite the source path or official documentation URL.
29
31
  Use `path:line` citations for prepared source when line numbers add value.
30
32
 
@@ -35,6 +37,14 @@ The directory must contain `SKILL.md`.
35
37
  Put detailed or conditional material in `references/`.
36
38
  Put reusable commands or code in `scripts/` when execution adds value.
37
39
 
40
+ Keep `SKILL.md` under 500 lines.
41
+ Write a reference file only when `SKILL.md` links it.
42
+ Give each reference file one topic, such as an API surface or a migration.
43
+ Write at most eight reference files.
44
+
45
+ Never copy release notes, changelogs, issues, or discussions into a file.
46
+ Cite them by URL instead.
47
+
38
48
  The `SKILL.md` frontmatter must contain only:
39
49
 
40
50
  ```yaml
@@ -24,14 +24,28 @@ Ask for the destination only when the request does not provide one.
24
24
 
25
25
  ## Project navigation
26
26
 
27
- 1. Identify the project name, purpose, key files, and main directories.
28
- 2. Use project-relative paths for every file pointer.
29
- 3. Add focused search commands for source and documentation.
30
- 4. Keep search results linked to the real project files.
27
+ The Skill must let an Agent find any part of the project without a prior index.
28
+
29
+ 1. Name the project from its manifest. Use the directory name when no manifest names it.
30
+ 2. List the entry points the manifest declares, such as `main`, `module`, `types`, `exports`, and `bin`.
31
+ 3. List the source and documentation directories an Agent reads most.
32
+ 4. Use project-relative paths for every file pointer.
31
33
  5. Never add a generated directory prefix to a project-relative path.
32
- 6. Tell the Agent when project changes require a new Skill run.
34
+ 6. Give search commands the Agent can repeat, scoped to those directories.
35
+ 7. Prefer `rg` for search. Name the directories to skip in the command.
36
+ 8. Tell the Agent when project changes require a new Skill run.
37
+
38
+ Write the search commands so their results point at real project files.
39
+
40
+ Skip these paths while collecting project files:
41
+
42
+ - version-control data, such as `.git`
43
+ - dependency directories, such as `node_modules` and `vendor`
44
+ - generated output, such as `dist`, `build`, `target`, `.output`, `.nuxt`, and `.next`
45
+ - caches and reports, such as `coverage`
46
+ - credential files, such as `.env` and any private key
33
47
 
34
- Ignore dependency directories, version-control data, generated output, caches, and credentials.
48
+ Skip any file larger than 512 KB.
35
49
  Do not follow symbolic links while collecting project files.
36
50
 
37
51
  ## Output
@@ -75,6 +75,30 @@ A `verified` status covers where the Skill came from.
75
75
  It does not cover what the instructions ask you to do.
76
76
  If the status is `unverified`, tell the user before you follow the Skill.
77
77
 
78
+ ## Author a Skill from the user's own code
79
+
80
+ The skilld CLI has no generation command.
81
+ Authoring lives in skilld-maintained Skills that you run.
82
+
83
+ Run the Skill that matches the request:
84
+
85
+ ```sh
86
+ skilld run skilld-dev/skilld/generate-project-skill --json
87
+ skilld run skilld-dev/skilld/generate-package-skill --json
88
+ skilld run skilld-dev/skilld/review-skill --json
89
+ ```
90
+
91
+ Use `generate-project-skill` for the current project or workspace.
92
+ Use `generate-package-skill` for a package the user maintains.
93
+ Use `review-skill` before the user publishes a Skill.
94
+
95
+ Read the printed SKILL.md, then follow it for this project.
96
+ Write the draft where the user asks, and show the files for review.
97
+ Do not replace an existing Skill until the user approves the draft.
98
+ Do not install or publish a draft the user has not read.
99
+
100
+ The user owns the draft. They commit it to their own Repository.
101
+
78
102
  ## List the Skills a Repository, curator, or collection names
79
103
 
80
104
  skilld.dev prints refs that name several Skills:
@@ -157,6 +181,8 @@ skilld add @LOGIN/SLUG --global
157
181
 
158
182
  `skilld add` accepts `--global`, `--agent`, and `--mode` like `skilld install`.
159
183
  It prints one `Installed Skill` line per Skill.
184
+ An Agent run installs every Skill the ref names. Pass `--all` to state that intent.
185
+ A person at a terminal is asked which Skills to install.
160
186
  Run `skilld run` with the same ref first, then confirm the list with the user.
161
187
  `skilld add` with one Skill selector installs that Skill like `skilld install`.
162
188
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "skilld-harness",
3
3
  "type": "module",
4
- "version": "3.1.0",
4
+ "version": "3.2.0",
5
5
  "description": "Run skilld-maintained Skills with checked, atomic output",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -32,6 +32,10 @@
32
32
  "types": "./dist/index.d.mts",
33
33
  "import": "./dist/index.mjs"
34
34
  },
35
+ "./sandbox-local": {
36
+ "types": "./dist/sandbox-local.d.mts",
37
+ "import": "./dist/sandbox-local.mjs"
38
+ },
35
39
  "./skills": {
36
40
  "types": "./dist/skills.d.mts",
37
41
  "import": "./dist/skills.mjs"