Author SHA1 Message Date
oleg-lukasonokandClaude Code 9b84db4b0a Fix skill origin metadata and macOS source-importer portability
Point SKILL.md origin-repository/origin-path at the renamed
diagrams-drawio repository and workspace path.

openConfined() and the directory walker addressed opened files through
/proc/self/fd, which only exists on Linux, so every file/directory source
import failed on macOS (2 of 150 tests). Keep the procfs path where it is
available and fall back to realpath() plus a dev/inode identity check
against the open handle elsewhere.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-06 12:58:12 +03:00
jarvis-at-skic 9809cf73e9 Merge pull request 'refactor: rename skill to diagrams-drawio' (#3) from refactor/rename-diagrams-drawio into test 2026-09-03 13:11:25 -07:00
2 changed files with 39 additions and 9 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ metadata:
author: workspace-swiss-knife
version: "2.0"
spec: agentskills.io/specification
origin-repository: git@github.ibm.com:CTOTools-skills-code-agent/drawio-main.git
origin-path: $HOME/projects-ibm/cognitive-architect/workspace-skills-code-agent/drawio-main
origin-repository: git@github.ibm.com:CTOTools-skills-code-agent/diagrams-drawio.git
origin-path: $HOME/projects-skills-code-agent/ws-skills-code-agent/diagrams-drawio
repository: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/diagrams-drawio
compatibility: Designed for Cline, Claude Code, GitHub Copilot, OpenAI Codex, and other compatible agent environments
---
+37 -7
View File
@@ -1,5 +1,5 @@
import { constants } from "node:fs";
import { open, readdir, realpath } from "node:fs/promises";
import { open, readdir, realpath, stat } from "node:fs/promises";
import type { FileHandle } from "node:fs/promises";
import { isAbsolute, relative, resolve } from "node:path";
@@ -643,7 +643,7 @@ async function openConfined(candidate: string, root: string): Promise<{ handle:
throw error;
}
try {
const canonical = await realpath(`/proc/self/fd/${handle.fd}`);
const canonical = await canonicalPathOfOpenHandle(handle, candidate);
if (!isConfined(root, canonical)) throw new Error("Opened source escapes outside declared root");
return { handle, canonical };
} catch (error) {
@@ -652,6 +652,35 @@ async function openConfined(candidate: string, root: string): Promise<{ handle:
}
}
let procFdAvailable: boolean | undefined;
/** Path to address an open directory by: its `/proc/self/fd/N` entry on Linux, else its verified canonical path. */
async function descriptorPath(handle: FileHandle, canonical: string): Promise<string> {
if (procFdAvailable === undefined) {
try { await realpath(`/proc/self/fd/${handle.fd}`); procFdAvailable = true; } catch { procFdAvailable = false; }
}
return procFdAvailable ? `/proc/self/fd/${handle.fd}` : canonical;
}
/**
* Resolve the canonical path of an already-opened file. On Linux `/proc/self/fd/N` answers for the
* exact descriptor; on macOS/BSD (no procfs) fall back to resolving the candidate path and proving it
* names the same inode as the open handle, so a swap between open() and realpath() is still rejected.
*/
async function canonicalPathOfOpenHandle(handle: FileHandle, candidate: string): Promise<string> {
try {
return await realpath(`/proc/self/fd/${handle.fd}`);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
const canonical = await realpath(candidate);
const [opened, resolved] = await Promise.all([handle.stat(), stat(canonical)]);
if (opened.dev !== resolved.dev || opened.ino !== resolved.ino) {
throw new Error("Opened source does not match its canonical path");
}
return canonical;
}
async function readBounded(handle: FileHandle, limit: number, directory: boolean): Promise<Buffer> {
const initial = await handle.stat();
if (!initial.isFile()) throw new Error("Source entry must be a regular file");
@@ -711,12 +740,13 @@ export async function importSource(request: SourceImportRequest): Promise<Source
const results: SourceImportResult[] = [];
let consumed = 0;
const walk = async (directory: FileHandle): Promise<void> => {
for (const name of (await readdir(`/proc/self/fd/${directory.fd}`)).sort(codePointCompare)) {
const opened = await openConfined(resolve(`/proc/self/fd/${directory.fd}`, name), rootPath);
const walk = async (directory: { handle: FileHandle; canonical: string }): Promise<void> => {
const base = await descriptorPath(directory.handle, directory.canonical);
for (const name of (await readdir(base)).sort(codePointCompare)) {
const opened = await openConfined(resolve(base, name), rootPath);
try {
const entryInfo = await opened.handle.stat();
if (entryInfo.isDirectory()) await walk(opened.handle);
if (entryInfo.isDirectory()) await walk(opened);
else if (entryInfo.isFile()) {
const dot = name.lastIndexOf("."); const extension = dot >= 0 ? name.slice(dot).toLowerCase() : "";
if (extensions[request.sourceKind].has(extension)) {
@@ -729,7 +759,7 @@ export async function importSource(request: SourceImportRequest): Promise<Source
} finally { await opened.handle.close(); }
}
};
try { await walk(source.handle); }
try { await walk(source); }
finally { await source.handle.close(); }
if (results.length === 0) throw new Error(`No supported ${request.sourceKind} source files found`);
const nodeById = new Map<string, DiagramNode>(); const edgeById = new Map<string, DiagramEdge>(); const diagnostics: ImportDiagnostic[] = [];