@kyo-so/cli 0.8.0 → 0.9.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/.agents/skills/kyoso-review/SKILL.md +8 -5
- package/.agents/skills/kyoso-review/agents/openai.yaml +0 -7
- package/CHANGELOG.md +41 -0
- package/README.ja.md +63 -7
- package/README.md +63 -7
- package/README.zh-CN.md +63 -7
- package/dist/audit/safeTraceFile.d.ts +20 -0
- package/dist/audit/stateRoot.d.ts +38 -0
- package/dist/audit/trace.d.ts +15 -3
- package/dist/bin/kyoso.js +2582 -493
- package/dist/cli/codexPluginDetector.d.ts +70 -0
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/integration.d.ts +30 -0
- package/dist/cli/knownSkillDigests.d.ts +11 -0
- package/dist/cli/pluginRuntimeContract.d.ts +232 -0
- package/dist/cli/setup.d.ts +24 -4
- package/dist/cli/skillInstall.d.ts +16 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/runReview.d.ts +2 -0
- package/dist/index.js +627 -115
- package/dist/utils/pathContainment.d.ts +1 -0
- package/package.json +9 -2
package/dist/bin/kyoso.js
CHANGED
|
@@ -7587,7 +7587,7 @@ ${lanes.join(`
|
|
|
7587
7587
|
getCurrentDirectory,
|
|
7588
7588
|
getAccessibleSortedChildDirectories,
|
|
7589
7589
|
fileSystemEntryExists,
|
|
7590
|
-
realpath,
|
|
7590
|
+
realpath: realpath2,
|
|
7591
7591
|
setTimeout: setTimeout2,
|
|
7592
7592
|
clearTimeout: clearTimeout2
|
|
7593
7593
|
}) {
|
|
@@ -7763,13 +7763,13 @@ ${lanes.join(`
|
|
|
7763
7763
|
const parentWatcher = cache.get(parentDirPath);
|
|
7764
7764
|
if (!parentWatcher)
|
|
7765
7765
|
return false;
|
|
7766
|
-
const target = normalizePath(
|
|
7766
|
+
const target = normalizePath(realpath2(parentDir));
|
|
7767
7767
|
let hasChanges;
|
|
7768
7768
|
let newChildWatches;
|
|
7769
7769
|
if (filePathComparer(target, parentDir) === 0) {
|
|
7770
7770
|
hasChanges = enumerateInsertsAndDeletes(fileSystemEntryExists(parentDir, 1) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), (child) => {
|
|
7771
7771
|
const childFullName = getNormalizedAbsolutePath(child, parentDir);
|
|
7772
|
-
return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(
|
|
7772
|
+
return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath2(childFullName))) === 0 ? childFullName : undefined;
|
|
7773
7773
|
}) : emptyArray, parentWatcher.childWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), createAndAddChildDirectoryWatcher, closeFileWatcher, addChildDirectoryWatcher);
|
|
7774
7774
|
} else if (parentWatcher.targetWatcher && filePathComparer(target, parentWatcher.targetWatcher.dirName) === 0) {
|
|
7775
7775
|
hasChanges = false;
|
|
@@ -7843,7 +7843,7 @@ ${lanes.join(`
|
|
|
7843
7843
|
getCurrentDirectory,
|
|
7844
7844
|
fsSupportsRecursiveFsWatch,
|
|
7845
7845
|
getAccessibleSortedChildDirectories,
|
|
7846
|
-
realpath,
|
|
7846
|
+
realpath: realpath2,
|
|
7847
7847
|
tscWatchFile,
|
|
7848
7848
|
useNonPollingWatchers,
|
|
7849
7849
|
tscWatchDirectory,
|
|
@@ -7928,7 +7928,7 @@ ${lanes.join(`
|
|
|
7928
7928
|
fileSystemEntryExists,
|
|
7929
7929
|
getAccessibleSortedChildDirectories,
|
|
7930
7930
|
watchDirectory: nonRecursiveWatchDirectory,
|
|
7931
|
-
realpath,
|
|
7931
|
+
realpath: realpath2,
|
|
7932
7932
|
setTimeout: setTimeout2,
|
|
7933
7933
|
clearTimeout: clearTimeout2
|
|
7934
7934
|
});
|
|
@@ -8104,7 +8104,7 @@ ${lanes.join(`
|
|
|
8104
8104
|
fileSystemEntryExists,
|
|
8105
8105
|
fsSupportsRecursiveFsWatch,
|
|
8106
8106
|
getAccessibleSortedChildDirectories: (path) => getAccessibleFileSystemEntries(path).directories,
|
|
8107
|
-
realpath,
|
|
8107
|
+
realpath: realpath2,
|
|
8108
8108
|
tscWatchFile: process.env.TSC_WATCHFILE,
|
|
8109
8109
|
useNonPollingWatchers: !!process.env.TSC_NONPOLLING_WATCHER,
|
|
8110
8110
|
tscWatchDirectory: process.env.TSC_WATCHDIRECTORY,
|
|
@@ -8178,7 +8178,7 @@ ${lanes.join(`
|
|
|
8178
8178
|
enableCPUProfiler,
|
|
8179
8179
|
disableCPUProfiler,
|
|
8180
8180
|
cpuProfilingEnabled: () => !!activeSession || contains(process.execArgv, "--cpu-prof") || contains(process.execArgv, "--prof"),
|
|
8181
|
-
realpath,
|
|
8181
|
+
realpath: realpath2,
|
|
8182
8182
|
debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(process.execArgv, (arg) => /^--(?:inspect|debug)(?:-brk)?(?:=\d+)?$/i.test(arg)) || !!process.recordreplay,
|
|
8183
8183
|
tryEnableSourceMapsForHost() {
|
|
8184
8184
|
try {
|
|
@@ -8391,7 +8391,7 @@ ${lanes.join(`
|
|
|
8391
8391
|
}
|
|
8392
8392
|
}
|
|
8393
8393
|
function readDirectory(path, extensions, excludes, includes, depth) {
|
|
8394
|
-
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries,
|
|
8394
|
+
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath2);
|
|
8395
8395
|
}
|
|
8396
8396
|
function fileSystemEntryExists(path, entryKind) {
|
|
8397
8397
|
const stat = statSync(path);
|
|
@@ -8419,7 +8419,7 @@ ${lanes.join(`
|
|
|
8419
8419
|
function fsRealPathHandlingLongPath(path) {
|
|
8420
8420
|
return path.length < 260 ? _fs.realpathSync.native(path) : _fs.realpathSync(path);
|
|
8421
8421
|
}
|
|
8422
|
-
function
|
|
8422
|
+
function realpath2(path) {
|
|
8423
8423
|
try {
|
|
8424
8424
|
return fsRealpath(path);
|
|
8425
8425
|
} catch {
|
|
@@ -19724,18 +19724,18 @@ ${lanes.join(`
|
|
|
19724
19724
|
diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
|
|
19725
19725
|
}, sourceFiles, data);
|
|
19726
19726
|
}
|
|
19727
|
-
function ensureDirectoriesExist(directoryPath,
|
|
19727
|
+
function ensureDirectoriesExist(directoryPath, createDirectory2, directoryExists) {
|
|
19728
19728
|
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
|
|
19729
19729
|
const parentDirectory = getDirectoryPath(directoryPath);
|
|
19730
|
-
ensureDirectoriesExist(parentDirectory,
|
|
19731
|
-
|
|
19730
|
+
ensureDirectoriesExist(parentDirectory, createDirectory2, directoryExists);
|
|
19731
|
+
createDirectory2(directoryPath);
|
|
19732
19732
|
}
|
|
19733
19733
|
}
|
|
19734
|
-
function writeFileEnsuringDirectories(path, data, writeByteOrderMark, writeFile22,
|
|
19734
|
+
function writeFileEnsuringDirectories(path, data, writeByteOrderMark, writeFile22, createDirectory2, directoryExists) {
|
|
19735
19735
|
try {
|
|
19736
19736
|
writeFile22(path, data, writeByteOrderMark);
|
|
19737
19737
|
} catch {
|
|
19738
|
-
ensureDirectoriesExist(getDirectoryPath(normalizePath(path)),
|
|
19738
|
+
ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory2, directoryExists);
|
|
19739
19739
|
writeFile22(path, data, writeByteOrderMark);
|
|
19740
19740
|
}
|
|
19741
19741
|
}
|
|
@@ -21670,7 +21670,7 @@ ${lanes.join(`
|
|
|
21670
21670
|
function getRegexFromPattern(pattern, useCaseSensitiveFileNames2) {
|
|
21671
21671
|
return new RegExp(pattern, useCaseSensitiveFileNames2 ? "" : "i");
|
|
21672
21672
|
}
|
|
21673
|
-
function matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries,
|
|
21673
|
+
function matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath2) {
|
|
21674
21674
|
path = normalizePath(path);
|
|
21675
21675
|
currentDirectory = normalizePath(currentDirectory);
|
|
21676
21676
|
const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames2, currentDirectory);
|
|
@@ -21685,7 +21685,7 @@ ${lanes.join(`
|
|
|
21685
21685
|
}
|
|
21686
21686
|
return flatten(results);
|
|
21687
21687
|
function visitDirectory(path2, absolutePath, depth2) {
|
|
21688
|
-
const canonicalPath = toCanonical(
|
|
21688
|
+
const canonicalPath = toCanonical(realpath2(absolutePath));
|
|
21689
21689
|
if (visited.has(canonicalPath))
|
|
21690
21690
|
return;
|
|
21691
21691
|
visited.set(canonicalPath, true);
|
|
@@ -41674,11 +41674,11 @@ ${lanes.join(`
|
|
|
41674
41674
|
if (i < rootLength) {
|
|
41675
41675
|
return;
|
|
41676
41676
|
}
|
|
41677
|
-
const
|
|
41678
|
-
if (
|
|
41677
|
+
const sep3 = directory.lastIndexOf(directorySeparator, i - 1);
|
|
41678
|
+
if (sep3 === -1) {
|
|
41679
41679
|
return;
|
|
41680
41680
|
}
|
|
41681
|
-
return directory.substr(0, Math.max(
|
|
41681
|
+
return directory.substr(0, Math.max(sep3, rootLength));
|
|
41682
41682
|
}
|
|
41683
41683
|
}
|
|
41684
41684
|
}
|
|
@@ -98915,14 +98915,14 @@ ${lanes.join(`
|
|
|
98915
98915
|
}
|
|
98916
98916
|
}
|
|
98917
98917
|
function createImportCallExpressionAMD(arg, containsLexicalThis) {
|
|
98918
|
-
const
|
|
98918
|
+
const resolve3 = factory2.createUniqueName("resolve");
|
|
98919
98919
|
const reject = factory2.createUniqueName("reject");
|
|
98920
98920
|
const parameters = [
|
|
98921
|
-
factory2.createParameterDeclaration(undefined, undefined,
|
|
98921
|
+
factory2.createParameterDeclaration(undefined, undefined, resolve3),
|
|
98922
98922
|
factory2.createParameterDeclaration(undefined, undefined, reject)
|
|
98923
98923
|
];
|
|
98924
98924
|
const body = factory2.createBlock([
|
|
98925
|
-
factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]),
|
|
98925
|
+
factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve3, reject]))
|
|
98926
98926
|
]);
|
|
98927
98927
|
let func;
|
|
98928
98928
|
if (languageVersion >= 2) {
|
|
@@ -107027,12 +107027,12 @@ ${lanes.join(`
|
|
|
107027
107027
|
directoryExists: host.directoryExists && directoryExists,
|
|
107028
107028
|
getDirectories,
|
|
107029
107029
|
readDirectory,
|
|
107030
|
-
createDirectory: host.createDirectory &&
|
|
107030
|
+
createDirectory: host.createDirectory && createDirectory2,
|
|
107031
107031
|
writeFile: host.writeFile && writeFile22,
|
|
107032
107032
|
addOrDeleteFileOrDirectory,
|
|
107033
107033
|
addOrDeleteFile,
|
|
107034
107034
|
clearCache,
|
|
107035
|
-
realpath: host.realpath &&
|
|
107035
|
+
realpath: host.realpath && realpath2
|
|
107036
107036
|
};
|
|
107037
107037
|
function toPath3(fileName) {
|
|
107038
107038
|
return toPath(fileName, currentDirectory, getCanonicalFileName);
|
|
@@ -107104,7 +107104,7 @@ ${lanes.join(`
|
|
|
107104
107104
|
const path = toPath3(dirPath);
|
|
107105
107105
|
return cachedReadDirectoryResult.has(ensureTrailingDirectorySeparator(path)) || host.directoryExists(dirPath);
|
|
107106
107106
|
}
|
|
107107
|
-
function
|
|
107107
|
+
function createDirectory2(dirPath) {
|
|
107108
107108
|
const path = toPath3(dirPath);
|
|
107109
107109
|
const result = getCachedFileSystemEntriesForBaseDir(path);
|
|
107110
107110
|
if (result) {
|
|
@@ -107130,7 +107130,7 @@ ${lanes.join(`
|
|
|
107130
107130
|
const rootResult = tryReadDirectory2(rootDir, rootDirPath);
|
|
107131
107131
|
let rootSymLinkResult;
|
|
107132
107132
|
if (rootResult !== undefined) {
|
|
107133
|
-
return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries,
|
|
107133
|
+
return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath2);
|
|
107134
107134
|
}
|
|
107135
107135
|
return host.readDirectory(rootDir, extensions, excludes, includes, depth);
|
|
107136
107136
|
function getFileSystemEntries(dir) {
|
|
@@ -107153,7 +107153,7 @@ ${lanes.join(`
|
|
|
107153
107153
|
return result;
|
|
107154
107154
|
}
|
|
107155
107155
|
}
|
|
107156
|
-
function
|
|
107156
|
+
function realpath2(s) {
|
|
107157
107157
|
return host.realpath ? host.realpath(s) : s;
|
|
107158
107158
|
}
|
|
107159
107159
|
function clearFirstAncestorEntry(fileOrDirectoryPath) {
|
|
@@ -107527,11 +107527,11 @@ ${lanes.join(`
|
|
|
107527
107527
|
return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined;
|
|
107528
107528
|
};
|
|
107529
107529
|
}
|
|
107530
|
-
function createWriteFileMeasuringIO(actualWriteFile,
|
|
107530
|
+
function createWriteFileMeasuringIO(actualWriteFile, createDirectory2, directoryExists) {
|
|
107531
107531
|
return (fileName, data, writeByteOrderMark, onError) => {
|
|
107532
107532
|
try {
|
|
107533
107533
|
mark("beforeIOWrite");
|
|
107534
|
-
writeFileEnsuringDirectories(fileName, data, writeByteOrderMark, actualWriteFile,
|
|
107534
|
+
writeFileEnsuringDirectories(fileName, data, writeByteOrderMark, actualWriteFile, createDirectory2, directoryExists);
|
|
107535
107535
|
mark("afterIOWrite");
|
|
107536
107536
|
measure("I/O Write", "beforeIOWrite", "afterIOWrite");
|
|
107537
107537
|
} catch (e) {
|
|
@@ -107558,7 +107558,7 @@ ${lanes.join(`
|
|
|
107558
107558
|
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
|
|
107559
107559
|
}
|
|
107560
107560
|
const newLine = getNewLineCharacter(options);
|
|
107561
|
-
const
|
|
107561
|
+
const realpath2 = system.realpath && ((path) => system.realpath(path));
|
|
107562
107562
|
const compilerHost = {
|
|
107563
107563
|
getSourceFile: createGetSourceFile((fileName) => compilerHost.readFile(fileName), setParentNodes),
|
|
107564
107564
|
getDefaultLibLocation,
|
|
@@ -107574,7 +107574,7 @@ ${lanes.join(`
|
|
|
107574
107574
|
directoryExists: (directoryName) => system.directoryExists(directoryName),
|
|
107575
107575
|
getEnvironmentVariable: (name) => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
|
|
107576
107576
|
getDirectories: (path) => system.getDirectories(path),
|
|
107577
|
-
realpath,
|
|
107577
|
+
realpath: realpath2,
|
|
107578
107578
|
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
|
|
107579
107579
|
createDirectory: (d) => system.createDirectory(d),
|
|
107580
107580
|
createHash: maybeBind(system, system.createHash)
|
|
@@ -108611,10 +108611,10 @@ ${lanes.join(`
|
|
|
108611
108611
|
}
|
|
108612
108612
|
function getDefaultLibFilePriority(a) {
|
|
108613
108613
|
if (containsPath(defaultLibraryPath, a.fileName, false)) {
|
|
108614
|
-
const
|
|
108615
|
-
if (
|
|
108614
|
+
const basename2 = getBaseFileName(a.fileName);
|
|
108615
|
+
if (basename2 === "lib.d.ts" || basename2 === "lib.es6.d.ts")
|
|
108616
108616
|
return 0;
|
|
108617
|
-
const name = removeSuffix(removePrefix(
|
|
108617
|
+
const name = removeSuffix(removePrefix(basename2, "lib."), ".d.ts");
|
|
108618
108618
|
const index = libs.indexOf(name);
|
|
108619
108619
|
if (index !== -1)
|
|
108620
108620
|
return index + 1;
|
|
@@ -160210,9 +160210,9 @@ ${options.prefix}` : `
|
|
|
160210
160210
|
if (this.host.realpath) {
|
|
160211
160211
|
Debug.assert(!!this.containingProjects.length);
|
|
160212
160212
|
const project = this.containingProjects[0];
|
|
160213
|
-
const
|
|
160214
|
-
if (
|
|
160215
|
-
this.realpath = project.toPath(
|
|
160213
|
+
const realpath2 = this.host.realpath(this.path);
|
|
160214
|
+
if (realpath2) {
|
|
160215
|
+
this.realpath = project.toPath(realpath2);
|
|
160216
160216
|
if (this.realpath !== this.path) {
|
|
160217
160217
|
project.projectService.realpathToScriptInfos.add(this.realpath, this);
|
|
160218
160218
|
}
|
|
@@ -161298,8 +161298,8 @@ ${options.prefix}` : `
|
|
|
161298
161298
|
}
|
|
161299
161299
|
};
|
|
161300
161300
|
for (const file2 of files) {
|
|
161301
|
-
const
|
|
161302
|
-
if (
|
|
161301
|
+
const basename2 = getBaseFileName(file2);
|
|
161302
|
+
if (basename2 === "package.json" || basename2 === "bower.json") {
|
|
161303
161303
|
createProjectWatcher(file2, "FileWatcher");
|
|
161304
161304
|
continue;
|
|
161305
161305
|
}
|
|
@@ -163486,9 +163486,9 @@ ${options.prefix}` : `
|
|
|
163486
163486
|
this.filenameToScriptInfo.delete(info.path);
|
|
163487
163487
|
this.filenameToScriptInfoVersion.set(info.path, info.textStorage.version);
|
|
163488
163488
|
this.stopWatchingScriptInfo(info);
|
|
163489
|
-
const
|
|
163490
|
-
if (
|
|
163491
|
-
this.realpathToScriptInfos.remove(
|
|
163489
|
+
const realpath2 = info.getRealpathIfDifferent();
|
|
163490
|
+
if (realpath2) {
|
|
163491
|
+
this.realpathToScriptInfos.remove(realpath2, info);
|
|
163492
163492
|
}
|
|
163493
163493
|
info.closeSourceMapFileWatcher();
|
|
163494
163494
|
}
|
|
@@ -164146,9 +164146,9 @@ All files are: ${JSON.stringify(names)}`, "Err");
|
|
|
164146
164146
|
getSymlinkedProjects(info) {
|
|
164147
164147
|
let projects;
|
|
164148
164148
|
if (this.realpathToScriptInfos) {
|
|
164149
|
-
const
|
|
164150
|
-
if (
|
|
164151
|
-
forEach(this.realpathToScriptInfos.get(
|
|
164149
|
+
const realpath2 = info.getRealpathIfDifferent();
|
|
164150
|
+
if (realpath2) {
|
|
164151
|
+
forEach(this.realpathToScriptInfos.get(realpath2), combineProjects);
|
|
164152
164152
|
}
|
|
164153
164153
|
forEach(this.realpathToScriptInfos.get(info.path), combineProjects);
|
|
164154
164154
|
}
|
|
@@ -164186,8 +164186,8 @@ All files are: ${JSON.stringify(names)}`, "Err");
|
|
|
164186
164186
|
const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
|
|
164187
164187
|
if (!fileOrDirectoryPath)
|
|
164188
164188
|
return;
|
|
164189
|
-
const
|
|
164190
|
-
if (((_a3 = result.affectedModuleSpecifierCacheProjects) == null ? undefined : _a3.size) && (
|
|
164189
|
+
const basename2 = getBaseFileName(fileOrDirectoryPath);
|
|
164190
|
+
if (((_a3 = result.affectedModuleSpecifierCacheProjects) == null ? undefined : _a3.size) && (basename2 === "package.json" || basename2 === "node_modules")) {
|
|
164191
164191
|
result.affectedModuleSpecifierCacheProjects.forEach((project) => {
|
|
164192
164192
|
var _a22;
|
|
164193
164193
|
(_a22 = project.getModuleSpecifierCache()) == null || _a22.clear();
|
|
@@ -169234,8 +169234,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
169234
169234
|
installPackage(options) {
|
|
169235
169235
|
this.packageInstallId++;
|
|
169236
169236
|
const request = { kind: "installPackage", ...options, id: this.packageInstallId };
|
|
169237
|
-
const promise2 = new Promise((
|
|
169238
|
-
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve, reject });
|
|
169237
|
+
const promise2 = new Promise((resolve3, reject) => {
|
|
169238
|
+
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve: resolve3, reject });
|
|
169239
169239
|
});
|
|
169240
169240
|
this.installer.send(request);
|
|
169241
169241
|
return promise2;
|
|
@@ -169495,7 +169495,7 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
169495
169495
|
});
|
|
169496
169496
|
|
|
169497
169497
|
// src/cli/main.ts
|
|
169498
|
-
import { spawnSync as
|
|
169498
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
169499
169499
|
|
|
169500
169500
|
// src/cli/args.ts
|
|
169501
169501
|
function parseArgs(argv) {
|
|
@@ -169599,14 +169599,364 @@ function languageFromPath(path) {
|
|
|
169599
169599
|
}
|
|
169600
169600
|
|
|
169601
169601
|
// src/cli/doctor.ts
|
|
169602
|
-
import { accessSync } from "node:fs";
|
|
169603
|
-
import { delimiter as
|
|
169602
|
+
import { accessSync as accessSync2 } from "node:fs";
|
|
169603
|
+
import { delimiter as delimiter3, resolve as resolve8 } from "node:path";
|
|
169604
|
+
|
|
169605
|
+
// src/audit/stateRoot.ts
|
|
169606
|
+
import { createHash } from "node:crypto";
|
|
169607
|
+
import { lstat, mkdir as mkdir2, realpath } from "node:fs/promises";
|
|
169608
|
+
import { basename, dirname as dirname2, isAbsolute, join, resolve as resolve2 } from "node:path";
|
|
169609
|
+
|
|
169610
|
+
// src/context/pathPolicy.ts
|
|
169611
|
+
import { normalize, sep } from "node:path";
|
|
169612
|
+
|
|
169613
|
+
// src/core/errors.ts
|
|
169614
|
+
class KyosoRequestError extends Error {
|
|
169615
|
+
code;
|
|
169616
|
+
constructor(message, code) {
|
|
169617
|
+
super(message);
|
|
169618
|
+
this.code = code;
|
|
169619
|
+
this.name = "KyosoRequestError";
|
|
169620
|
+
}
|
|
169621
|
+
}
|
|
169622
|
+
|
|
169623
|
+
// src/context/pathPolicy.ts
|
|
169624
|
+
function normalizeRelativePath(path) {
|
|
169625
|
+
const normalized = normalize(path).replaceAll("\\", "/");
|
|
169626
|
+
if (normalized === "." || normalized.startsWith("..") || normalized.includes(`${sep}..${sep}`) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized)) {
|
|
169627
|
+
throw new KyosoRequestError(`Path escapes workspace root: ${path}`, "INVALID_PATH");
|
|
169628
|
+
}
|
|
169629
|
+
return normalized;
|
|
169630
|
+
}
|
|
169631
|
+
function isDeniedPath(path, denyPatterns) {
|
|
169632
|
+
return matchesPathPattern(path, denyPatterns, "deny");
|
|
169633
|
+
}
|
|
169634
|
+
function isAllowedPath(path, allowPatterns) {
|
|
169635
|
+
if (allowPatterns.length === 0)
|
|
169636
|
+
return true;
|
|
169637
|
+
return matchesPathPattern(path, allowPatterns, "allow");
|
|
169638
|
+
}
|
|
169639
|
+
function matchesPathPattern(path, patterns, mode) {
|
|
169640
|
+
const normalized = normalizeRelativePath(path);
|
|
169641
|
+
const segments = normalized.split("/");
|
|
169642
|
+
return patterns.some((pattern) => {
|
|
169643
|
+
const normalizedPattern = normalizeRelativePath(pattern);
|
|
169644
|
+
if (normalizedPattern.endsWith(".*") && !normalizedPattern.includes("/")) {
|
|
169645
|
+
const base = normalizedPattern.slice(0, -2);
|
|
169646
|
+
if (mode === "allow") {
|
|
169647
|
+
const firstSegment = segments[0] ?? "";
|
|
169648
|
+
return firstSegment === base || firstSegment.startsWith(`${base}.`);
|
|
169649
|
+
}
|
|
169650
|
+
return segments.some((segment) => segment === base || segment.startsWith(`${base}.`));
|
|
169651
|
+
}
|
|
169652
|
+
if (normalizedPattern.includes("*")) {
|
|
169653
|
+
const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
169654
|
+
const regex = mode === "allow" ? new RegExp(`^${escaped}($|/)`) : new RegExp(`(^|/)${escaped}($|/)`);
|
|
169655
|
+
return regex.test(normalized);
|
|
169656
|
+
}
|
|
169657
|
+
if (!normalizedPattern.includes("/")) {
|
|
169658
|
+
if (mode === "allow") {
|
|
169659
|
+
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
|
|
169660
|
+
}
|
|
169661
|
+
return segments.includes(normalizedPattern);
|
|
169662
|
+
}
|
|
169663
|
+
if (mode === "allow") {
|
|
169664
|
+
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
|
|
169665
|
+
}
|
|
169666
|
+
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`) || normalized.endsWith(`/${normalizedPattern}`) || normalized.includes(`/${normalizedPattern}/`);
|
|
169667
|
+
});
|
|
169668
|
+
}
|
|
169669
|
+
|
|
169670
|
+
// src/core/constants.ts
|
|
169671
|
+
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
169672
|
+
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
169673
|
+
var TRACE_DIR = ".kyoso/traces";
|
|
169674
|
+
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
169675
|
+
var KYOSO_VERSION = "0.9.0";
|
|
169676
|
+
|
|
169677
|
+
// src/utils/pathContainment.ts
|
|
169678
|
+
import { resolve, sep as sep2 } from "node:path";
|
|
169679
|
+
function isPathWithin(candidate, root) {
|
|
169680
|
+
const resolvedCandidate = resolve(candidate);
|
|
169681
|
+
const resolvedRoot = resolve(root);
|
|
169682
|
+
const boundary = resolvedRoot.endsWith(sep2) ? resolvedRoot : `${resolvedRoot}${sep2}`;
|
|
169683
|
+
return resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(boundary);
|
|
169684
|
+
}
|
|
169685
|
+
|
|
169686
|
+
// src/audit/stateRoot.ts
|
|
169687
|
+
var PRIVATE_DIRECTORY_MODE = 448;
|
|
169688
|
+
var UNSAFE_DIRECTORY_MODE = 18;
|
|
169689
|
+
var STICKY_DIRECTORY_MODE = 512;
|
|
169690
|
+
var AUDIT_WARNING_DIRECTORY_IGNORED = "AUDIT_DIRECTORY_IGNORED: Audit directory is invalid; using the default logical directory.";
|
|
169691
|
+
var AUDIT_WARNING_UNSUPPORTED_PLATFORM = "AUDIT_DISABLED_UNSUPPORTED_PLATFORM: Audit trace writing is unavailable on this platform.";
|
|
169692
|
+
var AUDIT_WARNING_UNSUPPORTED_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
|
|
169693
|
+
var AUDIT_WARNING_UNSAFE_STATE_ROOT = "AUDIT_DISABLED_UNSAFE_STATE_ROOT: Audit state root could not be verified.";
|
|
169694
|
+
async function resolveAuditStateRoot(options) {
|
|
169695
|
+
const warnings = [];
|
|
169696
|
+
const logicalDirectory = validateAuditDirectory(options.directory, warnings);
|
|
169697
|
+
const platform = options.platform ?? process.platform;
|
|
169698
|
+
if (platform === "win32") {
|
|
169699
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_PLATFORM] };
|
|
169700
|
+
}
|
|
169701
|
+
const uid = getCurrentUid(options.getuid);
|
|
169702
|
+
if (uid === undefined) {
|
|
169703
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_CAPABILITY] };
|
|
169704
|
+
}
|
|
169705
|
+
try {
|
|
169706
|
+
const workspaceRoot = await realpath(resolve2(options.cwd));
|
|
169707
|
+
const candidate = resolveStateBaseCandidate(options.env ?? process.env);
|
|
169708
|
+
if (!candidate)
|
|
169709
|
+
throw new Error("missing trusted state base");
|
|
169710
|
+
const stateBase = await ensureTrustedStateBase({
|
|
169711
|
+
candidate,
|
|
169712
|
+
workspaceRoot,
|
|
169713
|
+
uid
|
|
169714
|
+
});
|
|
169715
|
+
const kyosoRoot = await ensureTrustedDirectory({
|
|
169716
|
+
root: stateBase,
|
|
169717
|
+
segments: ["kyoso"],
|
|
169718
|
+
uid,
|
|
169719
|
+
workspaceRoot
|
|
169720
|
+
});
|
|
169721
|
+
return {
|
|
169722
|
+
stateBase,
|
|
169723
|
+
kyosoRoot,
|
|
169724
|
+
workspaceRoot,
|
|
169725
|
+
workspaceHash: createHash("sha256").update(workspaceRoot).digest("hex"),
|
|
169726
|
+
logicalDirectory,
|
|
169727
|
+
uid,
|
|
169728
|
+
warnings
|
|
169729
|
+
};
|
|
169730
|
+
} catch {
|
|
169731
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSAFE_STATE_ROOT] };
|
|
169732
|
+
}
|
|
169733
|
+
}
|
|
169734
|
+
async function inspectAuditStateRootCapability(options) {
|
|
169735
|
+
const platform = options.platform ?? process.platform;
|
|
169736
|
+
if (platform === "win32" || getCurrentUid(options.getuid) === undefined) {
|
|
169737
|
+
return { available: false };
|
|
169738
|
+
}
|
|
169739
|
+
try {
|
|
169740
|
+
const uid = getCurrentUid(options.getuid);
|
|
169741
|
+
if (uid === undefined)
|
|
169742
|
+
return { available: false };
|
|
169743
|
+
const workspaceRoot = await realpath(resolve2(options.cwd));
|
|
169744
|
+
const candidate = resolveStateBaseCandidate(options.env ?? process.env);
|
|
169745
|
+
if (!candidate || isPathWithin(candidate, workspaceRoot)) {
|
|
169746
|
+
return { available: false };
|
|
169747
|
+
}
|
|
169748
|
+
const existing = await findExistingAncestor(candidate);
|
|
169749
|
+
assertSafeDirectory(existing.path, uid, await lstat(existing.path), {
|
|
169750
|
+
allowFilesystemRoot: true
|
|
169751
|
+
});
|
|
169752
|
+
const realExisting = await realpath(existing.path);
|
|
169753
|
+
if (isPathWithin(realExisting, workspaceRoot))
|
|
169754
|
+
return { available: false };
|
|
169755
|
+
await assertTrustedAncestorChain(realExisting, uid);
|
|
169756
|
+
if (existing.missingSegments.length === 0) {
|
|
169757
|
+
const kyosoRoot = join(realExisting, "kyoso");
|
|
169758
|
+
const kyosoStat = await optionalLstat(kyosoRoot);
|
|
169759
|
+
if (kyosoStat)
|
|
169760
|
+
assertSafeDirectory(kyosoRoot, uid, kyosoStat);
|
|
169761
|
+
}
|
|
169762
|
+
return { available: true };
|
|
169763
|
+
} catch {
|
|
169764
|
+
return { available: false };
|
|
169765
|
+
}
|
|
169766
|
+
}
|
|
169767
|
+
async function ensureTrustedDirectory(options) {
|
|
169768
|
+
const realRoot = await realpath(options.root);
|
|
169769
|
+
assertSafeDirectory(realRoot, options.uid, await lstat(realRoot));
|
|
169770
|
+
if (options.workspaceRoot && isPathWithin(realRoot, options.workspaceRoot)) {
|
|
169771
|
+
throw new Error("trusted directory resolves inside workspace");
|
|
169772
|
+
}
|
|
169773
|
+
await assertTrustedAncestorChain(realRoot, options.uid);
|
|
169774
|
+
let current = realRoot;
|
|
169775
|
+
for (const segment of options.segments) {
|
|
169776
|
+
if (!isSafePathSegment(segment))
|
|
169777
|
+
throw new Error("unsafe path segment");
|
|
169778
|
+
const next = join(current, segment);
|
|
169779
|
+
let entry = await optionalLstat(next);
|
|
169780
|
+
if (!entry) {
|
|
169781
|
+
await createDirectory(next);
|
|
169782
|
+
entry = await lstat(next);
|
|
169783
|
+
}
|
|
169784
|
+
assertSafeDirectory(next, options.uid, entry);
|
|
169785
|
+
const realNext = await realpath(next);
|
|
169786
|
+
if (!isPathWithin(realNext, realRoot)) {
|
|
169787
|
+
throw new Error("managed directory escaped trusted root");
|
|
169788
|
+
}
|
|
169789
|
+
if (options.workspaceRoot && isPathWithin(realNext, options.workspaceRoot)) {
|
|
169790
|
+
throw new Error("managed directory resolves inside workspace");
|
|
169791
|
+
}
|
|
169792
|
+
current = realNext;
|
|
169793
|
+
}
|
|
169794
|
+
return current;
|
|
169795
|
+
}
|
|
169796
|
+
function isResolvedAuditStateRoot(resolution) {
|
|
169797
|
+
return "kyosoRoot" in resolution;
|
|
169798
|
+
}
|
|
169799
|
+
function validateAuditDirectory(directory, warnings) {
|
|
169800
|
+
try {
|
|
169801
|
+
if (directory.trim().length === 0 || isAbsolute(directory) || directory.split(/[\\/]+/).includes("..")) {
|
|
169802
|
+
throw new Error("unsafe logical directory");
|
|
169803
|
+
}
|
|
169804
|
+
const normalized = normalizeRelativePath(directory);
|
|
169805
|
+
if (normalized === "." || normalized.split("/").includes("..")) {
|
|
169806
|
+
throw new Error("unsafe logical directory");
|
|
169807
|
+
}
|
|
169808
|
+
return normalized;
|
|
169809
|
+
} catch {
|
|
169810
|
+
warnings.push(AUDIT_WARNING_DIRECTORY_IGNORED);
|
|
169811
|
+
return TRACE_DIR;
|
|
169812
|
+
}
|
|
169813
|
+
}
|
|
169814
|
+
function resolveStateBaseCandidate(env) {
|
|
169815
|
+
const xdgStateHome = env.XDG_STATE_HOME?.trim();
|
|
169816
|
+
if (xdgStateHome && isAbsolute(xdgStateHome)) {
|
|
169817
|
+
return resolve2(xdgStateHome);
|
|
169818
|
+
}
|
|
169819
|
+
const home = env.HOME?.trim();
|
|
169820
|
+
if (!home || !isAbsolute(home))
|
|
169821
|
+
return;
|
|
169822
|
+
return join(resolve2(home), ".local", "state");
|
|
169823
|
+
}
|
|
169824
|
+
async function ensureTrustedStateBase(options) {
|
|
169825
|
+
if (isPathWithin(options.candidate, options.workspaceRoot)) {
|
|
169826
|
+
throw new Error("state base is inside workspace");
|
|
169827
|
+
}
|
|
169828
|
+
const existing = await findExistingAncestor(options.candidate);
|
|
169829
|
+
assertSafeDirectory(existing.path, options.uid, await lstat(existing.path), {
|
|
169830
|
+
allowFilesystemRoot: true
|
|
169831
|
+
});
|
|
169832
|
+
const realExisting = await realpath(existing.path);
|
|
169833
|
+
if (isPathWithin(realExisting, options.workspaceRoot)) {
|
|
169834
|
+
throw new Error("state base resolves inside workspace");
|
|
169835
|
+
}
|
|
169836
|
+
await assertTrustedAncestorChain(realExisting, options.uid);
|
|
169837
|
+
let current = realExisting;
|
|
169838
|
+
for (const segment of existing.missingSegments) {
|
|
169839
|
+
current = join(current, segment);
|
|
169840
|
+
await createDirectory(current);
|
|
169841
|
+
const entry = await lstat(current);
|
|
169842
|
+
assertSafeDirectory(current, options.uid, entry);
|
|
169843
|
+
const realCurrent = await realpath(current);
|
|
169844
|
+
if (!isPathWithin(realCurrent, realExisting)) {
|
|
169845
|
+
throw new Error("state base changed while being created");
|
|
169846
|
+
}
|
|
169847
|
+
if (isPathWithin(realCurrent, options.workspaceRoot)) {
|
|
169848
|
+
throw new Error("state base resolves inside workspace");
|
|
169849
|
+
}
|
|
169850
|
+
current = realCurrent;
|
|
169851
|
+
}
|
|
169852
|
+
const stateBase = await realpath(current);
|
|
169853
|
+
assertSafeDirectory(stateBase, options.uid, await lstat(stateBase));
|
|
169854
|
+
if (isPathWithin(stateBase, options.workspaceRoot)) {
|
|
169855
|
+
throw new Error("state base resolves inside workspace");
|
|
169856
|
+
}
|
|
169857
|
+
await assertTrustedAncestorChain(stateBase, options.uid);
|
|
169858
|
+
return stateBase;
|
|
169859
|
+
}
|
|
169860
|
+
async function findExistingAncestor(candidate) {
|
|
169861
|
+
let current = resolve2(candidate);
|
|
169862
|
+
const missingSegments = [];
|
|
169863
|
+
while (true) {
|
|
169864
|
+
const entry = await optionalLstat(current);
|
|
169865
|
+
if (entry) {
|
|
169866
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
169867
|
+
throw new Error("state base ancestor is unsafe");
|
|
169868
|
+
}
|
|
169869
|
+
return { path: current, missingSegments };
|
|
169870
|
+
}
|
|
169871
|
+
const parent = dirname2(current);
|
|
169872
|
+
if (parent === current)
|
|
169873
|
+
throw new Error("state base has no existing ancestor");
|
|
169874
|
+
missingSegments.unshift(basename(current));
|
|
169875
|
+
current = parent;
|
|
169876
|
+
}
|
|
169877
|
+
}
|
|
169878
|
+
function assertSafeDirectory(path, uid, entry, options = {}) {
|
|
169879
|
+
const stat = entry;
|
|
169880
|
+
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
169881
|
+
throw new Error("state directory is unsafe");
|
|
169882
|
+
}
|
|
169883
|
+
if (numericStatValue(stat.uid) !== uid && !(options.allowFilesystemRoot && isFilesystemRoot(path))) {
|
|
169884
|
+
throw new Error("state directory owner is unsafe");
|
|
169885
|
+
}
|
|
169886
|
+
const mode = numericStatValue(stat.mode);
|
|
169887
|
+
if ((mode & UNSAFE_DIRECTORY_MODE) !== 0) {
|
|
169888
|
+
throw new Error("state directory mode is unsafe");
|
|
169889
|
+
}
|
|
169890
|
+
}
|
|
169891
|
+
function numericStatValue(value) {
|
|
169892
|
+
return typeof value === "number" ? value : Number(value);
|
|
169893
|
+
}
|
|
169894
|
+
function getCurrentUid(getuid) {
|
|
169895
|
+
const resolveUid = getuid ?? (typeof process.getuid === "function" ? process.getuid.bind(process) : undefined);
|
|
169896
|
+
try {
|
|
169897
|
+
const uid = resolveUid?.();
|
|
169898
|
+
return typeof uid === "number" && Number.isInteger(uid) && uid >= 0 ? uid : undefined;
|
|
169899
|
+
} catch {
|
|
169900
|
+
return;
|
|
169901
|
+
}
|
|
169902
|
+
}
|
|
169903
|
+
function isFilesystemRoot(path) {
|
|
169904
|
+
return dirname2(path) === path;
|
|
169905
|
+
}
|
|
169906
|
+
async function assertTrustedAncestorChain(path, uid) {
|
|
169907
|
+
let child = path;
|
|
169908
|
+
while (!isFilesystemRoot(child)) {
|
|
169909
|
+
const parent = dirname2(child);
|
|
169910
|
+
const [parentStat, childStat] = await Promise.all([
|
|
169911
|
+
lstat(parent),
|
|
169912
|
+
lstat(child)
|
|
169913
|
+
]);
|
|
169914
|
+
if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) {
|
|
169915
|
+
throw new Error("state directory ancestor is unsafe");
|
|
169916
|
+
}
|
|
169917
|
+
const parentMode = numericStatValue(parentStat.mode);
|
|
169918
|
+
const parentUid = numericStatValue(parentStat.uid);
|
|
169919
|
+
const childUid = numericStatValue(childStat.uid);
|
|
169920
|
+
const parentWritableByOthers = (parentMode & UNSAFE_DIRECTORY_MODE) !== 0;
|
|
169921
|
+
const stickyChildEntry = (parentMode & STICKY_DIRECTORY_MODE) !== 0 && childUid === uid;
|
|
169922
|
+
if (parentUid !== uid && parentUid !== 0 || parentWritableByOthers && !stickyChildEntry) {
|
|
169923
|
+
throw new Error("state directory ancestor permissions are unsafe");
|
|
169924
|
+
}
|
|
169925
|
+
child = parent;
|
|
169926
|
+
}
|
|
169927
|
+
}
|
|
169928
|
+
function isSafePathSegment(segment) {
|
|
169929
|
+
return segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("/") && !segment.includes("\\");
|
|
169930
|
+
}
|
|
169931
|
+
async function optionalLstat(path) {
|
|
169932
|
+
try {
|
|
169933
|
+
return await lstat(path);
|
|
169934
|
+
} catch (error) {
|
|
169935
|
+
if (isMissingPathError(error))
|
|
169936
|
+
return;
|
|
169937
|
+
throw error;
|
|
169938
|
+
}
|
|
169939
|
+
}
|
|
169940
|
+
async function createDirectory(path) {
|
|
169941
|
+
try {
|
|
169942
|
+
await mkdir2(path, { mode: PRIVATE_DIRECTORY_MODE });
|
|
169943
|
+
} catch (error) {
|
|
169944
|
+
if (!isAlreadyExistsError(error))
|
|
169945
|
+
throw error;
|
|
169946
|
+
}
|
|
169947
|
+
}
|
|
169948
|
+
function isMissingPathError(error) {
|
|
169949
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
169950
|
+
}
|
|
169951
|
+
function isAlreadyExistsError(error) {
|
|
169952
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
169953
|
+
}
|
|
169604
169954
|
|
|
169605
169955
|
// src/config/loadConfig.ts
|
|
169606
169956
|
import { access as access2, readFile as readFile4 } from "node:fs/promises";
|
|
169607
169957
|
import { homedir as homedir2 } from "node:os";
|
|
169608
169958
|
import { stderr, stdin } from "node:process";
|
|
169609
|
-
import { extname as extname3, join as
|
|
169959
|
+
import { extname as extname3, join as join3, resolve as resolve4 } from "node:path";
|
|
169610
169960
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
169611
169961
|
|
|
169612
169962
|
// src/config/defaultConfig.ts
|
|
@@ -184360,12 +184710,6 @@ var kyosoConfigSecuritySensitivePrefixes = [
|
|
|
184360
184710
|
// src/security/redact.ts
|
|
184361
184711
|
var REDACTION = "[KYOSO_REDACTED]";
|
|
184362
184712
|
|
|
184363
|
-
// src/core/constants.ts
|
|
184364
|
-
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
184365
|
-
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
184366
|
-
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
184367
|
-
var KYOSO_VERSION = "0.8.0";
|
|
184368
|
-
|
|
184369
184713
|
// src/security/sanitizeText.ts
|
|
184370
184714
|
var SENSITIVE_TEXT_PATTERNS = [
|
|
184371
184715
|
/\bsk-(?:proj-)?[A-Za-z0-9_-]{8,}\b/g,
|
|
@@ -184833,7 +185177,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
184833
185177
|
}
|
|
184834
185178
|
return ptr;
|
|
184835
185179
|
}
|
|
184836
|
-
function skipUntil(str, ptr,
|
|
185180
|
+
function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
184837
185181
|
if (!end) {
|
|
184838
185182
|
ptr = indexOfNewline(str, ptr);
|
|
184839
185183
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -184842,7 +185186,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
|
184842
185186
|
let c = str[i];
|
|
184843
185187
|
if (c === "#") {
|
|
184844
185188
|
i = indexOfNewline(str, i);
|
|
184845
|
-
} else if (c ===
|
|
185189
|
+
} else if (c === sep3) {
|
|
184846
185190
|
return i + 1;
|
|
184847
185191
|
} else if (c === end || banNewLines && (c === `
|
|
184848
185192
|
` || c === "\r" && str[i + 1] === `
|
|
@@ -185340,7 +185684,7 @@ function formatTomlError(configPath, error51) {
|
|
|
185340
185684
|
// src/config/tsConfigLoader.ts
|
|
185341
185685
|
var import_typescript = __toESM(require_typescript(), 1);
|
|
185342
185686
|
import { createRequire as createRequire2 } from "node:module";
|
|
185343
|
-
import { dirname as
|
|
185687
|
+
import { dirname as dirname3, extname as extname2 } from "node:path";
|
|
185344
185688
|
import { pathToFileURL } from "node:url";
|
|
185345
185689
|
import vm from "node:vm";
|
|
185346
185690
|
|
|
@@ -185374,20 +185718,20 @@ function loadTypeScriptConfig(filePath, source) {
|
|
|
185374
185718
|
${transpiled}
|
|
185375
185719
|
})`, { filename: filePath });
|
|
185376
185720
|
const runner = script.runInThisContext();
|
|
185377
|
-
runner(module.exports, require2, module, filePath,
|
|
185721
|
+
runner(module.exports, require2, module, filePath, dirname3(filePath), defineConfig);
|
|
185378
185722
|
return module.exports;
|
|
185379
185723
|
}
|
|
185380
185724
|
|
|
185381
185725
|
// src/config/trustedConfig.ts
|
|
185382
|
-
import { createHash } from "node:crypto";
|
|
185383
|
-
import { mkdir as
|
|
185726
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
185727
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
185384
185728
|
import { homedir } from "node:os";
|
|
185385
|
-
import { dirname as
|
|
185729
|
+
import { dirname as dirname4, join as join2, resolve as resolve3 } from "node:path";
|
|
185386
185730
|
function hashConfigSource(source) {
|
|
185387
|
-
return
|
|
185731
|
+
return createHash2("sha256").update(source).digest("hex");
|
|
185388
185732
|
}
|
|
185389
185733
|
function defaultTrustedConfigStorePath(env = process.env) {
|
|
185390
|
-
return env.KYOSO_TRUST_STORE_PATH ?
|
|
185734
|
+
return env.KYOSO_TRUST_STORE_PATH ? resolve3(env.KYOSO_TRUST_STORE_PATH) : join2(env.HOME ? resolve3(env.HOME) : homedir(), ".kyoso", "trusted-configs.json");
|
|
185391
185735
|
}
|
|
185392
185736
|
async function isTrustedConfig(storePath, configPath, configHash) {
|
|
185393
185737
|
const store = await readTrustedConfigStore(storePath);
|
|
@@ -185396,7 +185740,7 @@ async function isTrustedConfig(storePath, configPath, configHash) {
|
|
|
185396
185740
|
async function trustConfig(storePath, configPath, configHash) {
|
|
185397
185741
|
const store = await readTrustedConfigStore(storePath);
|
|
185398
185742
|
store[configPath] = configHash;
|
|
185399
|
-
await
|
|
185743
|
+
await mkdir3(dirname4(storePath), { recursive: true });
|
|
185400
185744
|
await writeFile2(storePath, `${JSON.stringify(store, null, 2)}
|
|
185401
185745
|
`, {
|
|
185402
185746
|
encoding: "utf8",
|
|
@@ -185408,7 +185752,7 @@ async function readTrustedConfigStore(storePath) {
|
|
|
185408
185752
|
try {
|
|
185409
185753
|
parsed = JSON.parse(await readFile3(storePath, "utf8"));
|
|
185410
185754
|
} catch (error51) {
|
|
185411
|
-
if (
|
|
185755
|
+
if (isMissingPathError2(error51))
|
|
185412
185756
|
return {};
|
|
185413
185757
|
if (error51 instanceof SyntaxError)
|
|
185414
185758
|
return {};
|
|
@@ -185426,7 +185770,7 @@ async function readTrustedConfigStore(storePath) {
|
|
|
185426
185770
|
function isRecord2(value) {
|
|
185427
185771
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
185428
185772
|
}
|
|
185429
|
-
function
|
|
185773
|
+
function isMissingPathError2(error51) {
|
|
185430
185774
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
185431
185775
|
}
|
|
185432
185776
|
|
|
@@ -185457,7 +185801,7 @@ async function loadConfig(options = {}) {
|
|
|
185457
185801
|
sources.push({ path: globalConfigPath, layer: "global_toml" });
|
|
185458
185802
|
}
|
|
185459
185803
|
if (options.configPath) {
|
|
185460
|
-
const explicitConfigPath =
|
|
185804
|
+
const explicitConfigPath = resolve4(cwd, options.configPath);
|
|
185461
185805
|
if (!await exists2(explicitConfigPath)) {
|
|
185462
185806
|
throw new Error(`Config file not found: ${explicitConfigPath} (from --config)`);
|
|
185463
185807
|
}
|
|
@@ -185474,8 +185818,8 @@ async function loadConfig(options = {}) {
|
|
|
185474
185818
|
sources.push(loaded.source);
|
|
185475
185819
|
warnings.push(...loaded.warnings);
|
|
185476
185820
|
} else {
|
|
185477
|
-
const projectTomlPath =
|
|
185478
|
-
const projectTsPath =
|
|
185821
|
+
const projectTomlPath = resolve4(cwd, "kyoso.toml");
|
|
185822
|
+
const projectTsPath = resolve4(cwd, "kyoso.config.ts");
|
|
185479
185823
|
const hasProjectToml = await exists2(projectTomlPath);
|
|
185480
185824
|
const hasProjectTs = await exists2(projectTsPath);
|
|
185481
185825
|
if (hasProjectToml) {
|
|
@@ -185511,8 +185855,8 @@ async function loadConfig(options = {}) {
|
|
|
185511
185855
|
};
|
|
185512
185856
|
}
|
|
185513
185857
|
function resolveGlobalTomlConfigPath(env = process.env) {
|
|
185514
|
-
const configHome = env.XDG_CONFIG_HOME ?
|
|
185515
|
-
return
|
|
185858
|
+
const configHome = env.XDG_CONFIG_HOME ? resolve4(env.XDG_CONFIG_HOME) : join3(env.HOME ? resolve4(env.HOME) : homedir2(), ".config");
|
|
185859
|
+
return join3(configHome, "kyoso", "config.toml");
|
|
185516
185860
|
}
|
|
185517
185861
|
function collectGlobalConfigWarnings(configPath, config2) {
|
|
185518
185862
|
const unknownSettings = flattenLeaves(config2).map((leaf) => leaf.path).filter((path) => !isKnownGlobalConfigPath(path)).map((path) => ({
|
|
@@ -185947,23 +186291,1174 @@ function hasEnv(env, key) {
|
|
|
185947
186291
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
185948
186292
|
}
|
|
185949
186293
|
|
|
185950
|
-
// src/cli/
|
|
186294
|
+
// src/cli/codexPluginDetector.ts
|
|
185951
186295
|
import { spawnSync } from "node:child_process";
|
|
185952
|
-
|
|
185953
|
-
|
|
186296
|
+
|
|
186297
|
+
// src/cli/pluginRuntimeContract.ts
|
|
186298
|
+
var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
186299
|
+
var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
|
|
186300
|
+
var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
186301
|
+
distribution: {
|
|
186302
|
+
pluginVersion: "0.1.0",
|
|
186303
|
+
mcpCommand: "npx",
|
|
186304
|
+
mcpPackagePin: "@kyo-so/cli@0.8.0"
|
|
186305
|
+
},
|
|
186306
|
+
marketplace: {
|
|
186307
|
+
name: "kyoso",
|
|
186308
|
+
listed: true,
|
|
186309
|
+
pluginId: "kyoso@kyoso",
|
|
186310
|
+
selector: "kyoso@kyoso",
|
|
186311
|
+
installPolicy: "AVAILABLE",
|
|
186312
|
+
authPolicy: "ON_USE"
|
|
186313
|
+
},
|
|
186314
|
+
transitions: {
|
|
186315
|
+
beforeAdd: { installed: false, enabled: false },
|
|
186316
|
+
afterAdd: { installed: true, enabled: true },
|
|
186317
|
+
disabled: { installed: true, enabled: false },
|
|
186318
|
+
afterRemove: { installed: false, enabled: false },
|
|
186319
|
+
installPluginId: "kyoso@kyoso",
|
|
186320
|
+
removePluginId: "kyoso@kyoso"
|
|
186321
|
+
},
|
|
186322
|
+
mcp: {
|
|
186323
|
+
default: {
|
|
186324
|
+
enabled: true,
|
|
186325
|
+
command: "$NODE",
|
|
186326
|
+
args: ["$PROBE_SERVER", "$OBSERVATION"],
|
|
186327
|
+
envVars: [
|
|
186328
|
+
"OPENAI_API_KEY",
|
|
186329
|
+
"CODEX_API_KEY",
|
|
186330
|
+
"CODEX_HOME",
|
|
186331
|
+
"CODEX_ACCESS_TOKEN",
|
|
186332
|
+
"ANTHROPIC_API_KEY",
|
|
186333
|
+
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
186334
|
+
],
|
|
186335
|
+
cwd: null,
|
|
186336
|
+
startupTimeoutSec: 20,
|
|
186337
|
+
toolTimeoutSec: 360
|
|
186338
|
+
},
|
|
186339
|
+
pluginOverride: {
|
|
186340
|
+
enabled: false,
|
|
186341
|
+
command: "$NODE",
|
|
186342
|
+
args: ["$PROBE_SERVER", "$OBSERVATION"],
|
|
186343
|
+
envVars: [
|
|
186344
|
+
"OPENAI_API_KEY",
|
|
186345
|
+
"CODEX_API_KEY",
|
|
186346
|
+
"CODEX_HOME",
|
|
186347
|
+
"CODEX_ACCESS_TOKEN",
|
|
186348
|
+
"ANTHROPIC_API_KEY",
|
|
186349
|
+
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
186350
|
+
],
|
|
186351
|
+
cwd: null,
|
|
186352
|
+
startupTimeoutSec: 20,
|
|
186353
|
+
toolTimeoutSec: 360
|
|
186354
|
+
},
|
|
186355
|
+
manualOverride: {
|
|
186356
|
+
enabled: true,
|
|
186357
|
+
command: "manual-kyoso",
|
|
186358
|
+
args: ["mcp"],
|
|
186359
|
+
envVars: [],
|
|
186360
|
+
cwd: null,
|
|
186361
|
+
startupTimeoutSec: null,
|
|
186362
|
+
toolTimeoutSec: null
|
|
186363
|
+
}
|
|
186364
|
+
},
|
|
186365
|
+
appServer: {
|
|
186366
|
+
default: {
|
|
186367
|
+
serverFound: true,
|
|
186368
|
+
toolNames: ["probe_environment"],
|
|
186369
|
+
authStatus: "unsupported",
|
|
186370
|
+
skillFound: false,
|
|
186371
|
+
skillEnabled: null,
|
|
186372
|
+
skillHasKyosoMcpDependency: false
|
|
186373
|
+
},
|
|
186374
|
+
pluginOverride: {
|
|
186375
|
+
serverFound: true,
|
|
186376
|
+
toolNames: [],
|
|
186377
|
+
authStatus: "unsupported",
|
|
186378
|
+
skillFound: false,
|
|
186379
|
+
skillEnabled: null,
|
|
186380
|
+
skillHasKyosoMcpDependency: false,
|
|
186381
|
+
mcpObservationWritten: false
|
|
186382
|
+
}
|
|
186383
|
+
},
|
|
186384
|
+
environment: {
|
|
186385
|
+
cwdIsWorkspace: true,
|
|
186386
|
+
homeInherited: true,
|
|
186387
|
+
codexHomeForwarded: true,
|
|
186388
|
+
tempDirectoryInherited: true,
|
|
186389
|
+
pathInherited: true,
|
|
186390
|
+
allowlistedEnvForwarded: {
|
|
186391
|
+
OPENAI_API_KEY: true,
|
|
186392
|
+
CODEX_API_KEY: true,
|
|
186393
|
+
CODEX_ACCESS_TOKEN: true,
|
|
186394
|
+
ANTHROPIC_API_KEY: true,
|
|
186395
|
+
CLAUDE_CODE_OAUTH_TOKEN: true
|
|
186396
|
+
},
|
|
186397
|
+
deniedSentinelForwarded: false
|
|
186398
|
+
},
|
|
186399
|
+
isolation: {
|
|
186400
|
+
distinctHomeAndCodexHome: true,
|
|
186401
|
+
distinctWorkspace: true
|
|
186402
|
+
}
|
|
186403
|
+
};
|
|
186404
|
+
var PLUGIN_LIST_JSON_SCHEMA = {
|
|
186405
|
+
collections: ["installed", "available"],
|
|
186406
|
+
entry: {
|
|
186407
|
+
pluginId: "kyoso@kyoso",
|
|
186408
|
+
requiredFields: ["pluginId", "installed", "enabled"]
|
|
186409
|
+
}
|
|
186410
|
+
};
|
|
186411
|
+
var pluginRuntimeContract = {
|
|
186412
|
+
schemaVersion: PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION,
|
|
186413
|
+
minimumSupportedCodexVersion: MINIMUM_SUPPORTED_CODEX_VERSION,
|
|
186414
|
+
pluginId: "kyoso@kyoso",
|
|
186415
|
+
pluginListTopLevelKeys: PLUGIN_LIST_JSON_SCHEMA.collections,
|
|
186416
|
+
expectedContract: PLUGIN_RUNTIME_EXPECTED_CONTRACT
|
|
186417
|
+
};
|
|
186418
|
+
|
|
186419
|
+
// src/cli/codexPluginDetector.ts
|
|
186420
|
+
var CODEX_PLUGIN_INSPECTION_TIMEOUT_MS = 5000;
|
|
186421
|
+
var MAX_CODEX_INSPECTION_TIMEOUT_MS = 1e4;
|
|
186422
|
+
var CODEX_INSPECTION_MAX_BUFFER_BYTES = 1024 * 1024;
|
|
186423
|
+
function inspectCodexPlugin(options) {
|
|
186424
|
+
const input2 = normalizeOptions(options);
|
|
186425
|
+
const versionResult = input2.runCodex(commandFor(input2, ["--version"]));
|
|
186426
|
+
if (versionResult.kind !== "completed") {
|
|
186427
|
+
return unsupported("version", failureReason(versionResult));
|
|
186428
|
+
}
|
|
186429
|
+
const codexVersion = parseCodexVersion(versionResult.stdout);
|
|
186430
|
+
if (!codexVersion)
|
|
186431
|
+
return unsupported("version", "invalid_output");
|
|
186432
|
+
if (compareSemver(codexVersion, MINIMUM_SUPPORTED_CODEX_VERSION) < 0) {
|
|
186433
|
+
return unsupported("version", "unsupported_version");
|
|
186434
|
+
}
|
|
186435
|
+
const pluginListResult = input2.runCodex(commandFor(input2, ["plugin", "list", "--json"]));
|
|
186436
|
+
if (pluginListResult.kind !== "completed") {
|
|
186437
|
+
return unsupported("plugin_list", failureReason(pluginListResult));
|
|
186438
|
+
}
|
|
186439
|
+
const parsed = parseJson(pluginListResult.stdout);
|
|
186440
|
+
if (parsed === undefined)
|
|
186441
|
+
return unsupported("plugin_list", "invalid_output");
|
|
186442
|
+
const plugin = parsePluginList(parsed);
|
|
186443
|
+
if (!plugin)
|
|
186444
|
+
return unsupported("plugin_list", "unknown_schema");
|
|
186445
|
+
return { status: "supported", codexVersion, plugin };
|
|
186446
|
+
}
|
|
186447
|
+
function inspectCodexMcpList(options) {
|
|
186448
|
+
const input2 = normalizeOptions(options);
|
|
186449
|
+
const result = input2.runCodex(commandFor(input2, ["mcp", "list", "--json"]));
|
|
186450
|
+
if (result.kind !== "completed") {
|
|
186451
|
+
return unsupported("mcp_list", failureReason(result));
|
|
186452
|
+
}
|
|
186453
|
+
const parsed = parseJson(result.stdout);
|
|
186454
|
+
if (parsed === undefined)
|
|
186455
|
+
return unsupported("mcp_list", "invalid_output");
|
|
186456
|
+
const kyoso = parseMcpList(parsed);
|
|
186457
|
+
if (!kyoso)
|
|
186458
|
+
return unsupported("mcp_list", "unknown_schema");
|
|
186459
|
+
return { status: "supported", kyoso };
|
|
186460
|
+
}
|
|
186461
|
+
function formatCodexInspectionFailure(failure) {
|
|
186462
|
+
const operation = {
|
|
186463
|
+
version: "Codex version",
|
|
186464
|
+
plugin_list: "Codex Plugin list",
|
|
186465
|
+
mcp_list: "Codex MCP list"
|
|
186466
|
+
}[failure.operation];
|
|
186467
|
+
const reason = {
|
|
186468
|
+
unavailable: "is unavailable",
|
|
186469
|
+
timeout: "timed out",
|
|
186470
|
+
command_failed: "could not be inspected",
|
|
186471
|
+
invalid_output: "returned unsupported output",
|
|
186472
|
+
unknown_schema: "returned an unsupported schema",
|
|
186473
|
+
unsupported_version: "is below the supported version"
|
|
186474
|
+
}[failure.reason];
|
|
186475
|
+
return `${operation} ${reason}.`;
|
|
186476
|
+
}
|
|
186477
|
+
function normalizeOptions(options) {
|
|
186478
|
+
return {
|
|
186479
|
+
cwd: options.cwd,
|
|
186480
|
+
env: options.env ?? process.env,
|
|
186481
|
+
timeoutMs: boundedTimeout(options.timeoutMs),
|
|
186482
|
+
runCodex: options.runCodex ?? runCodex
|
|
186483
|
+
};
|
|
186484
|
+
}
|
|
186485
|
+
function boundedTimeout(timeoutMs) {
|
|
186486
|
+
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
|
|
186487
|
+
return CODEX_PLUGIN_INSPECTION_TIMEOUT_MS;
|
|
186488
|
+
}
|
|
186489
|
+
return Math.min(timeoutMs, MAX_CODEX_INSPECTION_TIMEOUT_MS);
|
|
186490
|
+
}
|
|
186491
|
+
function commandFor(options, args) {
|
|
186492
|
+
return {
|
|
186493
|
+
args,
|
|
186494
|
+
cwd: options.cwd,
|
|
186495
|
+
env: options.env,
|
|
186496
|
+
timeoutMs: options.timeoutMs
|
|
186497
|
+
};
|
|
186498
|
+
}
|
|
186499
|
+
function runCodex(command) {
|
|
186500
|
+
const result = spawnSync("codex", command.args, {
|
|
186501
|
+
cwd: command.cwd,
|
|
186502
|
+
env: command.env,
|
|
186503
|
+
encoding: "utf8",
|
|
186504
|
+
timeout: command.timeoutMs,
|
|
186505
|
+
maxBuffer: CODEX_INSPECTION_MAX_BUFFER_BYTES,
|
|
186506
|
+
windowsHide: true
|
|
186507
|
+
});
|
|
186508
|
+
const errorCode = commandErrorCode(result.error);
|
|
186509
|
+
if (errorCode === "ENOENT")
|
|
186510
|
+
return { kind: "unavailable" };
|
|
186511
|
+
if (errorCode === "ETIMEDOUT")
|
|
186512
|
+
return { kind: "timeout" };
|
|
186513
|
+
if (result.error || result.status === null || result.status !== 0) {
|
|
186514
|
+
return { kind: "failed" };
|
|
186515
|
+
}
|
|
186516
|
+
return {
|
|
186517
|
+
kind: "completed",
|
|
186518
|
+
exitCode: result.status,
|
|
186519
|
+
stdout: String(result.stdout ?? "")
|
|
186520
|
+
};
|
|
186521
|
+
}
|
|
186522
|
+
function commandErrorCode(error51) {
|
|
186523
|
+
if (typeof error51 !== "object" || error51 === null || !("code" in error51) || typeof error51.code !== "string") {
|
|
186524
|
+
return;
|
|
186525
|
+
}
|
|
186526
|
+
return error51.code;
|
|
186527
|
+
}
|
|
186528
|
+
function failureReason(result) {
|
|
186529
|
+
if (result.kind === "unavailable")
|
|
186530
|
+
return "unavailable";
|
|
186531
|
+
if (result.kind === "timeout")
|
|
186532
|
+
return "timeout";
|
|
186533
|
+
return "command_failed";
|
|
186534
|
+
}
|
|
186535
|
+
function unsupported(operation, reason) {
|
|
186536
|
+
return { status: "unsupported", failure: { operation, reason } };
|
|
186537
|
+
}
|
|
186538
|
+
function parseJson(value) {
|
|
186539
|
+
try {
|
|
186540
|
+
return JSON.parse(value);
|
|
186541
|
+
} catch {
|
|
186542
|
+
return;
|
|
186543
|
+
}
|
|
186544
|
+
}
|
|
186545
|
+
function parsePluginList(value) {
|
|
186546
|
+
if (!isRecord5(value))
|
|
186547
|
+
return;
|
|
186548
|
+
const allowedKeys = new Set(PLUGIN_LIST_JSON_SCHEMA.collections);
|
|
186549
|
+
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
186550
|
+
return;
|
|
186551
|
+
}
|
|
186552
|
+
const installed = parsePluginEntries(value.installed);
|
|
186553
|
+
const available = parsePluginEntries(value.available);
|
|
186554
|
+
if (!installed || !available)
|
|
186555
|
+
return;
|
|
186556
|
+
const matches = [...installed, ...available].filter((entry2) => entry2.pluginId === PLUGIN_RUNTIME_EXPECTED_CONTRACT.marketplace.pluginId);
|
|
186557
|
+
if (matches.length > 1)
|
|
186558
|
+
return;
|
|
186559
|
+
if (matches.length === 0) {
|
|
186560
|
+
return pluginState(false, false);
|
|
186561
|
+
}
|
|
186562
|
+
const entry = matches[0];
|
|
186563
|
+
if (!entry.installed && entry.enabled)
|
|
186564
|
+
return;
|
|
186565
|
+
return pluginState(entry.installed, entry.enabled);
|
|
186566
|
+
}
|
|
186567
|
+
function parsePluginEntries(value) {
|
|
186568
|
+
if (value === undefined)
|
|
186569
|
+
return [];
|
|
186570
|
+
if (!Array.isArray(value))
|
|
186571
|
+
return;
|
|
186572
|
+
const entries = [];
|
|
186573
|
+
for (const item of value) {
|
|
186574
|
+
if (!isRecord5(item))
|
|
186575
|
+
return;
|
|
186576
|
+
if (typeof item.pluginId !== "string" || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean") {
|
|
186577
|
+
return;
|
|
186578
|
+
}
|
|
186579
|
+
entries.push({
|
|
186580
|
+
pluginId: item.pluginId,
|
|
186581
|
+
installed: item.installed,
|
|
186582
|
+
enabled: item.enabled
|
|
186583
|
+
});
|
|
186584
|
+
}
|
|
186585
|
+
return entries;
|
|
186586
|
+
}
|
|
186587
|
+
function pluginState(installed, enabled) {
|
|
186588
|
+
return {
|
|
186589
|
+
pluginId: PLUGIN_RUNTIME_EXPECTED_CONTRACT.marketplace.pluginId,
|
|
186590
|
+
installed,
|
|
186591
|
+
enabled,
|
|
186592
|
+
state: installed ? enabled ? "enabled" : "disabled" : "not_installed"
|
|
186593
|
+
};
|
|
186594
|
+
}
|
|
186595
|
+
function parseMcpList(value) {
|
|
186596
|
+
if (!Array.isArray(value))
|
|
186597
|
+
return;
|
|
186598
|
+
const matches = [];
|
|
186599
|
+
for (const item of value) {
|
|
186600
|
+
if (!isRecord5(item) || typeof item.name !== "string")
|
|
186601
|
+
return "unknown";
|
|
186602
|
+
if (item.name !== "kyoso")
|
|
186603
|
+
continue;
|
|
186604
|
+
if (typeof item.enabled !== "boolean")
|
|
186605
|
+
return "unknown";
|
|
186606
|
+
matches.push(item.enabled);
|
|
186607
|
+
}
|
|
186608
|
+
if (matches.length === 0)
|
|
186609
|
+
return "missing";
|
|
186610
|
+
if (matches.length > 1)
|
|
186611
|
+
return "unknown";
|
|
186612
|
+
return matches[0] ? "enabled" : "disabled";
|
|
186613
|
+
}
|
|
186614
|
+
function parseCodexVersion(output2) {
|
|
186615
|
+
for (const line of output2.split(/\r?\n/)) {
|
|
186616
|
+
const match = /^codex-cli\s+(\S+)\s*$/.exec(line.trim());
|
|
186617
|
+
const version2 = match?.[1];
|
|
186618
|
+
if (version2 && parseSemver(version2))
|
|
186619
|
+
return version2;
|
|
186620
|
+
}
|
|
186621
|
+
return;
|
|
186622
|
+
}
|
|
186623
|
+
function compareSemver(left, right) {
|
|
186624
|
+
const parsedLeft = parseSemver(left);
|
|
186625
|
+
const parsedRight = parseSemver(right);
|
|
186626
|
+
if (!parsedLeft || !parsedRight)
|
|
186627
|
+
return 0;
|
|
186628
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
186629
|
+
if (parsedLeft[key] !== parsedRight[key]) {
|
|
186630
|
+
return parsedLeft[key] < parsedRight[key] ? -1 : 1;
|
|
186631
|
+
}
|
|
186632
|
+
}
|
|
186633
|
+
return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
|
|
186634
|
+
}
|
|
186635
|
+
function parseSemver(value) {
|
|
186636
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
|
|
186637
|
+
if (!match)
|
|
186638
|
+
return;
|
|
186639
|
+
const major = Number(match[1]);
|
|
186640
|
+
const minor = Number(match[2]);
|
|
186641
|
+
const patch = Number(match[3]);
|
|
186642
|
+
if (!Number.isSafeInteger(major) || !Number.isSafeInteger(minor) || !Number.isSafeInteger(patch)) {
|
|
186643
|
+
return;
|
|
186644
|
+
}
|
|
186645
|
+
return {
|
|
186646
|
+
major,
|
|
186647
|
+
minor,
|
|
186648
|
+
patch,
|
|
186649
|
+
prerelease: match[4]?.split(".") ?? []
|
|
186650
|
+
};
|
|
186651
|
+
}
|
|
186652
|
+
function comparePrerelease(left, right) {
|
|
186653
|
+
if (left.length === 0 && right.length === 0)
|
|
186654
|
+
return 0;
|
|
186655
|
+
if (left.length === 0)
|
|
186656
|
+
return 1;
|
|
186657
|
+
if (right.length === 0)
|
|
186658
|
+
return -1;
|
|
186659
|
+
const count = Math.max(left.length, right.length);
|
|
186660
|
+
for (let index = 0;index < count; index += 1) {
|
|
186661
|
+
const leftPart = left[index];
|
|
186662
|
+
const rightPart = right[index];
|
|
186663
|
+
if (leftPart === undefined)
|
|
186664
|
+
return -1;
|
|
186665
|
+
if (rightPart === undefined)
|
|
186666
|
+
return 1;
|
|
186667
|
+
if (leftPart === rightPart)
|
|
186668
|
+
continue;
|
|
186669
|
+
const leftNumeric = /^\d+$/.test(leftPart);
|
|
186670
|
+
const rightNumeric = /^\d+$/.test(rightPart);
|
|
186671
|
+
if (leftNumeric && rightNumeric) {
|
|
186672
|
+
return Number(leftPart) < Number(rightPart) ? -1 : 1;
|
|
186673
|
+
}
|
|
186674
|
+
if (leftNumeric)
|
|
186675
|
+
return -1;
|
|
186676
|
+
if (rightNumeric)
|
|
186677
|
+
return 1;
|
|
186678
|
+
return leftPart < rightPart ? -1 : 1;
|
|
186679
|
+
}
|
|
186680
|
+
return 0;
|
|
186681
|
+
}
|
|
186682
|
+
function isRecord5(value) {
|
|
186683
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186684
|
+
}
|
|
186685
|
+
|
|
186686
|
+
// src/cli/integration.ts
|
|
186687
|
+
import {
|
|
186688
|
+
accessSync,
|
|
186689
|
+
constants,
|
|
186690
|
+
existsSync,
|
|
186691
|
+
readFileSync,
|
|
186692
|
+
realpathSync
|
|
186693
|
+
} from "node:fs";
|
|
186694
|
+
import { tmpdir } from "node:os";
|
|
186695
|
+
import {
|
|
186696
|
+
delimiter,
|
|
186697
|
+
dirname as dirname5,
|
|
186698
|
+
extname as extname4,
|
|
186699
|
+
isAbsolute as isAbsolute2,
|
|
186700
|
+
join as join4,
|
|
186701
|
+
relative,
|
|
186702
|
+
resolve as resolve5
|
|
186703
|
+
} from "node:path";
|
|
186704
|
+
function detectCli(options) {
|
|
186705
|
+
const env = options.env ?? process.env;
|
|
186706
|
+
const platform = options.platform ?? process.platform;
|
|
186707
|
+
return {
|
|
186708
|
+
kyoso: detectInstalledKyoso(options.cwd, env, platform),
|
|
186709
|
+
npx: commandExists("npx", env, platform),
|
|
186710
|
+
bunx: commandExists("bunx", env, platform)
|
|
186711
|
+
};
|
|
186712
|
+
}
|
|
186713
|
+
function determineNonPluginIntegration(options) {
|
|
186714
|
+
const warnings = manualMcpWarnings(options.manualMcpStatus);
|
|
186715
|
+
if (options.manualMcpStatus === "unknown") {
|
|
186716
|
+
return { mode: "unknown", warnings };
|
|
186717
|
+
}
|
|
186718
|
+
if (options.manualMcpStatus === "enabled") {
|
|
186719
|
+
return {
|
|
186720
|
+
mode: options.hasSkill ? "manual-mcp" : "mcp-only",
|
|
186721
|
+
warnings
|
|
186722
|
+
};
|
|
186723
|
+
}
|
|
186724
|
+
const cliWarnings = cliIdentityWarnings(options.cli.kyoso);
|
|
186725
|
+
warnings.push(...cliWarnings);
|
|
186726
|
+
if (options.hasSkill) {
|
|
186727
|
+
if (options.cli.kyoso.kind === "installed") {
|
|
186728
|
+
return { mode: "cli-skill", warnings };
|
|
186729
|
+
}
|
|
186730
|
+
if (options.cli.npx || options.cli.bunx) {
|
|
186731
|
+
warnings.push("Package-runner fallback may require network access and can drift between versions.");
|
|
186732
|
+
return { mode: "skill-on-demand", warnings };
|
|
186733
|
+
}
|
|
186734
|
+
if (options.cli.kyoso.kind === "unknown") {
|
|
186735
|
+
return { mode: "unknown", warnings };
|
|
186736
|
+
}
|
|
186737
|
+
warnings.push("The Skill is installed, but no supported Kyoso CLI execution path was found.");
|
|
186738
|
+
return { mode: "missing", warnings };
|
|
186739
|
+
}
|
|
186740
|
+
if (options.cli.kyoso.kind === "installed") {
|
|
186741
|
+
return { mode: "cli-only", warnings };
|
|
186742
|
+
}
|
|
186743
|
+
if (options.cli.kyoso.kind === "unknown") {
|
|
186744
|
+
return { mode: "unknown", warnings };
|
|
186745
|
+
}
|
|
186746
|
+
return { mode: "missing", warnings };
|
|
186747
|
+
}
|
|
186748
|
+
function formatCliAvailability(cli) {
|
|
186749
|
+
if (cli.kind === "installed") {
|
|
186750
|
+
return `installed @kyo-so/cli ${cli.version} (${cli.scope})`;
|
|
186751
|
+
}
|
|
186752
|
+
if (cli.kind === "transient") {
|
|
186753
|
+
return "temporary runner (not treated as an installed CLI)";
|
|
186754
|
+
}
|
|
186755
|
+
if (cli.kind === "unknown") {
|
|
186756
|
+
return "unrecognized PATH entry (not treated as an installed CLI)";
|
|
186757
|
+
}
|
|
186758
|
+
return "missing";
|
|
186759
|
+
}
|
|
186760
|
+
function detectInstalledKyoso(cwd, env, platform) {
|
|
186761
|
+
const executable = resolveCommand("kyoso", env, platform);
|
|
186762
|
+
if (!executable)
|
|
186763
|
+
return { kind: "missing" };
|
|
186764
|
+
let realExecutable;
|
|
186765
|
+
try {
|
|
186766
|
+
realExecutable = realpathSync(executable);
|
|
186767
|
+
} catch {
|
|
186768
|
+
return { kind: "unknown" };
|
|
186769
|
+
}
|
|
186770
|
+
if (isTransientPath(realExecutable, cwd))
|
|
186771
|
+
return { kind: "transient" };
|
|
186772
|
+
const packageInfo = findKyosoPackage(realExecutable);
|
|
186773
|
+
if (!packageInfo)
|
|
186774
|
+
return { kind: "unknown" };
|
|
186775
|
+
return {
|
|
186776
|
+
kind: "installed",
|
|
186777
|
+
version: packageInfo.version,
|
|
186778
|
+
scope: isWithin(packageInfo.directory, realPathOrResolved(cwd)) ? "project" : "global"
|
|
186779
|
+
};
|
|
186780
|
+
}
|
|
186781
|
+
function findKyosoPackage(executable) {
|
|
186782
|
+
let directory = dirname5(executable);
|
|
186783
|
+
for (;; ) {
|
|
186784
|
+
const packagePath = join4(directory, "package.json");
|
|
186785
|
+
if (existsSync(packagePath)) {
|
|
186786
|
+
try {
|
|
186787
|
+
const parsed = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
186788
|
+
if (isRecord6(parsed) && parsed.name === "@kyo-so/cli" && typeof parsed.version === "string" && parsed.version.trim().length > 0) {
|
|
186789
|
+
return { directory, version: parsed.version };
|
|
186790
|
+
}
|
|
186791
|
+
} catch {}
|
|
186792
|
+
}
|
|
186793
|
+
const parent = dirname5(directory);
|
|
186794
|
+
if (parent === directory)
|
|
186795
|
+
return;
|
|
186796
|
+
directory = parent;
|
|
186797
|
+
}
|
|
186798
|
+
}
|
|
186799
|
+
function isTransientPath(path, cwd) {
|
|
186800
|
+
if (path.split(/[\\/]/).includes("_npx"))
|
|
186801
|
+
return true;
|
|
186802
|
+
const temporaryRoot = realPathOrResolved(tmpdir());
|
|
186803
|
+
return isWithin(path, temporaryRoot) && !isWithin(path, realPathOrResolved(cwd));
|
|
186804
|
+
}
|
|
186805
|
+
function realPathOrResolved(path) {
|
|
186806
|
+
try {
|
|
186807
|
+
return realpathSync(path);
|
|
186808
|
+
} catch {
|
|
186809
|
+
return resolve5(path);
|
|
186810
|
+
}
|
|
186811
|
+
}
|
|
186812
|
+
function manualMcpWarnings(status) {
|
|
186813
|
+
if (status === "disabled")
|
|
186814
|
+
return ["Manual MCP registration is disabled."];
|
|
186815
|
+
if (status === "unknown") {
|
|
186816
|
+
return [
|
|
186817
|
+
"Manual MCP registration could not be safely classified from its configuration."
|
|
186818
|
+
];
|
|
186819
|
+
}
|
|
186820
|
+
return [];
|
|
186821
|
+
}
|
|
186822
|
+
function cliIdentityWarnings(cli) {
|
|
186823
|
+
if (cli.kind === "transient") {
|
|
186824
|
+
return [
|
|
186825
|
+
"A temporary Kyoso runner was found on PATH and is not treated as an installed CLI."
|
|
186826
|
+
];
|
|
186827
|
+
}
|
|
186828
|
+
if (cli.kind === "unknown") {
|
|
186829
|
+
return [
|
|
186830
|
+
"A PATH entry named kyoso does not identify itself as @kyo-so/cli and is not treated as an installed CLI."
|
|
186831
|
+
];
|
|
186832
|
+
}
|
|
186833
|
+
return [];
|
|
186834
|
+
}
|
|
186835
|
+
function commandExists(command, env, platform) {
|
|
186836
|
+
return resolveCommand(command, env, platform) !== undefined;
|
|
186837
|
+
}
|
|
186838
|
+
function resolveCommand(command, env, platform) {
|
|
186839
|
+
for (const path of env.PATH?.split(delimiter) ?? []) {
|
|
186840
|
+
if (path.length === 0)
|
|
186841
|
+
continue;
|
|
186842
|
+
for (const name of commandNames(command, env, platform)) {
|
|
186843
|
+
const candidate = join4(path, name);
|
|
186844
|
+
try {
|
|
186845
|
+
accessSync(candidate, constants.X_OK);
|
|
186846
|
+
return candidate;
|
|
186847
|
+
} catch {}
|
|
186848
|
+
}
|
|
186849
|
+
}
|
|
186850
|
+
return;
|
|
186851
|
+
}
|
|
186852
|
+
function commandNames(command, env, platform) {
|
|
186853
|
+
if (platform !== "win32" || extname4(command).length > 0)
|
|
186854
|
+
return [command];
|
|
186855
|
+
const names = [command];
|
|
186856
|
+
const seen = new Set([command.toLowerCase()]);
|
|
186857
|
+
for (const extension of env.PATHEXT?.split(";") ?? []) {
|
|
186858
|
+
const normalized = extension.trim();
|
|
186859
|
+
if (!normalized.startsWith(".") || /[\\/]/.test(normalized))
|
|
186860
|
+
continue;
|
|
186861
|
+
const candidate = `${command}${normalized}`;
|
|
186862
|
+
const key = candidate.toLowerCase();
|
|
186863
|
+
if (seen.has(key))
|
|
186864
|
+
continue;
|
|
186865
|
+
seen.add(key);
|
|
186866
|
+
names.push(candidate);
|
|
186867
|
+
}
|
|
186868
|
+
return names;
|
|
186869
|
+
}
|
|
186870
|
+
function isWithin(path, parent) {
|
|
186871
|
+
const relativePath = relative(resolve5(parent), resolve5(path));
|
|
186872
|
+
return relativePath === "" || !relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && relativePath !== ".." && !isAbsolute2(relativePath);
|
|
186873
|
+
}
|
|
186874
|
+
function isRecord6(value) {
|
|
186875
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186876
|
+
}
|
|
186877
|
+
|
|
186878
|
+
// src/cli/setup.ts
|
|
186879
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
186880
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
186881
|
+
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
185954
186882
|
import { homedir as homedir3 } from "node:os";
|
|
185955
|
-
import { delimiter, dirname as
|
|
186883
|
+
import { delimiter as delimiter2, dirname as dirname7, join as join6, resolve as resolve7 } from "node:path";
|
|
185956
186884
|
import { fileURLToPath } from "node:url";
|
|
186885
|
+
|
|
186886
|
+
// src/cli/skillInstall.ts
|
|
186887
|
+
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
186888
|
+
import {
|
|
186889
|
+
copyFile,
|
|
186890
|
+
link,
|
|
186891
|
+
lstat as lstat2,
|
|
186892
|
+
mkdir as mkdir4,
|
|
186893
|
+
mkdtemp,
|
|
186894
|
+
readFile as readFile5,
|
|
186895
|
+
readdir,
|
|
186896
|
+
realpath as realpath2,
|
|
186897
|
+
rename,
|
|
186898
|
+
rm,
|
|
186899
|
+
writeFile as writeFile3
|
|
186900
|
+
} from "node:fs/promises";
|
|
186901
|
+
import {
|
|
186902
|
+
basename as basename2,
|
|
186903
|
+
dirname as dirname6,
|
|
186904
|
+
isAbsolute as isAbsolute3,
|
|
186905
|
+
join as join5,
|
|
186906
|
+
relative as relative2,
|
|
186907
|
+
resolve as resolve6,
|
|
186908
|
+
sep as sep3
|
|
186909
|
+
} from "node:path";
|
|
186910
|
+
|
|
186911
|
+
// src/cli/knownSkillDigests.ts
|
|
186912
|
+
var CURRENT_SKILL_DIGEST = "sha256:98e715396ee3e1994c29ab49bc489197f242091c8ac0e06e182c0eccd5ab277e";
|
|
186913
|
+
var KNOWN_SKILL_DIGESTS_BY_VERSION = {
|
|
186914
|
+
"0.8.0": [
|
|
186915
|
+
{
|
|
186916
|
+
digest: "sha256:b16ea3f8141a01399b96dee650365d99df2b8c5fc99184d9cb22d5d72c106fd8",
|
|
186917
|
+
kind: "historical"
|
|
186918
|
+
}
|
|
186919
|
+
]
|
|
186920
|
+
};
|
|
186921
|
+
function knownSkillDigest(digest) {
|
|
186922
|
+
if (digest === CURRENT_SKILL_DIGEST) {
|
|
186923
|
+
return { version: KYOSO_VERSION, kind: "current" };
|
|
186924
|
+
}
|
|
186925
|
+
for (const [version2, entries] of Object.entries(KNOWN_SKILL_DIGESTS_BY_VERSION)) {
|
|
186926
|
+
const entry = entries.find((candidate) => candidate.digest === digest);
|
|
186927
|
+
if (entry)
|
|
186928
|
+
return { version: version2, kind: entry.kind };
|
|
186929
|
+
}
|
|
186930
|
+
return;
|
|
186931
|
+
}
|
|
186932
|
+
|
|
186933
|
+
// src/cli/skillInstall.ts
|
|
186934
|
+
var SKILL_INSTALL_MARKER = ".kyoso-install.json";
|
|
186935
|
+
var SKILL_INSTALL_BACKUP = ".kyoso-review.backup";
|
|
186936
|
+
var SKILL_INSTALL_TRANSACTION = ".kyoso-review.install-transaction.json";
|
|
186937
|
+
async function ensureManagedSkill(options) {
|
|
186938
|
+
const sourceDigest = await hashSkillDirectory(options.sourceDir);
|
|
186939
|
+
if (sourceDigest !== CURRENT_SKILL_DIGEST) {
|
|
186940
|
+
throw new Error(`Bundled kyoso-review digest is ${sourceDigest}; update knownSkillDigests.ts before distributing it.`);
|
|
186941
|
+
}
|
|
186942
|
+
const safeDestination = await resolveSafeDestination(options.trustedRoot, options.destinationDir);
|
|
186943
|
+
const destinationDir = safeDestination.destinationDir;
|
|
186944
|
+
const recovery = await recoverInterruptedReplacement({
|
|
186945
|
+
destinationDir,
|
|
186946
|
+
trustedRoot: safeDestination.realRoot,
|
|
186947
|
+
write: options.write
|
|
186948
|
+
});
|
|
186949
|
+
if (recovery.status !== "none") {
|
|
186950
|
+
return {
|
|
186951
|
+
status: recovery.status,
|
|
186952
|
+
path: destinationDir,
|
|
186953
|
+
detail: recovery.detail
|
|
186954
|
+
};
|
|
186955
|
+
}
|
|
186956
|
+
const destinationStat = await optionalLstat2(destinationDir);
|
|
186957
|
+
if (!destinationStat) {
|
|
186958
|
+
const detail2 = copyDetail(options.sourceDir, destinationDir, "create");
|
|
186959
|
+
if (!options.write) {
|
|
186960
|
+
return { status: "dry-run", path: destinationDir, detail: detail2 };
|
|
186961
|
+
}
|
|
186962
|
+
await replaceSkillDirectory({
|
|
186963
|
+
sourceDir: options.sourceDir,
|
|
186964
|
+
destinationDir,
|
|
186965
|
+
trustedRoot: safeDestination.realRoot,
|
|
186966
|
+
destinationExists: false
|
|
186967
|
+
});
|
|
186968
|
+
return { status: "created", path: destinationDir, detail: detail2 };
|
|
186969
|
+
}
|
|
186970
|
+
if (!destinationStat.isDirectory()) {
|
|
186971
|
+
if (!options.force) {
|
|
186972
|
+
return conflictResult(destinationDir, "destination exists and is not a directory");
|
|
186973
|
+
}
|
|
186974
|
+
const detail2 = copyDetail(options.sourceDir, destinationDir, "force replace");
|
|
186975
|
+
if (!options.write) {
|
|
186976
|
+
return { status: "dry-run", path: destinationDir, detail: detail2 };
|
|
186977
|
+
}
|
|
186978
|
+
await replaceSkillDirectory({
|
|
186979
|
+
sourceDir: options.sourceDir,
|
|
186980
|
+
destinationDir,
|
|
186981
|
+
trustedRoot: safeDestination.realRoot,
|
|
186982
|
+
destinationExists: true
|
|
186983
|
+
});
|
|
186984
|
+
return { status: "updated", path: destinationDir, detail: detail2 };
|
|
186985
|
+
}
|
|
186986
|
+
if (await realpath2(options.sourceDir) === await realpath2(destinationDir)) {
|
|
186987
|
+
return {
|
|
186988
|
+
status: "skipped",
|
|
186989
|
+
path: destinationDir,
|
|
186990
|
+
detail: "canonical source is already the destination"
|
|
186991
|
+
};
|
|
186992
|
+
}
|
|
186993
|
+
const actualDigest = await hashSkillDirectory(destinationDir);
|
|
186994
|
+
const marker = await readInstallMarker(destinationDir);
|
|
186995
|
+
if (options.force) {
|
|
186996
|
+
const detail2 = [
|
|
186997
|
+
`force replace ${destinationDir}`,
|
|
186998
|
+
`from ${actualDigest}`,
|
|
186999
|
+
`to ${CURRENT_SKILL_DIGEST}`
|
|
187000
|
+
].join(`
|
|
187001
|
+
`);
|
|
187002
|
+
if (!options.write) {
|
|
187003
|
+
return { status: "dry-run", path: destinationDir, detail: detail2 };
|
|
187004
|
+
}
|
|
187005
|
+
await replaceSkillDirectory({
|
|
187006
|
+
sourceDir: options.sourceDir,
|
|
187007
|
+
destinationDir,
|
|
187008
|
+
trustedRoot: safeDestination.realRoot,
|
|
187009
|
+
destinationExists: true
|
|
187010
|
+
});
|
|
187011
|
+
return { status: "updated", path: destinationDir, detail: detail2 };
|
|
187012
|
+
}
|
|
187013
|
+
if (marker.kind === "invalid") {
|
|
187014
|
+
return conflictResult(destinationDir, marker.reason);
|
|
187015
|
+
}
|
|
187016
|
+
if (marker.kind === "valid") {
|
|
187017
|
+
if (marker.value.digest !== actualDigest) {
|
|
187018
|
+
return conflictResult(destinationDir, `managed digest ${marker.value.digest} does not match installed digest ${actualDigest}`);
|
|
187019
|
+
}
|
|
187020
|
+
if (actualDigest === CURRENT_SKILL_DIGEST && marker.value.cliVersion === KYOSO_VERSION) {
|
|
187021
|
+
return {
|
|
187022
|
+
status: "skipped",
|
|
187023
|
+
path: destinationDir,
|
|
187024
|
+
detail: `managed skill is current (${CURRENT_SKILL_DIGEST})`
|
|
187025
|
+
};
|
|
187026
|
+
}
|
|
187027
|
+
if (actualDigest === CURRENT_SKILL_DIGEST) {
|
|
187028
|
+
const detail3 = `refresh install marker to CLI ${KYOSO_VERSION}`;
|
|
187029
|
+
if (!options.write) {
|
|
187030
|
+
return { status: "dry-run", path: destinationDir, detail: detail3 };
|
|
187031
|
+
}
|
|
187032
|
+
await writeInstallMarker(destinationDir);
|
|
187033
|
+
return { status: "updated", path: destinationDir, detail: detail3 };
|
|
187034
|
+
}
|
|
187035
|
+
const detail2 = [
|
|
187036
|
+
`update managed skill from ${actualDigest}`,
|
|
187037
|
+
`to ${CURRENT_SKILL_DIGEST}`
|
|
187038
|
+
].join(`
|
|
187039
|
+
`);
|
|
187040
|
+
if (!options.write) {
|
|
187041
|
+
return { status: "dry-run", path: destinationDir, detail: detail2 };
|
|
187042
|
+
}
|
|
187043
|
+
await replaceSkillDirectory({
|
|
187044
|
+
sourceDir: options.sourceDir,
|
|
187045
|
+
destinationDir,
|
|
187046
|
+
trustedRoot: safeDestination.realRoot,
|
|
187047
|
+
destinationExists: true
|
|
187048
|
+
});
|
|
187049
|
+
return { status: "updated", path: destinationDir, detail: detail2 };
|
|
187050
|
+
}
|
|
187051
|
+
const known = knownSkillDigest(actualDigest);
|
|
187052
|
+
if (!known) {
|
|
187053
|
+
return conflictResult(destinationDir, `unmanaged skill digest ${actualDigest} is not recognized`);
|
|
187054
|
+
}
|
|
187055
|
+
if (actualDigest === CURRENT_SKILL_DIGEST) {
|
|
187056
|
+
const detail2 = `adopt existing ${known.version} skill and write ${SKILL_INSTALL_MARKER}`;
|
|
187057
|
+
if (!options.write) {
|
|
187058
|
+
return { status: "dry-run", path: destinationDir, detail: detail2 };
|
|
187059
|
+
}
|
|
187060
|
+
await writeInstallMarker(destinationDir);
|
|
187061
|
+
return { status: "updated", path: destinationDir, detail: detail2 };
|
|
187062
|
+
}
|
|
187063
|
+
const detail = [
|
|
187064
|
+
`adopt known ${known.version} ${known.kind} skill (${actualDigest})`,
|
|
187065
|
+
`update to ${CURRENT_SKILL_DIGEST}`
|
|
187066
|
+
].join(`
|
|
187067
|
+
`);
|
|
187068
|
+
if (!options.write) {
|
|
187069
|
+
return { status: "dry-run", path: destinationDir, detail };
|
|
187070
|
+
}
|
|
187071
|
+
await replaceSkillDirectory({
|
|
187072
|
+
sourceDir: options.sourceDir,
|
|
187073
|
+
destinationDir,
|
|
187074
|
+
trustedRoot: safeDestination.realRoot,
|
|
187075
|
+
destinationExists: true
|
|
187076
|
+
});
|
|
187077
|
+
return { status: "updated", path: destinationDir, detail };
|
|
187078
|
+
}
|
|
187079
|
+
async function hashSkillDirectory(directory) {
|
|
187080
|
+
const rootStat = await lstat2(directory);
|
|
187081
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
187082
|
+
throw new Error(`Skill root must be a regular directory: ${directory}`);
|
|
187083
|
+
}
|
|
187084
|
+
const files = await listRegularFiles(directory);
|
|
187085
|
+
files.sort((left, right) => Buffer.compare(Buffer.from(left.relativePath), Buffer.from(right.relativePath)));
|
|
187086
|
+
const hash2 = createHash3("sha256");
|
|
187087
|
+
for (const file2 of files) {
|
|
187088
|
+
const bytes = await readFile5(file2.absolutePath);
|
|
187089
|
+
hash2.update(file2.relativePath);
|
|
187090
|
+
hash2.update("\x00");
|
|
187091
|
+
hash2.update(String(bytes.byteLength));
|
|
187092
|
+
hash2.update("\x00");
|
|
187093
|
+
hash2.update(bytes);
|
|
187094
|
+
}
|
|
187095
|
+
return `sha256:${hash2.digest("hex")}`;
|
|
187096
|
+
}
|
|
187097
|
+
async function listRegularFiles(directory, prefix = "") {
|
|
187098
|
+
const result = [];
|
|
187099
|
+
const entries = await readdir(directory);
|
|
187100
|
+
for (const name of entries) {
|
|
187101
|
+
const relativePath = prefix.length > 0 ? `${prefix}/${name}` : name;
|
|
187102
|
+
const absolutePath = join5(directory, name);
|
|
187103
|
+
const stat = await lstat2(absolutePath);
|
|
187104
|
+
if (stat.isSymbolicLink()) {
|
|
187105
|
+
throw new Error(`Skill directory contains a symlink: ${relativePath}`);
|
|
187106
|
+
}
|
|
187107
|
+
if (relativePath === SKILL_INSTALL_MARKER) {
|
|
187108
|
+
if (!stat.isFile()) {
|
|
187109
|
+
throw new Error(`${SKILL_INSTALL_MARKER} must be a regular file`);
|
|
187110
|
+
}
|
|
187111
|
+
continue;
|
|
187112
|
+
}
|
|
187113
|
+
if (stat.isDirectory()) {
|
|
187114
|
+
result.push(...await listRegularFiles(absolutePath, relativePath));
|
|
187115
|
+
continue;
|
|
187116
|
+
}
|
|
187117
|
+
if (!stat.isFile()) {
|
|
187118
|
+
throw new Error(`Skill directory contains a non-regular file: ${relativePath}`);
|
|
187119
|
+
}
|
|
187120
|
+
result.push({ relativePath, absolutePath });
|
|
187121
|
+
}
|
|
187122
|
+
return result;
|
|
187123
|
+
}
|
|
187124
|
+
async function resolveSafeDestination(trustedRoot, destinationDir) {
|
|
187125
|
+
const logicalRoot = resolve6(trustedRoot);
|
|
187126
|
+
const logicalDestination = resolve6(destinationDir);
|
|
187127
|
+
const relativeDestination = relative2(logicalRoot, logicalDestination);
|
|
187128
|
+
if (relativeDestination === "" || !isPathWithin(logicalDestination, logicalRoot)) {
|
|
187129
|
+
throw new Error(`Skill destination escapes trusted install root: ${destinationDir}`);
|
|
187130
|
+
}
|
|
187131
|
+
const realRoot = await realpath2(logicalRoot);
|
|
187132
|
+
const safeDestination = resolve6(realRoot, relativeDestination);
|
|
187133
|
+
if (!isPathWithin(safeDestination, realRoot)) {
|
|
187134
|
+
throw new Error(`Skill destination escapes trusted install root: ${destinationDir}`);
|
|
187135
|
+
}
|
|
187136
|
+
await assertExistingSegmentsSafe(realRoot, safeDestination);
|
|
187137
|
+
return { realRoot, destinationDir: safeDestination };
|
|
187138
|
+
}
|
|
187139
|
+
async function assertExistingSegmentsSafe(realRoot, destination) {
|
|
187140
|
+
const segments = relative2(realRoot, destination).split(sep3).filter(Boolean);
|
|
187141
|
+
let current = realRoot;
|
|
187142
|
+
for (const segment of segments) {
|
|
187143
|
+
current = join5(current, segment);
|
|
187144
|
+
const stat = await optionalLstat2(current);
|
|
187145
|
+
if (!stat)
|
|
187146
|
+
return;
|
|
187147
|
+
if (stat.isSymbolicLink()) {
|
|
187148
|
+
throw new Error(`Skill install path contains a symlink: ${current}`);
|
|
187149
|
+
}
|
|
187150
|
+
if (current !== destination && !stat.isDirectory()) {
|
|
187151
|
+
throw new Error(`Skill install parent is not a directory: ${current}`);
|
|
187152
|
+
}
|
|
187153
|
+
}
|
|
187154
|
+
}
|
|
187155
|
+
async function ensureSafeParent(realRoot, destinationDir) {
|
|
187156
|
+
const parent = dirname6(destinationDir);
|
|
187157
|
+
const relativeParent = relative2(realRoot, parent);
|
|
187158
|
+
if (relativeParent === ".." || relativeParent.startsWith(`..${sep3}`) || isAbsolute3(relativeParent)) {
|
|
187159
|
+
throw new Error(`Skill install parent escapes trusted root: ${parent}`);
|
|
187160
|
+
}
|
|
187161
|
+
const segments = relative2(realRoot, parent).split(sep3).filter(Boolean);
|
|
187162
|
+
let current = realRoot;
|
|
187163
|
+
for (const segment of segments) {
|
|
187164
|
+
current = join5(current, segment);
|
|
187165
|
+
let stat = await optionalLstat2(current);
|
|
187166
|
+
if (!stat) {
|
|
187167
|
+
await mkdir4(current);
|
|
187168
|
+
stat = await lstat2(current);
|
|
187169
|
+
}
|
|
187170
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
187171
|
+
throw new Error(`Skill install parent is unsafe: ${current}`);
|
|
187172
|
+
}
|
|
187173
|
+
}
|
|
187174
|
+
}
|
|
187175
|
+
async function replaceSkillDirectory(options) {
|
|
187176
|
+
await ensureSafeParent(options.trustedRoot, options.destinationDir);
|
|
187177
|
+
await assertExistingSegmentsSafe(options.trustedRoot, options.destinationDir);
|
|
187178
|
+
const parent = dirname6(options.destinationDir);
|
|
187179
|
+
const mutationGuard = await captureMutationGuard(parent);
|
|
187180
|
+
const stage = await mkdtemp(join5(parent, ".kyoso-review.stage-"));
|
|
187181
|
+
const backup = join5(parent, SKILL_INSTALL_BACKUP);
|
|
187182
|
+
const transaction = join5(parent, SKILL_INSTALL_TRANSACTION);
|
|
187183
|
+
let backupCreated = false;
|
|
187184
|
+
let transactionCreated = false;
|
|
187185
|
+
try {
|
|
187186
|
+
await copySkillFiles(options.sourceDir, stage);
|
|
187187
|
+
await writeInstallMarker(stage);
|
|
187188
|
+
const stagedDigest = await hashSkillDirectory(stage);
|
|
187189
|
+
if (stagedDigest !== CURRENT_SKILL_DIGEST) {
|
|
187190
|
+
throw new Error(`Staged kyoso-review digest mismatch: ${stagedDigest} != ${CURRENT_SKILL_DIGEST}`);
|
|
187191
|
+
}
|
|
187192
|
+
if (options.destinationExists) {
|
|
187193
|
+
await assertMutationGuard(mutationGuard);
|
|
187194
|
+
if (await optionalLstat2(backup) || await optionalLstat2(transaction)) {
|
|
187195
|
+
throw interruptedReplacementConflict(options.destinationDir, backup);
|
|
187196
|
+
}
|
|
187197
|
+
await writeReplacementTransaction(transaction, options.destinationDir);
|
|
187198
|
+
transactionCreated = true;
|
|
187199
|
+
await assertMutationGuard(mutationGuard);
|
|
187200
|
+
await rename(options.destinationDir, backup);
|
|
187201
|
+
backupCreated = true;
|
|
187202
|
+
await assertMutationGuard(mutationGuard);
|
|
187203
|
+
}
|
|
187204
|
+
try {
|
|
187205
|
+
await assertMutationGuard(mutationGuard);
|
|
187206
|
+
await rename(stage, options.destinationDir);
|
|
187207
|
+
await assertMutationGuard(mutationGuard);
|
|
187208
|
+
} catch (error51) {
|
|
187209
|
+
if (backupCreated && !await optionalLstat2(options.destinationDir)) {
|
|
187210
|
+
await assertMutationGuard(mutationGuard);
|
|
187211
|
+
await rename(backup, options.destinationDir);
|
|
187212
|
+
backupCreated = false;
|
|
187213
|
+
await assertMutationGuard(mutationGuard);
|
|
187214
|
+
await rm(transaction, { force: true });
|
|
187215
|
+
transactionCreated = false;
|
|
187216
|
+
await assertMutationGuard(mutationGuard);
|
|
187217
|
+
}
|
|
187218
|
+
throw error51;
|
|
187219
|
+
}
|
|
187220
|
+
if (backupCreated) {
|
|
187221
|
+
await assertMutationGuard(mutationGuard);
|
|
187222
|
+
await rm(backup, { recursive: true, force: true });
|
|
187223
|
+
backupCreated = false;
|
|
187224
|
+
await assertMutationGuard(mutationGuard);
|
|
187225
|
+
await rm(transaction, { force: true });
|
|
187226
|
+
transactionCreated = false;
|
|
187227
|
+
await assertMutationGuard(mutationGuard);
|
|
187228
|
+
}
|
|
187229
|
+
} finally {
|
|
187230
|
+
await assertMutationGuard(mutationGuard);
|
|
187231
|
+
await rm(stage, { recursive: true, force: true });
|
|
187232
|
+
if (backupCreated && !await optionalLstat2(options.destinationDir)) {
|
|
187233
|
+
await assertMutationGuard(mutationGuard);
|
|
187234
|
+
await rename(backup, options.destinationDir);
|
|
187235
|
+
backupCreated = false;
|
|
187236
|
+
await assertMutationGuard(mutationGuard);
|
|
187237
|
+
}
|
|
187238
|
+
if (transactionCreated && !backupCreated) {
|
|
187239
|
+
await rm(transaction, { force: true });
|
|
187240
|
+
}
|
|
187241
|
+
}
|
|
187242
|
+
}
|
|
187243
|
+
async function captureMutationGuard(parent) {
|
|
187244
|
+
const stat = await lstat2(parent);
|
|
187245
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
187246
|
+
throw new Error(`Skill install parent is unsafe: ${parent}`);
|
|
187247
|
+
}
|
|
187248
|
+
const realParent = await realpath2(parent);
|
|
187249
|
+
if (realParent !== parent) {
|
|
187250
|
+
throw new Error(`Skill install parent changed during setup: ${parent}`);
|
|
187251
|
+
}
|
|
187252
|
+
return {
|
|
187253
|
+
parent,
|
|
187254
|
+
realParent,
|
|
187255
|
+
device: stat.dev,
|
|
187256
|
+
inode: stat.ino
|
|
187257
|
+
};
|
|
187258
|
+
}
|
|
187259
|
+
async function assertMutationGuard(guard) {
|
|
187260
|
+
const stat = await optionalLstat2(guard.parent);
|
|
187261
|
+
if (!stat || stat.isSymbolicLink() || !stat.isDirectory() || stat.dev !== guard.device || stat.ino !== guard.inode || await realpath2(guard.parent) !== guard.realParent) {
|
|
187262
|
+
throw new Error(`Skill install parent changed during setup: ${guard.parent}. No further path mutation was attempted.`);
|
|
187263
|
+
}
|
|
187264
|
+
}
|
|
187265
|
+
async function recoverInterruptedReplacement(options) {
|
|
187266
|
+
const parent = dirname6(options.destinationDir);
|
|
187267
|
+
const backup = join5(parent, SKILL_INSTALL_BACKUP);
|
|
187268
|
+
const transaction = join5(parent, SKILL_INSTALL_TRANSACTION);
|
|
187269
|
+
const backupStat = await optionalLstat2(backup);
|
|
187270
|
+
const transactionStat = await optionalLstat2(transaction);
|
|
187271
|
+
if (!backupStat && !transactionStat)
|
|
187272
|
+
return { status: "none" };
|
|
187273
|
+
if (backupStat?.isSymbolicLink()) {
|
|
187274
|
+
throw new Error(`Interrupted Skill backup is a symlink: ${backup}`);
|
|
187275
|
+
}
|
|
187276
|
+
if (transactionStat?.isSymbolicLink() || !transactionStat?.isFile()) {
|
|
187277
|
+
throw new Error(`Interrupted Skill transaction marker is not a regular file: ${transaction}`);
|
|
187278
|
+
}
|
|
187279
|
+
if (!backupStat) {
|
|
187280
|
+
await assertExistingSegmentsSafe(options.trustedRoot, transaction);
|
|
187281
|
+
} else {
|
|
187282
|
+
await assertExistingSegmentsSafe(options.trustedRoot, backup);
|
|
187283
|
+
}
|
|
187284
|
+
const parsed = await readReplacementTransaction(transaction);
|
|
187285
|
+
if (parsed.destinationName !== basename2(options.destinationDir) || parsed.backupName !== SKILL_INSTALL_BACKUP) {
|
|
187286
|
+
throw new Error(`Interrupted Skill transaction does not match ${options.destinationDir}: ${transaction}`);
|
|
187287
|
+
}
|
|
187288
|
+
const destinationStat = await optionalLstat2(options.destinationDir);
|
|
187289
|
+
if (backupStat && destinationStat) {
|
|
187290
|
+
throw interruptedReplacementConflict(options.destinationDir, backup);
|
|
187291
|
+
}
|
|
187292
|
+
if (!backupStat && !destinationStat) {
|
|
187293
|
+
throw new Error(`Interrupted Skill transaction has neither destination nor backup: ${transaction}`);
|
|
187294
|
+
}
|
|
187295
|
+
if (!options.write) {
|
|
187296
|
+
return {
|
|
187297
|
+
status: "dry-run",
|
|
187298
|
+
detail: backupStat ? `recover interrupted skill replacement from ${SKILL_INSTALL_BACKUP}` : `clean completed Skill replacement marker ${SKILL_INSTALL_TRANSACTION}`
|
|
187299
|
+
};
|
|
187300
|
+
}
|
|
187301
|
+
const mutationGuard = await captureMutationGuard(parent);
|
|
187302
|
+
await assertMutationGuard(mutationGuard);
|
|
187303
|
+
if (backupStat) {
|
|
187304
|
+
await rename(backup, options.destinationDir);
|
|
187305
|
+
}
|
|
187306
|
+
await assertMutationGuard(mutationGuard);
|
|
187307
|
+
await rm(transaction, { force: true });
|
|
187308
|
+
await assertMutationGuard(mutationGuard);
|
|
187309
|
+
return {
|
|
187310
|
+
status: "updated",
|
|
187311
|
+
detail: backupStat ? `recovered interrupted skill replacement from ${SKILL_INSTALL_BACKUP}; rerun setup to apply the requested install` : `cleaned completed Skill replacement marker ${SKILL_INSTALL_TRANSACTION}; rerun setup to verify the installed Skill`
|
|
187312
|
+
};
|
|
187313
|
+
}
|
|
187314
|
+
function interruptedReplacementConflict(destinationDir, backup) {
|
|
187315
|
+
return new Error(`Interrupted Skill replacement is ambiguous because both ${destinationDir} and ${backup} exist. Inspect both directories and remove ${backup} only after preserving any needed files.`);
|
|
187316
|
+
}
|
|
187317
|
+
async function writeReplacementTransaction(transactionPath, destinationDir) {
|
|
187318
|
+
const transaction = {
|
|
187319
|
+
schemaVersion: 1,
|
|
187320
|
+
installer: "@kyo-so/cli",
|
|
187321
|
+
destinationName: basename2(destinationDir),
|
|
187322
|
+
backupName: SKILL_INSTALL_BACKUP
|
|
187323
|
+
};
|
|
187324
|
+
const temporaryPath = `${transactionPath}.tmp-${randomUUID()}`;
|
|
187325
|
+
await writeFile3(temporaryPath, `${JSON.stringify(transaction, null, 2)}
|
|
187326
|
+
`, {
|
|
187327
|
+
encoding: "utf8",
|
|
187328
|
+
flag: "wx"
|
|
187329
|
+
});
|
|
187330
|
+
try {
|
|
187331
|
+
await link(temporaryPath, transactionPath);
|
|
187332
|
+
} finally {
|
|
187333
|
+
await rm(temporaryPath, { force: true });
|
|
187334
|
+
}
|
|
187335
|
+
}
|
|
187336
|
+
async function readReplacementTransaction(transactionPath) {
|
|
187337
|
+
let parsed;
|
|
187338
|
+
try {
|
|
187339
|
+
parsed = JSON.parse(await readFile5(transactionPath, "utf8"));
|
|
187340
|
+
} catch (error51) {
|
|
187341
|
+
throw new Error(`Interrupted Skill transaction is invalid: ${transactionPath}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
187342
|
+
}
|
|
187343
|
+
if (!isReplacementTransaction(parsed)) {
|
|
187344
|
+
throw new Error(`Interrupted Skill transaction has an invalid schema: ${transactionPath}`);
|
|
187345
|
+
}
|
|
187346
|
+
return parsed;
|
|
187347
|
+
}
|
|
187348
|
+
function isReplacementTransaction(value) {
|
|
187349
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
187350
|
+
return false;
|
|
187351
|
+
}
|
|
187352
|
+
const candidate = value;
|
|
187353
|
+
return candidate.schemaVersion === 1 && candidate.installer === "@kyo-so/cli" && typeof candidate.destinationName === "string" && candidate.destinationName.length > 0 && candidate.backupName === SKILL_INSTALL_BACKUP;
|
|
187354
|
+
}
|
|
187355
|
+
async function copySkillFiles(sourceDir, destinationDir) {
|
|
187356
|
+
for (const file2 of await listRegularFiles(sourceDir)) {
|
|
187357
|
+
const destination = join5(destinationDir, ...file2.relativePath.split("/"));
|
|
187358
|
+
await mkdir4(dirname6(destination), { recursive: true });
|
|
187359
|
+
await copyFile(file2.absolutePath, destination);
|
|
187360
|
+
}
|
|
187361
|
+
}
|
|
187362
|
+
async function readInstallMarker(destinationDir) {
|
|
187363
|
+
const markerPath = join5(destinationDir, SKILL_INSTALL_MARKER);
|
|
187364
|
+
const markerStat = await optionalLstat2(markerPath);
|
|
187365
|
+
if (!markerStat)
|
|
187366
|
+
return { kind: "missing" };
|
|
187367
|
+
if (markerStat.isSymbolicLink() || !markerStat.isFile()) {
|
|
187368
|
+
return {
|
|
187369
|
+
kind: "invalid",
|
|
187370
|
+
reason: `${SKILL_INSTALL_MARKER} is not a regular file`
|
|
187371
|
+
};
|
|
187372
|
+
}
|
|
187373
|
+
try {
|
|
187374
|
+
const parsed = JSON.parse(await readFile5(markerPath, "utf8"));
|
|
187375
|
+
if (!isInstallMarker(parsed)) {
|
|
187376
|
+
return {
|
|
187377
|
+
kind: "invalid",
|
|
187378
|
+
reason: `${SKILL_INSTALL_MARKER} has an invalid schema`
|
|
187379
|
+
};
|
|
187380
|
+
}
|
|
187381
|
+
return { kind: "valid", value: parsed };
|
|
187382
|
+
} catch (error51) {
|
|
187383
|
+
return {
|
|
187384
|
+
kind: "invalid",
|
|
187385
|
+
reason: `${SKILL_INSTALL_MARKER} is invalid: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
187386
|
+
};
|
|
187387
|
+
}
|
|
187388
|
+
}
|
|
187389
|
+
async function writeInstallMarker(destinationDir) {
|
|
187390
|
+
const marker = {
|
|
187391
|
+
schemaVersion: 1,
|
|
187392
|
+
installer: "@kyo-so/cli",
|
|
187393
|
+
cliVersion: KYOSO_VERSION,
|
|
187394
|
+
digest: CURRENT_SKILL_DIGEST
|
|
187395
|
+
};
|
|
187396
|
+
const markerPath = join5(destinationDir, SKILL_INSTALL_MARKER);
|
|
187397
|
+
const temporaryPath = join5(destinationDir, `${SKILL_INSTALL_MARKER}.tmp-${randomUUID()}`);
|
|
187398
|
+
await writeFile3(temporaryPath, `${JSON.stringify(marker, null, 2)}
|
|
187399
|
+
`, {
|
|
187400
|
+
encoding: "utf8",
|
|
187401
|
+
flag: "wx"
|
|
187402
|
+
});
|
|
187403
|
+
try {
|
|
187404
|
+
await rename(temporaryPath, markerPath);
|
|
187405
|
+
} finally {
|
|
187406
|
+
await rm(temporaryPath, { force: true });
|
|
187407
|
+
}
|
|
187408
|
+
}
|
|
187409
|
+
function isInstallMarker(value) {
|
|
187410
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
187411
|
+
return false;
|
|
187412
|
+
}
|
|
187413
|
+
const candidate = value;
|
|
187414
|
+
return candidate.schemaVersion === 1 && candidate.installer === "@kyo-so/cli" && typeof candidate.cliVersion === "string" && candidate.cliVersion.length > 0 && typeof candidate.digest === "string" && /^sha256:[0-9a-f]{64}$/.test(candidate.digest);
|
|
187415
|
+
}
|
|
187416
|
+
function copyDetail(sourceDir, destinationDir, action) {
|
|
187417
|
+
return [
|
|
187418
|
+
`${action} ${sourceDir}`,
|
|
187419
|
+
`to ${destinationDir}`,
|
|
187420
|
+
`digest ${CURRENT_SKILL_DIGEST}`
|
|
187421
|
+
].join(`
|
|
187422
|
+
`);
|
|
187423
|
+
}
|
|
187424
|
+
function conflictResult(destinationDir, reason) {
|
|
187425
|
+
return {
|
|
187426
|
+
status: "conflict",
|
|
187427
|
+
path: destinationDir,
|
|
187428
|
+
detail: `${reason}
|
|
187429
|
+
Existing skill was not changed. Rerun with --force to replace only this skill directory.`
|
|
187430
|
+
};
|
|
187431
|
+
}
|
|
187432
|
+
async function optionalLstat2(path) {
|
|
187433
|
+
try {
|
|
187434
|
+
return await lstat2(path);
|
|
187435
|
+
} catch (error51) {
|
|
187436
|
+
if (isMissingPathError3(error51))
|
|
187437
|
+
return;
|
|
187438
|
+
throw error51;
|
|
187439
|
+
}
|
|
187440
|
+
}
|
|
187441
|
+
function isMissingPathError3(error51) {
|
|
187442
|
+
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
187443
|
+
}
|
|
187444
|
+
|
|
187445
|
+
// src/cli/setup.ts
|
|
185957
187446
|
async function runSetup(options) {
|
|
185958
187447
|
const client = parseClient(options.client);
|
|
187448
|
+
validateSkillOnlyOptions(options, client);
|
|
185959
187449
|
const runner = parseRunner(options.runner);
|
|
185960
187450
|
const command = options.command ? parseCommandSpec(options.command) : commandForRunner(runner);
|
|
187451
|
+
const env = options.env ?? process.env;
|
|
187452
|
+
const home = resolveUserHome(env);
|
|
185961
187453
|
const context = {
|
|
185962
187454
|
cwd: options.cwd,
|
|
185963
|
-
home
|
|
185964
|
-
|
|
187455
|
+
home,
|
|
187456
|
+
codexHome: dirname7(resolveCodexConfigPath(env, options.cwd)),
|
|
187457
|
+
env,
|
|
185965
187458
|
write: options.write,
|
|
185966
187459
|
scope: options.global ? "global" : "project",
|
|
187460
|
+
skillOnly: options.skillOnly ?? false,
|
|
187461
|
+
force: options.force ?? false,
|
|
185967
187462
|
mcpCommand: command,
|
|
185968
187463
|
sourceSkillDir: resolveBundledSkillDir()
|
|
185969
187464
|
};
|
|
@@ -186006,50 +187501,103 @@ function buildClaudeMcpEntry(command) {
|
|
|
186006
187501
|
function skillDestination(client, scope, cwd, home) {
|
|
186007
187502
|
if (client === "codex") {
|
|
186008
187503
|
const root2 = scope === "global" ? home : cwd;
|
|
186009
|
-
return
|
|
187504
|
+
return join6(root2, ".agents", "skills", "kyoso-review");
|
|
186010
187505
|
}
|
|
186011
187506
|
const root = scope === "global" ? home : cwd;
|
|
186012
|
-
return
|
|
187507
|
+
return join6(root, ".claude", "skills", "kyoso-review");
|
|
187508
|
+
}
|
|
187509
|
+
function resolveUserHome(env = process.env) {
|
|
187510
|
+
return env.HOME ? resolve7(env.HOME) : homedir3();
|
|
187511
|
+
}
|
|
187512
|
+
function resolveCodexConfigPath(env = process.env, cwd = process.cwd()) {
|
|
187513
|
+
const codexHome = env.CODEX_HOME ? resolve7(cwd, env.CODEX_HOME) : join6(resolveUserHome(env), ".codex");
|
|
187514
|
+
return join6(codexHome, "config.toml");
|
|
187515
|
+
}
|
|
187516
|
+
function detectCodexPluginMcpOverride(options) {
|
|
187517
|
+
const env = options.env ?? process.env;
|
|
187518
|
+
const path = resolveCodexConfigPath(env, options.cwd);
|
|
187519
|
+
if (!existsSync2(path))
|
|
187520
|
+
return { status: "missing", path };
|
|
187521
|
+
try {
|
|
187522
|
+
const parsed = parse5(readTextSync(path));
|
|
187523
|
+
if (hasUnprobedProjectIntegrationOverride(parsed, options.cwd, resolveUserHome(env))) {
|
|
187524
|
+
return { status: "unknown", path };
|
|
187525
|
+
}
|
|
187526
|
+
return {
|
|
187527
|
+
status: nestedMcpEntryStatus(parsed, [
|
|
187528
|
+
"plugins",
|
|
187529
|
+
"kyoso@kyoso",
|
|
187530
|
+
"mcp_servers",
|
|
187531
|
+
"kyoso"
|
|
187532
|
+
]),
|
|
187533
|
+
path
|
|
187534
|
+
};
|
|
187535
|
+
} catch {
|
|
187536
|
+
return { status: "unknown", path };
|
|
187537
|
+
}
|
|
186013
187538
|
}
|
|
186014
187539
|
function detectSetup(options) {
|
|
186015
|
-
const
|
|
187540
|
+
const env = options.env ?? process.env;
|
|
187541
|
+
const home = options.home ? resolve7(options.home) : resolveUserHome(env);
|
|
187542
|
+
const codexConfigPath = options.codexHome ? join6(resolve7(options.codexHome), "config.toml") : options.home ? join6(home, ".codex", "config.toml") : resolveCodexConfigPath(env, options.cwd);
|
|
187543
|
+
const codexMcp = detectCodexMcp(codexConfigPath, options.cwd, home);
|
|
187544
|
+
const claudeMcp = mergeMcpDetections([
|
|
187545
|
+
detectClaudeMcp(join6(options.cwd, ".mcp.json"), options.cwd, home),
|
|
187546
|
+
detectClaudeMcp(join6(home, ".claude.json"), options.cwd, home)
|
|
187547
|
+
]);
|
|
187548
|
+
const codexSkillPaths = existingSkillPaths([
|
|
187549
|
+
join6(options.cwd, ".agents", "skills", "kyoso-review"),
|
|
187550
|
+
join6(home, ".agents", "skills", "kyoso-review")
|
|
187551
|
+
]);
|
|
187552
|
+
const claudeSkillPaths = existingSkillPaths([
|
|
187553
|
+
join6(options.cwd, ".claude", "skills", "kyoso-review"),
|
|
187554
|
+
join6(home, ".claude", "skills", "kyoso-review")
|
|
187555
|
+
]);
|
|
186016
187556
|
return {
|
|
186017
187557
|
codex: {
|
|
186018
|
-
mcp:
|
|
186019
|
-
skill:
|
|
187558
|
+
mcp: codexMcp.status === "enabled",
|
|
187559
|
+
skill: codexSkillPaths.length > 0,
|
|
187560
|
+
manualMcpStatus: codexMcp.status,
|
|
187561
|
+
mcpPaths: codexMcp.paths,
|
|
187562
|
+
skillPaths: codexSkillPaths
|
|
186020
187563
|
},
|
|
186021
187564
|
"claude-code": {
|
|
186022
|
-
mcp:
|
|
186023
|
-
skill:
|
|
187565
|
+
mcp: claudeMcp.status === "enabled",
|
|
187566
|
+
skill: claudeSkillPaths.length > 0,
|
|
187567
|
+
manualMcpStatus: claudeMcp.status,
|
|
187568
|
+
mcpPaths: claudeMcp.paths,
|
|
187569
|
+
skillPaths: claudeSkillPaths
|
|
186024
187570
|
}
|
|
186025
187571
|
};
|
|
186026
187572
|
}
|
|
186027
187573
|
async function setupCodex(context) {
|
|
187574
|
+
if (context.skillOnly) {
|
|
187575
|
+
return [
|
|
187576
|
+
await ensureSkill(context, "codex", "Codex skill"),
|
|
187577
|
+
...singleAgentAdvice(context, "codex")
|
|
187578
|
+
];
|
|
187579
|
+
}
|
|
186028
187580
|
return [
|
|
186029
187581
|
await ensureCodexMcp(context),
|
|
186030
|
-
await ensureSkill(
|
|
186031
|
-
title: "Codex skill",
|
|
186032
|
-
sourceDir: context.sourceSkillDir,
|
|
186033
|
-
destinationDir: skillDestination("codex", context.scope, context.cwd, context.home),
|
|
186034
|
-
write: context.write
|
|
186035
|
-
}),
|
|
187582
|
+
await ensureSkill(context, "codex", "Codex skill"),
|
|
186036
187583
|
...singleAgentAdvice(context, "codex")
|
|
186037
187584
|
];
|
|
186038
187585
|
}
|
|
186039
187586
|
async function setupClaudeCode(context) {
|
|
187587
|
+
if (context.skillOnly) {
|
|
187588
|
+
return [
|
|
187589
|
+
await ensureSkill(context, "claude-code", "Claude Code skill"),
|
|
187590
|
+
...singleAgentAdvice(context, "claude-code")
|
|
187591
|
+
];
|
|
187592
|
+
}
|
|
186040
187593
|
return [
|
|
186041
187594
|
await ensureClaudeMcp(context),
|
|
186042
|
-
await ensureSkill(
|
|
186043
|
-
title: "Claude Code skill",
|
|
186044
|
-
sourceDir: context.sourceSkillDir,
|
|
186045
|
-
destinationDir: skillDestination("claude-code", context.scope, context.cwd, context.home),
|
|
186046
|
-
write: context.write
|
|
186047
|
-
}),
|
|
187595
|
+
await ensureSkill(context, "claude-code", "Claude Code skill"),
|
|
186048
187596
|
...singleAgentAdvice(context, "claude-code")
|
|
186049
187597
|
];
|
|
186050
187598
|
}
|
|
186051
187599
|
async function ensureCodexMcp(context) {
|
|
186052
|
-
const configPath =
|
|
187600
|
+
const configPath = join6(context.codexHome, "config.toml");
|
|
186053
187601
|
const snippet = buildCodexMcpToml(context.mcpCommand);
|
|
186054
187602
|
const current = await readOptionalFile(configPath);
|
|
186055
187603
|
if (hasCodexMcpContent(current)) {
|
|
@@ -186057,7 +187605,7 @@ async function ensureCodexMcp(context) {
|
|
|
186057
187605
|
title: "Codex MCP",
|
|
186058
187606
|
status: "skipped",
|
|
186059
187607
|
path: configPath,
|
|
186060
|
-
detail: "existing [mcp_servers.kyoso] kept"
|
|
187608
|
+
detail: codexMcpStatusFromContent(current) === "disabled" ? disabledCodexMcpDetail(configPath) : "existing [mcp_servers.kyoso] kept"
|
|
186061
187609
|
};
|
|
186062
187610
|
}
|
|
186063
187611
|
const detail = diffForAppend(configPath, snippet);
|
|
@@ -186068,8 +187616,8 @@ async function ensureCodexMcp(context) {
|
|
|
186068
187616
|
`) ? `
|
|
186069
187617
|
|
|
186070
187618
|
` : "";
|
|
186071
|
-
await
|
|
186072
|
-
await
|
|
187619
|
+
await mkdir5(dirname7(configPath), { recursive: true });
|
|
187620
|
+
await writeFile4(configPath, `${current}${separator}${snippet}`, "utf8");
|
|
186073
187621
|
return {
|
|
186074
187622
|
title: "Codex MCP",
|
|
186075
187623
|
status: current.length > 0 ? "updated" : "created",
|
|
@@ -186081,15 +187629,15 @@ async function ensureClaudeMcp(context) {
|
|
|
186081
187629
|
if (context.scope === "global") {
|
|
186082
187630
|
return ensureClaudeGlobalMcp(context);
|
|
186083
187631
|
}
|
|
186084
|
-
const configPath =
|
|
187632
|
+
const configPath = join6(context.cwd, ".mcp.json");
|
|
186085
187633
|
const current = await readJsonObject(configPath);
|
|
186086
187634
|
const mcpServers = recordValue(current.mcpServers);
|
|
186087
|
-
if (
|
|
187635
|
+
if (isRecord7(mcpServers.kyoso)) {
|
|
186088
187636
|
return {
|
|
186089
187637
|
title: "Claude Code MCP",
|
|
186090
187638
|
status: "skipped",
|
|
186091
187639
|
path: configPath,
|
|
186092
|
-
detail: "existing mcpServers.kyoso kept"
|
|
187640
|
+
detail: mcpEntryStatus(mcpServers.kyoso) === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
|
|
186093
187641
|
};
|
|
186094
187642
|
}
|
|
186095
187643
|
const next = {
|
|
@@ -186108,7 +187656,7 @@ async function ensureClaudeMcp(context) {
|
|
|
186108
187656
|
detail
|
|
186109
187657
|
};
|
|
186110
187658
|
}
|
|
186111
|
-
await
|
|
187659
|
+
await writeFile4(configPath, `${JSON.stringify(next, null, 2)}
|
|
186112
187660
|
`, "utf8");
|
|
186113
187661
|
return {
|
|
186114
187662
|
title: "Claude Code MCP",
|
|
@@ -186118,13 +187666,14 @@ async function ensureClaudeMcp(context) {
|
|
|
186118
187666
|
};
|
|
186119
187667
|
}
|
|
186120
187668
|
function ensureClaudeGlobalMcp(context) {
|
|
186121
|
-
const configPath =
|
|
186122
|
-
|
|
187669
|
+
const configPath = join6(context.home, ".claude.json");
|
|
187670
|
+
const existingMcp = detectClaudeMcp(configPath, context.cwd, context.home);
|
|
187671
|
+
if (existingMcp.status !== "missing") {
|
|
186123
187672
|
return {
|
|
186124
187673
|
title: "Claude Code MCP",
|
|
186125
187674
|
status: "skipped",
|
|
186126
187675
|
path: configPath,
|
|
186127
|
-
detail: "existing mcpServers.kyoso kept"
|
|
187676
|
+
detail: existingMcp.status === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
|
|
186128
187677
|
};
|
|
186129
187678
|
}
|
|
186130
187679
|
const json2 = JSON.stringify(buildClaudeMcpEntry(context.mcpCommand));
|
|
@@ -186138,7 +187687,7 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
186138
187687
|
detail: commandLine
|
|
186139
187688
|
};
|
|
186140
187689
|
}
|
|
186141
|
-
const result =
|
|
187690
|
+
const result = spawnSync2("claude", args, { encoding: "utf8" });
|
|
186142
187691
|
if (result.status !== 0) {
|
|
186143
187692
|
throw new Error(result.stderr || result.stdout || "claude mcp add-json failed");
|
|
186144
187693
|
}
|
|
@@ -186149,43 +187698,25 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
186149
187698
|
detail: commandLine
|
|
186150
187699
|
};
|
|
186151
187700
|
}
|
|
186152
|
-
async function ensureSkill(
|
|
186153
|
-
const
|
|
186154
|
-
|
|
186155
|
-
|
|
186156
|
-
|
|
186157
|
-
|
|
186158
|
-
|
|
186159
|
-
detail: "existing kyoso-review skill kept"
|
|
186160
|
-
};
|
|
186161
|
-
}
|
|
186162
|
-
const detail = [
|
|
186163
|
-
`copy ${options.sourceDir}`,
|
|
186164
|
-
`to ${options.destinationDir}`
|
|
186165
|
-
].join(`
|
|
186166
|
-
`);
|
|
186167
|
-
if (!options.write) {
|
|
186168
|
-
return {
|
|
186169
|
-
title: options.title,
|
|
186170
|
-
status: "dry-run",
|
|
186171
|
-
path: options.destinationDir,
|
|
186172
|
-
detail
|
|
186173
|
-
};
|
|
186174
|
-
}
|
|
186175
|
-
await mkdir3(dirname4(options.destinationDir), { recursive: true });
|
|
186176
|
-
await cp(options.sourceDir, options.destinationDir, {
|
|
186177
|
-
recursive: true,
|
|
186178
|
-
force: false
|
|
187701
|
+
async function ensureSkill(context, client, title) {
|
|
187702
|
+
const result = await ensureManagedSkill({
|
|
187703
|
+
sourceDir: context.sourceSkillDir,
|
|
187704
|
+
destinationDir: skillDestination(client, context.scope, context.cwd, context.home),
|
|
187705
|
+
trustedRoot: context.scope === "global" ? context.home : context.cwd,
|
|
187706
|
+
write: context.write,
|
|
187707
|
+
force: context.force
|
|
186179
187708
|
});
|
|
186180
187709
|
return {
|
|
186181
|
-
title
|
|
186182
|
-
|
|
186183
|
-
path: options.destinationDir,
|
|
186184
|
-
detail
|
|
187710
|
+
title,
|
|
187711
|
+
...result
|
|
186185
187712
|
};
|
|
186186
187713
|
}
|
|
186187
187714
|
function renderSetupOverview(context) {
|
|
186188
|
-
const detected = detectSetup({
|
|
187715
|
+
const detected = detectSetup({
|
|
187716
|
+
cwd: context.cwd,
|
|
187717
|
+
home: context.home,
|
|
187718
|
+
codexHome: context.codexHome
|
|
187719
|
+
});
|
|
186189
187720
|
return [
|
|
186190
187721
|
"Kyoso setup",
|
|
186191
187722
|
"",
|
|
@@ -186194,8 +187725,10 @@ function renderSetupOverview(context) {
|
|
|
186194
187725
|
` claude-code: MCP ${statusWord(detected["claude-code"].mcp)}, skill ${statusWord(detected["claude-code"].skill)}`,
|
|
186195
187726
|
"",
|
|
186196
187727
|
"Commands",
|
|
186197
|
-
" kyoso setup codex [--write] [--runner npx|bunx] [--global]",
|
|
186198
|
-
" kyoso setup claude-code [--write] [--runner npx|bunx] [--global]",
|
|
187728
|
+
" kyoso setup codex [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]",
|
|
187729
|
+
" kyoso setup claude-code [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]",
|
|
187730
|
+
" kyoso setup codex --skill-only [--write] [--global] [--force]",
|
|
187731
|
+
" kyoso setup claude-code --skill-only [--write] [--global] [--force]",
|
|
186199
187732
|
"",
|
|
186200
187733
|
`Default MCP command: ${context.mcpCommand.command} ${context.mcpCommand.args.join(" ")}`,
|
|
186201
187734
|
"Dry-run is the default. Add --write to modify files."
|
|
@@ -186221,6 +187754,19 @@ function parseClient(client) {
|
|
|
186221
187754
|
return client;
|
|
186222
187755
|
throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
|
|
186223
187756
|
}
|
|
187757
|
+
function validateSkillOnlyOptions(options, client) {
|
|
187758
|
+
if (!options.skillOnly)
|
|
187759
|
+
return;
|
|
187760
|
+
if (!client) {
|
|
187761
|
+
throw new Error("--skill-only requires setup client codex or claude-code.");
|
|
187762
|
+
}
|
|
187763
|
+
if (options.runner !== undefined) {
|
|
187764
|
+
throw new Error("--skill-only cannot be combined with --runner.");
|
|
187765
|
+
}
|
|
187766
|
+
if (options.command !== undefined) {
|
|
187767
|
+
throw new Error("--skill-only cannot be combined with --command.");
|
|
187768
|
+
}
|
|
187769
|
+
}
|
|
186224
187770
|
function parseRunner(runner) {
|
|
186225
187771
|
if (runner === undefined || runner === "npx")
|
|
186226
187772
|
return "npx";
|
|
@@ -186268,21 +187814,21 @@ function splitCommand(value) {
|
|
|
186268
187814
|
return parts;
|
|
186269
187815
|
}
|
|
186270
187816
|
function resolveBundledSkillDir() {
|
|
186271
|
-
const start =
|
|
187817
|
+
const start = dirname7(fileURLToPath(import.meta.url));
|
|
186272
187818
|
let current = start;
|
|
186273
187819
|
for (let depth = 0;depth < 5; depth += 1) {
|
|
186274
|
-
const candidate =
|
|
186275
|
-
if (
|
|
187820
|
+
const candidate = join6(current, ".agents", "skills", "kyoso-review");
|
|
187821
|
+
if (existsSync2(join6(candidate, "SKILL.md")))
|
|
186276
187822
|
return candidate;
|
|
186277
|
-
current =
|
|
187823
|
+
current = dirname7(current);
|
|
186278
187824
|
}
|
|
186279
187825
|
throw new Error("Bundled kyoso-review skill was not found in this package.");
|
|
186280
187826
|
}
|
|
186281
187827
|
async function readOptionalFile(path) {
|
|
186282
187828
|
try {
|
|
186283
|
-
return await
|
|
187829
|
+
return await readFile6(path, "utf8");
|
|
186284
187830
|
} catch (error51) {
|
|
186285
|
-
if (
|
|
187831
|
+
if (isMissingPathError4(error51))
|
|
186286
187832
|
return "";
|
|
186287
187833
|
throw error51;
|
|
186288
187834
|
}
|
|
@@ -186292,41 +187838,180 @@ async function readJsonObject(path) {
|
|
|
186292
187838
|
if (content.trim().length === 0)
|
|
186293
187839
|
return {};
|
|
186294
187840
|
const parsed = JSON.parse(content);
|
|
186295
|
-
if (!
|
|
187841
|
+
if (!isRecord7(parsed))
|
|
186296
187842
|
throw new Error(`${path} must contain a JSON object`);
|
|
186297
187843
|
return parsed;
|
|
186298
187844
|
}
|
|
186299
|
-
function hasCodexMcp(path) {
|
|
186300
|
-
return existsSync(path) && hasCodexMcpContent(readTextSync(path));
|
|
186301
|
-
}
|
|
186302
187845
|
function hasCodexMcpContent(content) {
|
|
186303
187846
|
return /^\s*\[mcp_servers\.(?:"kyoso"|kyoso)]\s*$/m.test(content);
|
|
186304
187847
|
}
|
|
186305
|
-
function
|
|
186306
|
-
|
|
186307
|
-
|
|
187848
|
+
function codexMcpStatusFromContent(content) {
|
|
187849
|
+
try {
|
|
187850
|
+
const parsed = parse5(content);
|
|
187851
|
+
if (!isRecord7(parsed))
|
|
187852
|
+
return "unknown";
|
|
187853
|
+
if (!isRecord7(parsed.mcp_servers))
|
|
187854
|
+
return "missing";
|
|
187855
|
+
if (!("kyoso" in parsed.mcp_servers))
|
|
187856
|
+
return "missing";
|
|
187857
|
+
return mcpEntryStatus(parsed.mcp_servers.kyoso);
|
|
187858
|
+
} catch {
|
|
187859
|
+
return "unknown";
|
|
187860
|
+
}
|
|
187861
|
+
}
|
|
187862
|
+
function disabledCodexMcpDetail(configPath) {
|
|
187863
|
+
return [
|
|
187864
|
+
"existing [mcp_servers.kyoso] is disabled and was kept unchanged.",
|
|
187865
|
+
`To re-enable it, edit ${configPath} and change enabled = false to enabled = true.`
|
|
187866
|
+
].join(" ");
|
|
187867
|
+
}
|
|
187868
|
+
function disabledClaudeMcpDetail(configPath) {
|
|
187869
|
+
return [
|
|
187870
|
+
"existing mcpServers.kyoso is disabled and was kept unchanged.",
|
|
187871
|
+
`To re-enable it, edit ${configPath} and change "enabled": false to "enabled": true.`
|
|
187872
|
+
].join(" ");
|
|
187873
|
+
}
|
|
187874
|
+
function detectCodexMcp(path, cwd, home) {
|
|
187875
|
+
if (!existsSync2(path))
|
|
187876
|
+
return { status: "missing", paths: [] };
|
|
187877
|
+
try {
|
|
187878
|
+
const parsed = parse5(readTextSync(path));
|
|
187879
|
+
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
187880
|
+
return { status: "unknown", paths: [path] };
|
|
187881
|
+
}
|
|
187882
|
+
if (!isRecord7(parsed))
|
|
187883
|
+
return { status: "unknown", paths: [path] };
|
|
187884
|
+
if (!("mcp_servers" in parsed))
|
|
187885
|
+
return { status: "missing", paths: [] };
|
|
187886
|
+
if (!isRecord7(parsed.mcp_servers)) {
|
|
187887
|
+
return { status: "unknown", paths: [path] };
|
|
187888
|
+
}
|
|
187889
|
+
if (!("kyoso" in parsed.mcp_servers)) {
|
|
187890
|
+
return { status: "missing", paths: [] };
|
|
187891
|
+
}
|
|
187892
|
+
return { status: mcpEntryStatus(parsed.mcp_servers.kyoso), paths: [path] };
|
|
187893
|
+
} catch {
|
|
187894
|
+
return { status: "unknown", paths: [path] };
|
|
187895
|
+
}
|
|
187896
|
+
}
|
|
187897
|
+
function detectClaudeMcp(path, cwd, home) {
|
|
187898
|
+
if (!existsSync2(path))
|
|
187899
|
+
return { status: "missing", paths: [] };
|
|
186308
187900
|
try {
|
|
186309
187901
|
const parsed = JSON.parse(readTextSync(path));
|
|
186310
|
-
|
|
187902
|
+
const statuses = jsonMcpStatuses(parsed, cwd, home);
|
|
187903
|
+
if (statuses.length === 0)
|
|
187904
|
+
return { status: "missing", paths: [] };
|
|
187905
|
+
return { status: mergeMcpStatuses(statuses), paths: [path] };
|
|
186311
187906
|
} catch {
|
|
186312
|
-
return
|
|
187907
|
+
return { status: "unknown", paths: [path] };
|
|
187908
|
+
}
|
|
187909
|
+
}
|
|
187910
|
+
function jsonMcpStatuses(value, cwd, home) {
|
|
187911
|
+
if (!isRecord7(value))
|
|
187912
|
+
return ["unknown"];
|
|
187913
|
+
const statuses = directMcpStatuses(value);
|
|
187914
|
+
if (!("projects" in value))
|
|
187915
|
+
return statuses;
|
|
187916
|
+
if (!isRecord7(value.projects))
|
|
187917
|
+
return [...statuses, "unknown"];
|
|
187918
|
+
const currentProject = normalizeProjectPath(cwd, home);
|
|
187919
|
+
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
187920
|
+
if (normalizeProjectPath(projectPath, home) !== currentProject)
|
|
187921
|
+
continue;
|
|
187922
|
+
if (!isRecord7(projectConfig)) {
|
|
187923
|
+
statuses.push("unknown");
|
|
187924
|
+
continue;
|
|
187925
|
+
}
|
|
187926
|
+
statuses.push(...directMcpStatuses(projectConfig));
|
|
186313
187927
|
}
|
|
187928
|
+
return statuses;
|
|
186314
187929
|
}
|
|
186315
|
-
function
|
|
186316
|
-
|
|
187930
|
+
function directMcpStatuses(value) {
|
|
187931
|
+
const statuses = [];
|
|
187932
|
+
if ("mcpServers" in value) {
|
|
187933
|
+
if (!isRecord7(value.mcpServers)) {
|
|
187934
|
+
statuses.push("unknown");
|
|
187935
|
+
} else if ("kyoso" in value.mcpServers) {
|
|
187936
|
+
statuses.push(mcpEntryStatus(value.mcpServers.kyoso));
|
|
187937
|
+
}
|
|
187938
|
+
}
|
|
187939
|
+
return statuses;
|
|
187940
|
+
}
|
|
187941
|
+
function nestedMcpEntryStatus(value, path) {
|
|
187942
|
+
let current = value;
|
|
187943
|
+
for (const key of path) {
|
|
187944
|
+
if (!isRecord7(current))
|
|
187945
|
+
return "unknown";
|
|
187946
|
+
if (!(key in current))
|
|
187947
|
+
return "missing";
|
|
187948
|
+
current = current[key];
|
|
187949
|
+
}
|
|
187950
|
+
return mcpEntryStatus(current);
|
|
187951
|
+
}
|
|
187952
|
+
function hasUnprobedProjectIntegrationOverride(value, cwd, home) {
|
|
187953
|
+
if (!isRecord7(value) || !isRecord7(value.projects))
|
|
186317
187954
|
return false;
|
|
186318
|
-
|
|
186319
|
-
|
|
187955
|
+
const currentProject = normalizeProjectPath(cwd, home);
|
|
187956
|
+
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
187957
|
+
if (normalizeProjectPath(projectPath, home) !== currentProject || !isRecord7(projectConfig)) {
|
|
187958
|
+
continue;
|
|
187959
|
+
}
|
|
187960
|
+
if ("mcp_servers" in projectConfig || "plugins" in projectConfig) {
|
|
187961
|
+
return true;
|
|
187962
|
+
}
|
|
186320
187963
|
}
|
|
186321
|
-
return
|
|
187964
|
+
return false;
|
|
187965
|
+
}
|
|
187966
|
+
function normalizeProjectPath(path, home) {
|
|
187967
|
+
if (path.trim().length === 0)
|
|
187968
|
+
return;
|
|
187969
|
+
const expanded = path === "~" ? home : path.startsWith("~/") || path.startsWith("~\\") ? join6(home, path.slice(2)) : path;
|
|
187970
|
+
const resolved = resolve7(expanded);
|
|
187971
|
+
let normalized;
|
|
187972
|
+
try {
|
|
187973
|
+
normalized = realpathSync2(resolved);
|
|
187974
|
+
} catch {
|
|
187975
|
+
normalized = resolved;
|
|
187976
|
+
}
|
|
187977
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
187978
|
+
}
|
|
187979
|
+
function mcpEntryStatus(value) {
|
|
187980
|
+
if (!isRecord7(value))
|
|
187981
|
+
return "unknown";
|
|
187982
|
+
if (!("enabled" in value))
|
|
187983
|
+
return "enabled";
|
|
187984
|
+
if (value.enabled === true)
|
|
187985
|
+
return "enabled";
|
|
187986
|
+
if (value.enabled === false)
|
|
187987
|
+
return "disabled";
|
|
187988
|
+
return "unknown";
|
|
187989
|
+
}
|
|
187990
|
+
function mergeMcpDetections(detections) {
|
|
187991
|
+
return {
|
|
187992
|
+
status: mergeMcpStatuses(detections.map((detection) => detection.status)),
|
|
187993
|
+
paths: detections.flatMap((detection) => detection.paths)
|
|
187994
|
+
};
|
|
187995
|
+
}
|
|
187996
|
+
function mergeMcpStatuses(statuses) {
|
|
187997
|
+
if (statuses.includes("enabled"))
|
|
187998
|
+
return "enabled";
|
|
187999
|
+
if (statuses.includes("unknown"))
|
|
188000
|
+
return "unknown";
|
|
188001
|
+
if (statuses.includes("disabled"))
|
|
188002
|
+
return "disabled";
|
|
188003
|
+
return "missing";
|
|
188004
|
+
}
|
|
188005
|
+
function existingSkillPaths(paths) {
|
|
188006
|
+
return [...new Set(paths)].filter((path) => existsSync2(join6(path, "SKILL.md")));
|
|
186322
188007
|
}
|
|
186323
188008
|
function readTextSync(path) {
|
|
186324
|
-
return
|
|
188009
|
+
return readFileSync2(path, "utf8");
|
|
186325
188010
|
}
|
|
186326
188011
|
function recordValue(value) {
|
|
186327
188012
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
186328
188013
|
}
|
|
186329
|
-
function
|
|
188014
|
+
function isRecord7(value) {
|
|
186330
188015
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186331
188016
|
}
|
|
186332
188017
|
function diffForAppend(path, snippet) {
|
|
@@ -186357,7 +188042,7 @@ function statusWord(value) {
|
|
|
186357
188042
|
return value ? "ok" : "missing";
|
|
186358
188043
|
}
|
|
186359
188044
|
function singleAgentAdvice(context, client) {
|
|
186360
|
-
if (client === "claude-code" && !
|
|
188045
|
+
if (client === "claude-code" && !commandExists2("codex", context.env)) {
|
|
186361
188046
|
return [
|
|
186362
188047
|
{
|
|
186363
188048
|
title: "Single-agent config",
|
|
@@ -186373,7 +188058,7 @@ function singleAgentAdvice(context, client) {
|
|
|
186373
188058
|
}
|
|
186374
188059
|
];
|
|
186375
188060
|
}
|
|
186376
|
-
if (client === "codex" && !
|
|
188061
|
+
if (client === "codex" && !commandExists2("claude", context.env)) {
|
|
186377
188062
|
return [
|
|
186378
188063
|
{
|
|
186379
188064
|
title: "Single-agent config",
|
|
@@ -186401,11 +188086,11 @@ function shellQuote(value) {
|
|
|
186401
188086
|
return value;
|
|
186402
188087
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
186403
188088
|
}
|
|
186404
|
-
function
|
|
186405
|
-
const paths = env.PATH?.split(
|
|
186406
|
-
return paths.some((path) =>
|
|
188089
|
+
function commandExists2(command, env) {
|
|
188090
|
+
const paths = env.PATH?.split(delimiter2) ?? [];
|
|
188091
|
+
return paths.some((path) => existsSync2(join6(path, command)));
|
|
186407
188092
|
}
|
|
186408
|
-
function
|
|
188093
|
+
function isMissingPathError4(error51) {
|
|
186409
188094
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
186410
188095
|
}
|
|
186411
188096
|
|
|
@@ -186414,11 +188099,11 @@ async function runDoctor(options) {
|
|
|
186414
188099
|
const env = options.env ?? process.env;
|
|
186415
188100
|
const loaded = await loadConfig(options);
|
|
186416
188101
|
const globalConfigPath = resolveGlobalTomlConfigPath(env);
|
|
186417
|
-
const projectTomlPath =
|
|
186418
|
-
const projectTsPath =
|
|
188102
|
+
const projectTomlPath = resolve8(options.cwd, "kyoso.toml");
|
|
188103
|
+
const projectTsPath = resolve8(options.cwd, "kyoso.config.ts");
|
|
186419
188104
|
const lines = ["Kyoso doctor", "", "Runtime"];
|
|
186420
|
-
lines.push(` Bun: ${
|
|
186421
|
-
lines.push(` Node/npm: ${
|
|
188105
|
+
lines.push(` Bun: ${commandExists3("bun", env) ? "ok" : "warning not found"}`);
|
|
188106
|
+
lines.push(` Node/npm: ${commandExists3("npm", env) ? "ok" : "warning npm not found"}`);
|
|
186422
188107
|
lines.push("", "Config");
|
|
186423
188108
|
lines.push(` global config.toml: ${formatLayer(loaded, "global_toml", globalConfigPath)}`);
|
|
186424
188109
|
lines.push(` kyoso.toml: ${formatLayer(loaded, "project_toml", projectTomlPath)}`);
|
|
@@ -186428,36 +188113,37 @@ async function runDoctor(options) {
|
|
|
186428
188113
|
lines.push(` config hash: ${loaded.configHash}`);
|
|
186429
188114
|
for (const warning of loaded.warnings)
|
|
186430
188115
|
lines.push(` warning: ${warning}`);
|
|
186431
|
-
const setup = detectSetup({ cwd: options.cwd,
|
|
188116
|
+
const setup = detectSetup({ cwd: options.cwd, env });
|
|
188117
|
+
const cli = detectCli({ cwd: options.cwd, env });
|
|
188118
|
+
const codexIntegration = determineCodexIntegration({
|
|
188119
|
+
cwd: options.cwd,
|
|
188120
|
+
env,
|
|
188121
|
+
setup: setup.codex,
|
|
188122
|
+
cli,
|
|
188123
|
+
pluginInspector: options.pluginInspector ?? inspectCodexPlugin,
|
|
188124
|
+
mcpListInspector: options.mcpListInspector ?? inspectCodexMcpList
|
|
188125
|
+
});
|
|
188126
|
+
const claudeIntegration = determineClientIntegration("claude-code", setup["claude-code"], cli);
|
|
186432
188127
|
lines.push("", "MCP", " stdio server: ok");
|
|
186433
|
-
lines.push(` Codex registration: ${setup.codex.
|
|
186434
|
-
lines.push(` Claude Code registration: ${setup["claude-code"].
|
|
186435
|
-
if (!setup.codex.mcp) {
|
|
186436
|
-
lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
|
|
186437
|
-
}
|
|
186438
|
-
if (!setup["claude-code"].mcp) {
|
|
186439
|
-
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
186440
|
-
}
|
|
188128
|
+
lines.push(` Codex registration: ${formatManualMcpStatus(setup.codex.manualMcpStatus)}`);
|
|
188129
|
+
lines.push(` Claude Code registration: ${formatManualMcpStatus(setup["claude-code"].manualMcpStatus)}`);
|
|
186441
188130
|
lines.push("", "Skills");
|
|
186442
188131
|
lines.push(` Codex kyoso-review: ${setup.codex.skill ? "ok" : "missing"}`);
|
|
186443
188132
|
lines.push(` Claude Code kyoso-review: ${setup["claude-code"].skill ? "ok" : "missing"}`);
|
|
186444
|
-
|
|
186445
|
-
|
|
186446
|
-
|
|
186447
|
-
if (!setup["claude-code"].skill) {
|
|
186448
|
-
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
186449
|
-
}
|
|
188133
|
+
lines.push("", "Integration");
|
|
188134
|
+
appendIntegration(lines, codexIntegration);
|
|
188135
|
+
appendIntegration(lines, claudeIntegration);
|
|
186450
188136
|
lines.push("", "ACP agents");
|
|
186451
188137
|
const agentCommandExists = {
|
|
186452
|
-
codex:
|
|
186453
|
-
claude:
|
|
188138
|
+
codex: commandExists3(loaded.config.agents.codex.command, env),
|
|
188139
|
+
claude: commandExists3(loaded.config.agents.claude.command, env)
|
|
186454
188140
|
};
|
|
186455
188141
|
for (const agent of ["codex", "claude"]) {
|
|
186456
188142
|
const config2 = loaded.config.agents[agent];
|
|
186457
188143
|
const exists3 = agentCommandExists[agent];
|
|
186458
188144
|
lines.push(` ${agent === "codex" ? "Codex" : "Claude"}: ${exists3 ? "ok" : "warning command not found"}`);
|
|
186459
188145
|
lines.push(` command: ${[config2.command, ...config2.args].join(" ")}`);
|
|
186460
|
-
if (!exists3 && config2.command === "npx" &&
|
|
188146
|
+
if (!exists3 && config2.command === "npx" && commandExists3("bunx", env)) {
|
|
186461
188147
|
lines.push(' hint: set agents.<name>.command = "bunx" in config.toml');
|
|
186462
188148
|
}
|
|
186463
188149
|
if (agent === "claude") {
|
|
@@ -186492,10 +188178,209 @@ async function runDoctor(options) {
|
|
|
186492
188178
|
lines.push(` network default: ${loaded.config.network.defaultMode}`);
|
|
186493
188179
|
lines.push("", "Audit");
|
|
186494
188180
|
lines.push(` directory: ${loaded.config.audit.directory}`);
|
|
188181
|
+
const auditStateRoot = await inspectAuditStateRootCapability({
|
|
188182
|
+
cwd: options.cwd,
|
|
188183
|
+
env
|
|
188184
|
+
});
|
|
188185
|
+
lines.push(` state root: ${auditStateRoot.available ? "available" : "unavailable"}`);
|
|
186495
188186
|
lines.push(` raw agent output: ${loaded.config.audit.includeRawAgentOutput ? "enabled" : "disabled"}`);
|
|
186496
188187
|
return lines.join(`
|
|
186497
188188
|
`);
|
|
186498
188189
|
}
|
|
188190
|
+
function determineCodexIntegration(options) {
|
|
188191
|
+
const fallback = determineClientIntegration("codex", options.setup, options.cli);
|
|
188192
|
+
const plugin = options.pluginInspector({
|
|
188193
|
+
cwd: options.cwd,
|
|
188194
|
+
env: options.env
|
|
188195
|
+
});
|
|
188196
|
+
if (plugin.status === "unsupported") {
|
|
188197
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188198
|
+
`Plugin detection unsupported: ${formatCodexInspectionFailure(plugin.failure)}`
|
|
188199
|
+
], undefined, "not applicable"), "unsupported", "not applicable");
|
|
188200
|
+
}
|
|
188201
|
+
if (plugin.plugin.state === "not_installed") {
|
|
188202
|
+
return withPluginDetails(fallback, "not installed", "not applicable");
|
|
188203
|
+
}
|
|
188204
|
+
if (plugin.plugin.state === "disabled") {
|
|
188205
|
+
return withPluginDetails(withIntegrationWarnings(fallback, ["Plugin disabled."], undefined, undefined), "installed, disabled", "disabled with Plugin");
|
|
188206
|
+
}
|
|
188207
|
+
const pluginWarnings = pluginSkillWarnings(options.setup);
|
|
188208
|
+
const override = detectCodexPluginMcpOverride({
|
|
188209
|
+
cwd: options.cwd,
|
|
188210
|
+
env: options.env
|
|
188211
|
+
});
|
|
188212
|
+
if (options.setup.manualMcpStatus === "unknown") {
|
|
188213
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188214
|
+
...pluginWarnings,
|
|
188215
|
+
"Plugin MCP origin is unknown because the manual MCP configuration could not be classified."
|
|
188216
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
188217
|
+
}
|
|
188218
|
+
if (options.setup.manualMcpStatus === "enabled" && override.status !== "disabled") {
|
|
188219
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188220
|
+
...fallback,
|
|
188221
|
+
mode: "manual-mcp",
|
|
188222
|
+
advice: integrationAdvice("manual-mcp", "codex")
|
|
188223
|
+
}, [
|
|
188224
|
+
...pluginWarnings,
|
|
188225
|
+
"Plugin and manual Codex MCP registrations coexist; neither registration was changed.",
|
|
188226
|
+
"Plugin MCP origin is unknown while a manual MCP registration is enabled."
|
|
188227
|
+
], undefined, "unknown"), "installed, enabled", "unknown");
|
|
188228
|
+
}
|
|
188229
|
+
if (override.status === "unknown") {
|
|
188230
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188231
|
+
...pluginWarnings,
|
|
188232
|
+
"Plugin MCP override could not be safely classified from the Codex configuration."
|
|
188233
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
188234
|
+
}
|
|
188235
|
+
if (override.status === "disabled" && options.setup.manualMcpStatus === "enabled") {
|
|
188236
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188237
|
+
...fallback,
|
|
188238
|
+
mode: "manual-mcp",
|
|
188239
|
+
advice: integrationAdvice("manual-mcp", "codex")
|
|
188240
|
+
}, [
|
|
188241
|
+
...pluginWarnings,
|
|
188242
|
+
"Plugin and manual Codex MCP registrations coexist; neither registration was changed.",
|
|
188243
|
+
"Plugin MCP is disabled by configuration override while the manual MCP remains enabled."
|
|
188244
|
+
], undefined, "disabled by configuration override"), "installed, enabled", "disabled by configuration override");
|
|
188245
|
+
}
|
|
188246
|
+
if (override.status === "disabled" && options.setup.manualMcpStatus === "disabled") {
|
|
188247
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188248
|
+
...fallback,
|
|
188249
|
+
mode: "plugin-skill",
|
|
188250
|
+
advice: integrationAdvice("plugin-skill", "codex")
|
|
188251
|
+
}, pluginWarnings, undefined, "disabled by configuration override"), "installed, enabled", "disabled by configuration override");
|
|
188252
|
+
}
|
|
188253
|
+
if (options.setup.manualMcpStatus === "disabled") {
|
|
188254
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188255
|
+
...fallback,
|
|
188256
|
+
mode: "plugin-mcp",
|
|
188257
|
+
advice: integrationAdvice("plugin-mcp", "codex")
|
|
188258
|
+
}, pluginWarnings, undefined, "enabled (manual registration is disabled)"), "installed, enabled", "enabled (manual registration is disabled)");
|
|
188259
|
+
}
|
|
188260
|
+
const effectiveMcp = options.mcpListInspector({
|
|
188261
|
+
cwd: options.cwd,
|
|
188262
|
+
env: options.env
|
|
188263
|
+
});
|
|
188264
|
+
if (effectiveMcp.status === "unsupported") {
|
|
188265
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188266
|
+
...pluginWarnings,
|
|
188267
|
+
`Plugin MCP effective-state check unsupported: ${formatCodexInspectionFailure(effectiveMcp.failure)}`
|
|
188268
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
188269
|
+
}
|
|
188270
|
+
const expectedEffectiveState = override.status === "disabled" ? "disabled" : "enabled";
|
|
188271
|
+
if (effectiveMcp.kyoso === "enabled" && expectedEffectiveState === "enabled") {
|
|
188272
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188273
|
+
...fallback,
|
|
188274
|
+
mode: "plugin-mcp",
|
|
188275
|
+
advice: integrationAdvice("plugin-mcp", "codex")
|
|
188276
|
+
}, pluginWarnings, undefined, "enabled"), "installed, enabled", "enabled");
|
|
188277
|
+
}
|
|
188278
|
+
if (effectiveMcp.kyoso === "disabled" && expectedEffectiveState === "disabled") {
|
|
188279
|
+
return withPluginDetails(withIntegrationWarnings({
|
|
188280
|
+
...fallback,
|
|
188281
|
+
mode: "plugin-skill",
|
|
188282
|
+
advice: integrationAdvice("plugin-skill", "codex")
|
|
188283
|
+
}, pluginWarnings, undefined, "disabled"), "installed, enabled", "disabled");
|
|
188284
|
+
}
|
|
188285
|
+
if (effectiveMcp.kyoso === "enabled" || effectiveMcp.kyoso === "disabled") {
|
|
188286
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188287
|
+
...pluginWarnings,
|
|
188288
|
+
"Plugin MCP effective state does not match the recorded configuration precedence; Plugin MCP mode was not inferred."
|
|
188289
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
188290
|
+
}
|
|
188291
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
188292
|
+
...pluginWarnings,
|
|
188293
|
+
"Plugin MCP effective state is unknown; Plugin MCP mode was not inferred."
|
|
188294
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
188295
|
+
}
|
|
188296
|
+
function determineClientIntegration(client, setup, cli) {
|
|
188297
|
+
const integration = determineNonPluginIntegration({
|
|
188298
|
+
manualMcpStatus: setup.manualMcpStatus,
|
|
188299
|
+
hasSkill: setup.skill,
|
|
188300
|
+
cli
|
|
188301
|
+
});
|
|
188302
|
+
return {
|
|
188303
|
+
client: client === "codex" ? "Codex" : "Claude Code",
|
|
188304
|
+
commandClient: client,
|
|
188305
|
+
mode: integration.mode,
|
|
188306
|
+
setup,
|
|
188307
|
+
cli,
|
|
188308
|
+
warnings: integration.warnings,
|
|
188309
|
+
advice: integrationAdvice(integration.mode, client)
|
|
188310
|
+
};
|
|
188311
|
+
}
|
|
188312
|
+
function withPluginDetails(integration, plugin, pluginMcp) {
|
|
188313
|
+
return { ...integration, plugin, pluginMcp };
|
|
188314
|
+
}
|
|
188315
|
+
function withIntegrationWarnings(integration, warnings, mode, pluginMcp) {
|
|
188316
|
+
const nextMode = mode ?? integration.mode;
|
|
188317
|
+
return {
|
|
188318
|
+
...integration,
|
|
188319
|
+
mode: nextMode,
|
|
188320
|
+
warnings: [...integration.warnings, ...warnings],
|
|
188321
|
+
advice: integrationAdvice(nextMode, integration.commandClient),
|
|
188322
|
+
...pluginMcp ? { pluginMcp } : {}
|
|
188323
|
+
};
|
|
188324
|
+
}
|
|
188325
|
+
function pluginSkillWarnings(setup) {
|
|
188326
|
+
if (!setup.skill)
|
|
188327
|
+
return [];
|
|
188328
|
+
return [
|
|
188329
|
+
`Plugin Skill and manual Skill copy coexist; priority is not inferred. Manual Skill path(s): ${setup.skillPaths.join(", ")}`
|
|
188330
|
+
];
|
|
188331
|
+
}
|
|
188332
|
+
function appendIntegration(lines, integration) {
|
|
188333
|
+
lines.push(` ${integration.client} integration: ${integration.mode}`);
|
|
188334
|
+
lines.push(` manual MCP: ${formatManualMcpStatus(integration.setup.manualMcpStatus)}`);
|
|
188335
|
+
if (integration.setup.mcpPaths.length > 0) {
|
|
188336
|
+
lines.push(` manual MCP path(s): ${integration.setup.mcpPaths.join(", ")}`);
|
|
188337
|
+
}
|
|
188338
|
+
lines.push(` Skill: ${integration.setup.skill ? "ok" : "missing"}`);
|
|
188339
|
+
if (integration.setup.skillPaths.length > 0) {
|
|
188340
|
+
lines.push(` manual Skill path(s): ${integration.setup.skillPaths.join(", ")}`);
|
|
188341
|
+
}
|
|
188342
|
+
lines.push(` CLI: ${formatCliAvailability(integration.cli.kyoso)}`);
|
|
188343
|
+
lines.push(` npx: ${integration.cli.npx ? "available" : "missing"}`);
|
|
188344
|
+
lines.push(` bunx: ${integration.cli.bunx ? "available" : "missing"}`);
|
|
188345
|
+
if (integration.plugin)
|
|
188346
|
+
lines.push(` Plugin: ${integration.plugin}`);
|
|
188347
|
+
if (integration.pluginMcp) {
|
|
188348
|
+
lines.push(` Plugin MCP: ${integration.pluginMcp}`);
|
|
188349
|
+
}
|
|
188350
|
+
for (const warning of integration.warnings) {
|
|
188351
|
+
lines.push(` warning: ${warning}`);
|
|
188352
|
+
}
|
|
188353
|
+
lines.push(` ${integration.advice}`);
|
|
188354
|
+
}
|
|
188355
|
+
function formatManualMcpStatus(status) {
|
|
188356
|
+
if (status === "enabled")
|
|
188357
|
+
return "ok";
|
|
188358
|
+
return status;
|
|
188359
|
+
}
|
|
188360
|
+
function integrationAdvice(mode, client) {
|
|
188361
|
+
if (mode === "plugin-mcp" || mode === "manual-mcp") {
|
|
188362
|
+
return "status: ready";
|
|
188363
|
+
}
|
|
188364
|
+
if (mode === "plugin-skill") {
|
|
188365
|
+
return "status: bundled Plugin MCP is disabled; re-enable it or remove the Plugin and use CLI plus Skill-only.";
|
|
188366
|
+
}
|
|
188367
|
+
if (mode === "cli-skill") {
|
|
188368
|
+
return "status: ready; MCP is optional for CLI plus Skill mode.";
|
|
188369
|
+
}
|
|
188370
|
+
if (mode === "skill-on-demand") {
|
|
188371
|
+
return "status: runnable on demand; package-runner fallback may need network access.";
|
|
188372
|
+
}
|
|
188373
|
+
if (mode === "mcp-only") {
|
|
188374
|
+
return `next: run \`npx @kyo-so/cli setup ${client} --write --skill-only\``;
|
|
188375
|
+
}
|
|
188376
|
+
if (mode === "cli-only") {
|
|
188377
|
+
return `next: run \`kyoso setup ${client} --write --skill-only\``;
|
|
188378
|
+
}
|
|
188379
|
+
if (mode === "missing") {
|
|
188380
|
+
return "next: choose a Marketplace Plugin, CLI plus Skill, or traditional MCP setup.";
|
|
188381
|
+
}
|
|
188382
|
+
return "status: inspect the warnings before changing integration state.";
|
|
188383
|
+
}
|
|
186499
188384
|
function formatLayer(loaded, layer, defaultPath) {
|
|
186500
188385
|
const source = loaded.sources.find((candidate) => candidate.layer === layer);
|
|
186501
188386
|
return source ? `found ${source.path}` : `not found ${defaultPath}`;
|
|
@@ -186529,11 +188414,11 @@ function formatClaudeDualAuthWarning(preferApiKey) {
|
|
|
186529
188414
|
function hasEnv2(env, key) {
|
|
186530
188415
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
186531
188416
|
}
|
|
186532
|
-
function
|
|
186533
|
-
const paths = env.PATH?.split(
|
|
188417
|
+
function commandExists3(command, env) {
|
|
188418
|
+
const paths = env.PATH?.split(delimiter3) ?? [];
|
|
186534
188419
|
return paths.some((path) => {
|
|
186535
188420
|
try {
|
|
186536
|
-
|
|
188421
|
+
accessSync2(`${path}/${command}`);
|
|
186537
188422
|
return true;
|
|
186538
188423
|
} catch {
|
|
186539
188424
|
return false;
|
|
@@ -186542,13 +188427,13 @@ function commandExists2(command, env) {
|
|
|
186542
188427
|
}
|
|
186543
188428
|
|
|
186544
188429
|
// src/cli/init.ts
|
|
186545
|
-
import { access as access3, readFile as
|
|
186546
|
-
import { join as
|
|
188430
|
+
import { access as access3, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
|
|
188431
|
+
import { join as join7 } from "node:path";
|
|
186547
188432
|
async function runInit(options) {
|
|
186548
|
-
const configPath =
|
|
186549
|
-
const tsConfigPath =
|
|
186550
|
-
const skillPath =
|
|
186551
|
-
const gitignorePath =
|
|
188433
|
+
const configPath = join7(options.cwd, "kyoso.toml");
|
|
188434
|
+
const tsConfigPath = join7(options.cwd, "kyoso.config.ts");
|
|
188435
|
+
const skillPath = join7(options.cwd, ".agents/skills/kyoso-review/SKILL.md");
|
|
188436
|
+
const gitignorePath = join7(options.cwd, ".gitignore");
|
|
186552
188437
|
const configResult = await writeFileWithOverwritePrompt(configPath, CONFIG_TEMPLATE, options.force);
|
|
186553
188438
|
const skillResult = await writeFileWithOverwritePrompt(skillPath, SKILL_TEMPLATE, options.force);
|
|
186554
188439
|
const gitignoreResult = await ensureGitignoreEntry(gitignorePath, ".kyoso/");
|
|
@@ -186566,11 +188451,11 @@ async function runInit(options) {
|
|
|
186566
188451
|
async function ensureGitignoreEntry(path, entry) {
|
|
186567
188452
|
let content = "";
|
|
186568
188453
|
try {
|
|
186569
|
-
content = await
|
|
188454
|
+
content = await readFile7(path, "utf8");
|
|
186570
188455
|
} catch (error51) {
|
|
186571
|
-
if (!
|
|
188456
|
+
if (!isMissingPathError5(error51))
|
|
186572
188457
|
throw error51;
|
|
186573
|
-
await
|
|
188458
|
+
await writeFile5(path, `${entry}
|
|
186574
188459
|
`, "utf8");
|
|
186575
188460
|
return "created";
|
|
186576
188461
|
}
|
|
@@ -186580,11 +188465,11 @@ async function ensureGitignoreEntry(path, entry) {
|
|
|
186580
188465
|
const separator = content.length > 0 && !content.endsWith(`
|
|
186581
188466
|
`) ? `
|
|
186582
188467
|
` : "";
|
|
186583
|
-
await
|
|
188468
|
+
await writeFile5(path, `${content}${separator}${entry}
|
|
186584
188469
|
`, "utf8");
|
|
186585
188470
|
return "updated";
|
|
186586
188471
|
}
|
|
186587
|
-
function
|
|
188472
|
+
function isMissingPathError5(error51) {
|
|
186588
188473
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
186589
188474
|
}
|
|
186590
188475
|
async function exists3(path) {
|
|
@@ -187067,11 +188952,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187067
188952
|
const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
|
|
187068
188953
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
187069
188954
|
}
|
|
187070
|
-
optimizeNames(names,
|
|
188955
|
+
optimizeNames(names, constants2) {
|
|
187071
188956
|
if (!names[this.name.str])
|
|
187072
188957
|
return;
|
|
187073
188958
|
if (this.rhs)
|
|
187074
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
188959
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
187075
188960
|
return this;
|
|
187076
188961
|
}
|
|
187077
188962
|
get names() {
|
|
@@ -187088,10 +188973,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187088
188973
|
render({ _n }) {
|
|
187089
188974
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
187090
188975
|
}
|
|
187091
|
-
optimizeNames(names,
|
|
188976
|
+
optimizeNames(names, constants2) {
|
|
187092
188977
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
187093
188978
|
return;
|
|
187094
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
188979
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
187095
188980
|
return this;
|
|
187096
188981
|
}
|
|
187097
188982
|
get names() {
|
|
@@ -187150,8 +189035,8 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187150
189035
|
optimizeNodes() {
|
|
187151
189036
|
return `${this.code}` ? this : undefined;
|
|
187152
189037
|
}
|
|
187153
|
-
optimizeNames(names,
|
|
187154
|
-
this.code = optimizeExpr(this.code, names,
|
|
189038
|
+
optimizeNames(names, constants2) {
|
|
189039
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
187155
189040
|
return this;
|
|
187156
189041
|
}
|
|
187157
189042
|
get names() {
|
|
@@ -187180,12 +189065,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187180
189065
|
}
|
|
187181
189066
|
return nodes.length > 0 ? this : undefined;
|
|
187182
189067
|
}
|
|
187183
|
-
optimizeNames(names,
|
|
189068
|
+
optimizeNames(names, constants2) {
|
|
187184
189069
|
const { nodes } = this;
|
|
187185
189070
|
let i = nodes.length;
|
|
187186
189071
|
while (i--) {
|
|
187187
189072
|
const n = nodes[i];
|
|
187188
|
-
if (n.optimizeNames(names,
|
|
189073
|
+
if (n.optimizeNames(names, constants2))
|
|
187189
189074
|
continue;
|
|
187190
189075
|
subtractNames(names, n.names);
|
|
187191
189076
|
nodes.splice(i, 1);
|
|
@@ -187238,12 +189123,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187238
189123
|
return;
|
|
187239
189124
|
return this;
|
|
187240
189125
|
}
|
|
187241
|
-
optimizeNames(names,
|
|
189126
|
+
optimizeNames(names, constants2) {
|
|
187242
189127
|
var _a3;
|
|
187243
|
-
this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names,
|
|
187244
|
-
if (!(super.optimizeNames(names,
|
|
189128
|
+
this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names, constants2);
|
|
189129
|
+
if (!(super.optimizeNames(names, constants2) || this.else))
|
|
187245
189130
|
return;
|
|
187246
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
189131
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
187247
189132
|
return this;
|
|
187248
189133
|
}
|
|
187249
189134
|
get names() {
|
|
@@ -187266,10 +189151,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187266
189151
|
render(opts) {
|
|
187267
189152
|
return `for(${this.iteration})` + super.render(opts);
|
|
187268
189153
|
}
|
|
187269
|
-
optimizeNames(names,
|
|
187270
|
-
if (!super.optimizeNames(names,
|
|
189154
|
+
optimizeNames(names, constants2) {
|
|
189155
|
+
if (!super.optimizeNames(names, constants2))
|
|
187271
189156
|
return;
|
|
187272
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
189157
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
187273
189158
|
return this;
|
|
187274
189159
|
}
|
|
187275
189160
|
get names() {
|
|
@@ -187304,10 +189189,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187304
189189
|
render(opts) {
|
|
187305
189190
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
187306
189191
|
}
|
|
187307
|
-
optimizeNames(names,
|
|
187308
|
-
if (!super.optimizeNames(names,
|
|
189192
|
+
optimizeNames(names, constants2) {
|
|
189193
|
+
if (!super.optimizeNames(names, constants2))
|
|
187309
189194
|
return;
|
|
187310
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
189195
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
187311
189196
|
return this;
|
|
187312
189197
|
}
|
|
187313
189198
|
get names() {
|
|
@@ -187348,11 +189233,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187348
189233
|
(_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
|
|
187349
189234
|
return this;
|
|
187350
189235
|
}
|
|
187351
|
-
optimizeNames(names,
|
|
189236
|
+
optimizeNames(names, constants2) {
|
|
187352
189237
|
var _a3, _b;
|
|
187353
|
-
super.optimizeNames(names,
|
|
187354
|
-
(_a3 = this.catch) === null || _a3 === undefined || _a3.optimizeNames(names,
|
|
187355
|
-
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names,
|
|
189238
|
+
super.optimizeNames(names, constants2);
|
|
189239
|
+
(_a3 = this.catch) === null || _a3 === undefined || _a3.optimizeNames(names, constants2);
|
|
189240
|
+
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants2);
|
|
187356
189241
|
return this;
|
|
187357
189242
|
}
|
|
187358
189243
|
get names() {
|
|
@@ -187621,7 +189506,7 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187621
189506
|
function addExprNames(names, from) {
|
|
187622
189507
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
187623
189508
|
}
|
|
187624
|
-
function optimizeExpr(expr, names,
|
|
189509
|
+
function optimizeExpr(expr, names, constants2) {
|
|
187625
189510
|
if (expr instanceof code_1.Name)
|
|
187626
189511
|
return replaceName(expr);
|
|
187627
189512
|
if (!canOptimize(expr))
|
|
@@ -187636,14 +189521,14 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187636
189521
|
return items;
|
|
187637
189522
|
}, []));
|
|
187638
189523
|
function replaceName(n) {
|
|
187639
|
-
const c =
|
|
189524
|
+
const c = constants2[n.str];
|
|
187640
189525
|
if (c === undefined || names[n.str] !== 1)
|
|
187641
189526
|
return n;
|
|
187642
189527
|
delete names[n.str];
|
|
187643
189528
|
return c;
|
|
187644
189529
|
}
|
|
187645
189530
|
function canOptimize(e) {
|
|
187646
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
189531
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== undefined);
|
|
187647
189532
|
}
|
|
187648
189533
|
}
|
|
187649
189534
|
function subtractNames(names, from) {
|
|
@@ -188780,8 +190665,8 @@ var require_resolve = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
188780
190665
|
}
|
|
188781
190666
|
return count;
|
|
188782
190667
|
}
|
|
188783
|
-
function getFullPath(resolver, id = "",
|
|
188784
|
-
if (
|
|
190668
|
+
function getFullPath(resolver, id = "", normalize2) {
|
|
190669
|
+
if (normalize2 !== false)
|
|
188785
190670
|
id = normalizeId(id);
|
|
188786
190671
|
return _getFullPath(resolver, resolver.parse(id));
|
|
188787
190672
|
}
|
|
@@ -189496,7 +191381,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189496
191381
|
const schOrFunc = root.refs[ref];
|
|
189497
191382
|
if (schOrFunc)
|
|
189498
191383
|
return schOrFunc;
|
|
189499
|
-
let _sch =
|
|
191384
|
+
let _sch = resolve9.call(this, root, ref);
|
|
189500
191385
|
if (_sch === undefined) {
|
|
189501
191386
|
const schema = (_a3 = root.localRefs) === null || _a3 === undefined ? undefined : _a3[ref];
|
|
189502
191387
|
const { schemaId } = this.opts;
|
|
@@ -189527,7 +191412,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189527
191412
|
function sameSchemaEnv(s1, s2) {
|
|
189528
191413
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
189529
191414
|
}
|
|
189530
|
-
function
|
|
191415
|
+
function resolve9(root, ref) {
|
|
189531
191416
|
let sch;
|
|
189532
191417
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
189533
191418
|
ref = sch;
|
|
@@ -190026,60 +191911,60 @@ var require_schemes = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190026
191911
|
var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
190027
191912
|
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
|
|
190028
191913
|
const { SCHEMES, getSchemeHandler } = require_schemes();
|
|
190029
|
-
function
|
|
191914
|
+
function normalize2(uri, options) {
|
|
190030
191915
|
if (typeof uri === "string")
|
|
190031
191916
|
uri = serialize(parse6(uri, options), options);
|
|
190032
191917
|
else if (typeof uri === "object")
|
|
190033
191918
|
uri = parse6(serialize(uri, options), options);
|
|
190034
191919
|
return uri;
|
|
190035
191920
|
}
|
|
190036
|
-
function
|
|
191921
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
190037
191922
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
190038
191923
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
190039
191924
|
schemelessOptions.skipEscape = true;
|
|
190040
191925
|
return serialize(resolved, schemelessOptions);
|
|
190041
191926
|
}
|
|
190042
|
-
function resolveComponent(base,
|
|
191927
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
190043
191928
|
const target = {};
|
|
190044
191929
|
if (!skipNormalization) {
|
|
190045
191930
|
base = parse6(serialize(base, options), options);
|
|
190046
|
-
|
|
191931
|
+
relative3 = parse6(serialize(relative3, options), options);
|
|
190047
191932
|
}
|
|
190048
191933
|
options = options || {};
|
|
190049
|
-
if (!options.tolerant &&
|
|
190050
|
-
target.scheme =
|
|
190051
|
-
target.userinfo =
|
|
190052
|
-
target.host =
|
|
190053
|
-
target.port =
|
|
190054
|
-
target.path = removeDotSegments(
|
|
190055
|
-
target.query =
|
|
191934
|
+
if (!options.tolerant && relative3.scheme) {
|
|
191935
|
+
target.scheme = relative3.scheme;
|
|
191936
|
+
target.userinfo = relative3.userinfo;
|
|
191937
|
+
target.host = relative3.host;
|
|
191938
|
+
target.port = relative3.port;
|
|
191939
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
191940
|
+
target.query = relative3.query;
|
|
190056
191941
|
} else {
|
|
190057
|
-
if (
|
|
190058
|
-
target.userinfo =
|
|
190059
|
-
target.host =
|
|
190060
|
-
target.port =
|
|
190061
|
-
target.path = removeDotSegments(
|
|
190062
|
-
target.query =
|
|
191942
|
+
if (relative3.userinfo !== undefined || relative3.host !== undefined || relative3.port !== undefined) {
|
|
191943
|
+
target.userinfo = relative3.userinfo;
|
|
191944
|
+
target.host = relative3.host;
|
|
191945
|
+
target.port = relative3.port;
|
|
191946
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
191947
|
+
target.query = relative3.query;
|
|
190063
191948
|
} else {
|
|
190064
|
-
if (!
|
|
191949
|
+
if (!relative3.path) {
|
|
190065
191950
|
target.path = base.path;
|
|
190066
|
-
if (
|
|
190067
|
-
target.query =
|
|
191951
|
+
if (relative3.query !== undefined)
|
|
191952
|
+
target.query = relative3.query;
|
|
190068
191953
|
else
|
|
190069
191954
|
target.query = base.query;
|
|
190070
191955
|
} else {
|
|
190071
|
-
if (
|
|
190072
|
-
target.path = removeDotSegments(
|
|
191956
|
+
if (relative3.path[0] === "/")
|
|
191957
|
+
target.path = removeDotSegments(relative3.path);
|
|
190073
191958
|
else {
|
|
190074
191959
|
if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path)
|
|
190075
|
-
target.path = "/" +
|
|
191960
|
+
target.path = "/" + relative3.path;
|
|
190076
191961
|
else if (!base.path)
|
|
190077
|
-
target.path =
|
|
191962
|
+
target.path = relative3.path;
|
|
190078
191963
|
else
|
|
190079
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
191964
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
190080
191965
|
target.path = removeDotSegments(target.path);
|
|
190081
191966
|
}
|
|
190082
|
-
target.query =
|
|
191967
|
+
target.query = relative3.query;
|
|
190083
191968
|
}
|
|
190084
191969
|
target.userinfo = base.userinfo;
|
|
190085
191970
|
target.host = base.host;
|
|
@@ -190087,7 +191972,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190087
191972
|
}
|
|
190088
191973
|
target.scheme = base.scheme;
|
|
190089
191974
|
}
|
|
190090
|
-
target.fragment =
|
|
191975
|
+
target.fragment = relative3.fragment;
|
|
190091
191976
|
return target;
|
|
190092
191977
|
}
|
|
190093
191978
|
function equal(uriA, uriB, options) {
|
|
@@ -190243,8 +192128,8 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190243
192128
|
}
|
|
190244
192129
|
const fastUri = {
|
|
190245
192130
|
SCHEMES,
|
|
190246
|
-
normalize,
|
|
190247
|
-
resolve:
|
|
192131
|
+
normalize: normalize2,
|
|
192132
|
+
resolve: resolve9,
|
|
190248
192133
|
resolveComponent,
|
|
190249
192134
|
equal,
|
|
190250
192135
|
serialize,
|
|
@@ -197386,14 +199271,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
197386
199271
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
197387
199272
|
}
|
|
197388
199273
|
function sleep(ms, signal) {
|
|
197389
|
-
return new Promise((
|
|
199274
|
+
return new Promise((resolve9, reject) => {
|
|
197390
199275
|
if (signal?.aborted) {
|
|
197391
199276
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
197392
199277
|
return;
|
|
197393
199278
|
}
|
|
197394
199279
|
const timer = setTimeout(() => {
|
|
197395
199280
|
signal?.removeEventListener("abort", onAbort);
|
|
197396
|
-
|
|
199281
|
+
resolve9();
|
|
197397
199282
|
}, ms);
|
|
197398
199283
|
const onAbort = () => {
|
|
197399
199284
|
clearTimeout(timer);
|
|
@@ -198155,7 +200040,7 @@ var Protocol = class {
|
|
|
198155
200040
|
const flowStartedAt = Date.now();
|
|
198156
200041
|
let onAbort;
|
|
198157
200042
|
let cleanupMessageId;
|
|
198158
|
-
return new Promise((
|
|
200043
|
+
return new Promise((resolve9, reject) => {
|
|
198159
200044
|
const earlyReject = (error51) => {
|
|
198160
200045
|
reject(error51);
|
|
198161
200046
|
};
|
|
@@ -198231,7 +200116,7 @@ var Protocol = class {
|
|
|
198231
200116
|
return reject(decoded.error);
|
|
198232
200117
|
if (decoded.kind === "input_required") {
|
|
198233
200118
|
if (options?.allowInputRequired === true)
|
|
198234
|
-
return
|
|
200119
|
+
return resolve9(manualInputRequiredValue(decoded));
|
|
198235
200120
|
const flow = {
|
|
198236
200121
|
codec: codec2,
|
|
198237
200122
|
request,
|
|
@@ -198243,12 +200128,12 @@ var Protocol = class {
|
|
|
198243
200128
|
params
|
|
198244
200129
|
}, resultSchema, legOptions)
|
|
198245
200130
|
};
|
|
198246
|
-
return
|
|
200131
|
+
return resolve9(this._resolveNonCompleteResult(decoded, flow));
|
|
198247
200132
|
}
|
|
198248
200133
|
const result = decoded.result;
|
|
198249
200134
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
198250
200135
|
if (parseResult.success)
|
|
198251
|
-
|
|
200136
|
+
resolve9(parseResult.data);
|
|
198252
200137
|
else
|
|
198253
200138
|
reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
198254
200139
|
}, reject);
|
|
@@ -199792,7 +201677,7 @@ var StdioServerTransport = class {
|
|
|
199792
201677
|
send(message) {
|
|
199793
201678
|
if (this._closed)
|
|
199794
201679
|
return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
199795
|
-
return new Promise((
|
|
201680
|
+
return new Promise((resolve9, reject) => {
|
|
199796
201681
|
const json2 = serializeMessage(message);
|
|
199797
201682
|
let settled = false;
|
|
199798
201683
|
const onError = (error51) => {
|
|
@@ -199809,7 +201694,7 @@ var StdioServerTransport = class {
|
|
|
199809
201694
|
settled = true;
|
|
199810
201695
|
this._stdout.off("error", onError);
|
|
199811
201696
|
this._stdout.off("drain", onDrain);
|
|
199812
|
-
|
|
201697
|
+
resolve9();
|
|
199813
201698
|
};
|
|
199814
201699
|
this._stdout.once("error", onError);
|
|
199815
201700
|
if (this._stdout.write(json2)) {
|
|
@@ -199817,7 +201702,7 @@ var StdioServerTransport = class {
|
|
|
199817
201702
|
return;
|
|
199818
201703
|
settled = true;
|
|
199819
201704
|
this._stdout.off("error", onError);
|
|
199820
|
-
|
|
201705
|
+
resolve9();
|
|
199821
201706
|
} else if (!settled)
|
|
199822
201707
|
this._stdout.once("drain", onDrain);
|
|
199823
201708
|
});
|
|
@@ -199825,7 +201710,7 @@ var StdioServerTransport = class {
|
|
|
199825
201710
|
};
|
|
199826
201711
|
|
|
199827
201712
|
// src/core/runReview.ts
|
|
199828
|
-
import { resolve as
|
|
201713
|
+
import { resolve as resolve10 } from "node:path";
|
|
199829
201714
|
|
|
199830
201715
|
// src/config/configOverrides.ts
|
|
199831
201716
|
var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
@@ -199888,7 +201773,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
199888
201773
|
function readPath2(target, path) {
|
|
199889
201774
|
let current = target;
|
|
199890
201775
|
for (const key of path) {
|
|
199891
|
-
if (!
|
|
201776
|
+
if (!isRecord8(current))
|
|
199892
201777
|
return;
|
|
199893
201778
|
current = current[key];
|
|
199894
201779
|
}
|
|
@@ -199898,7 +201783,7 @@ function writePath2(target, path, value) {
|
|
|
199898
201783
|
let current = target;
|
|
199899
201784
|
for (const key of path.slice(0, -1)) {
|
|
199900
201785
|
const child = current[key];
|
|
199901
|
-
if (!
|
|
201786
|
+
if (!isRecord8(child)) {
|
|
199902
201787
|
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
199903
201788
|
}
|
|
199904
201789
|
current = child;
|
|
@@ -199907,14 +201792,14 @@ function writePath2(target, path, value) {
|
|
|
199907
201792
|
if (leaf)
|
|
199908
201793
|
current[leaf] = value;
|
|
199909
201794
|
}
|
|
199910
|
-
function
|
|
201795
|
+
function isRecord8(value) {
|
|
199911
201796
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
199912
201797
|
}
|
|
199913
201798
|
|
|
199914
201799
|
// src/acp/AcpAgentProcess.ts
|
|
199915
201800
|
import { spawn } from "node:child_process";
|
|
199916
|
-
import { readFile as
|
|
199917
|
-
import { isAbsolute, relative, resolve as
|
|
201801
|
+
import { readFile as readFile8, realpath as realpath3 } from "node:fs/promises";
|
|
201802
|
+
import { isAbsolute as isAbsolute4, relative as relative3, resolve as resolve9 } from "node:path";
|
|
199918
201803
|
import { Readable, Writable } from "node:stream";
|
|
199919
201804
|
|
|
199920
201805
|
// node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -199986,6 +201871,48 @@ function requiredDefaultOnError(schema, fallback) {
|
|
|
199986
201871
|
return exports_external.NEVER;
|
|
199987
201872
|
});
|
|
199988
201873
|
}
|
|
201874
|
+
function stringTag(value, key) {
|
|
201875
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
201876
|
+
return;
|
|
201877
|
+
}
|
|
201878
|
+
const tag = value[key];
|
|
201879
|
+
return typeof tag === "string" ? tag : undefined;
|
|
201880
|
+
}
|
|
201881
|
+
function excludeKnownTags(schema, key, knownTags) {
|
|
201882
|
+
return schema.superRefine((value, context) => {
|
|
201883
|
+
const tag = stringTag(value, key);
|
|
201884
|
+
if (tag !== undefined && knownTags.includes(tag)) {
|
|
201885
|
+
context.addIssue({
|
|
201886
|
+
code: "custom",
|
|
201887
|
+
path: [key],
|
|
201888
|
+
message: `${key} ${JSON.stringify(tag)} is reserved by a known variant, ` + `but the value does not match that variant's schema`
|
|
201889
|
+
});
|
|
201890
|
+
}
|
|
201891
|
+
});
|
|
201892
|
+
}
|
|
201893
|
+
function preserveCustomPayload(schema, key, knownTags) {
|
|
201894
|
+
return exports_external.unknown().transform((value, context) => {
|
|
201895
|
+
const result = schema.safeParse(value);
|
|
201896
|
+
if (!result.success) {
|
|
201897
|
+
for (const issue2 of result.error.issues) {
|
|
201898
|
+
context.addIssue({ ...issue2, input: value });
|
|
201899
|
+
}
|
|
201900
|
+
return exports_external.NEVER;
|
|
201901
|
+
}
|
|
201902
|
+
const output2 = result.data;
|
|
201903
|
+
const tag = stringTag(value, key);
|
|
201904
|
+
if (tag !== undefined && !knownTags.includes(tag)) {
|
|
201905
|
+
const raw = value;
|
|
201906
|
+
for (const [property, rawValue] of Object.entries(raw)) {
|
|
201907
|
+
if (property === "__proto__")
|
|
201908
|
+
continue;
|
|
201909
|
+
if (!Object.hasOwn(output2, property))
|
|
201910
|
+
output2[property] = rawValue;
|
|
201911
|
+
}
|
|
201912
|
+
}
|
|
201913
|
+
return output2;
|
|
201914
|
+
});
|
|
201915
|
+
}
|
|
199989
201916
|
function vecSkipError(itemSchema) {
|
|
199990
201917
|
return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
|
|
199991
201918
|
}
|
|
@@ -200409,15 +202336,15 @@ var zTitledMultiSelectItems = object({
|
|
|
200409
202336
|
return;
|
|
200410
202337
|
})
|
|
200411
202338
|
});
|
|
200412
|
-
var zMultiSelectItems = union([
|
|
202339
|
+
var zMultiSelectItems = preserveCustomPayload(union([
|
|
200413
202340
|
zStringMultiSelectItems.and(object({
|
|
200414
202341
|
type: literal("string")
|
|
200415
202342
|
})),
|
|
200416
|
-
object({
|
|
202343
|
+
excludeKnownTags(object({
|
|
200417
202344
|
type: string2()
|
|
200418
|
-
}),
|
|
202345
|
+
}), "type", ["string"]),
|
|
200419
202346
|
zTitledMultiSelectItems
|
|
200420
|
-
]);
|
|
202347
|
+
]), "type", ["string"]);
|
|
200421
202348
|
var zMultiSelectPropertySchema = object({
|
|
200422
202349
|
title: defaultOnError(string2().nullish(), () => {
|
|
200423
202350
|
return;
|
|
@@ -200435,7 +202362,7 @@ var zMultiSelectPropertySchema = object({
|
|
|
200435
202362
|
return;
|
|
200436
202363
|
})
|
|
200437
202364
|
});
|
|
200438
|
-
var zElicitationPropertySchema = union([
|
|
202365
|
+
var zElicitationPropertySchema = preserveCustomPayload(union([
|
|
200439
202366
|
zStringPropertySchema.and(object({
|
|
200440
202367
|
type: literal("string")
|
|
200441
202368
|
})),
|
|
@@ -200451,10 +202378,10 @@ var zElicitationPropertySchema = union([
|
|
|
200451
202378
|
zMultiSelectPropertySchema.and(object({
|
|
200452
202379
|
type: literal("array")
|
|
200453
202380
|
})),
|
|
200454
|
-
object({
|
|
202381
|
+
excludeKnownTags(object({
|
|
200455
202382
|
type: string2()
|
|
200456
|
-
})
|
|
200457
|
-
]);
|
|
202383
|
+
}), "type", ["array", "boolean", "integer", "number", "string"])
|
|
202384
|
+
]), "type", ["array", "boolean", "integer", "number", "string"]);
|
|
200458
202385
|
var zElicitationSchema = object({
|
|
200459
202386
|
type: defaultOnError(zElicitationSchemaType.optional().default("object"), () => "object"),
|
|
200460
202387
|
title: defaultOnError(string2().nullish(), () => {
|
|
@@ -200477,22 +202404,22 @@ var zElicitationUrlMode = intersection(union([zElicitationSessionScope, zElicita
|
|
|
200477
202404
|
elicitationId: zElicitationId,
|
|
200478
202405
|
url: url()
|
|
200479
202406
|
}));
|
|
200480
|
-
var zCreateElicitationRequest = intersection(union([
|
|
202407
|
+
var zCreateElicitationRequest = preserveCustomPayload(intersection(union([
|
|
200481
202408
|
zElicitationFormMode.and(object({
|
|
200482
202409
|
mode: literal("form")
|
|
200483
202410
|
})),
|
|
200484
202411
|
zElicitationUrlMode.and(object({
|
|
200485
202412
|
mode: literal("url")
|
|
200486
202413
|
})),
|
|
200487
|
-
intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
202414
|
+
excludeKnownTags(intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
200488
202415
|
mode: string2()
|
|
200489
|
-
}))
|
|
202416
|
+
})), "mode", ["form", "url"])
|
|
200490
202417
|
]), object({
|
|
200491
202418
|
message: string2(),
|
|
200492
202419
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
200493
202420
|
return;
|
|
200494
202421
|
})
|
|
200495
|
-
}));
|
|
202422
|
+
})), "mode", ["form", "url"]);
|
|
200496
202423
|
var zMcpServerAcpId = string2();
|
|
200497
202424
|
var zConnectMcpRequest = object({
|
|
200498
202425
|
serverId: zMcpServerAcpId,
|
|
@@ -202092,7 +204019,7 @@ var zElicitationContentValue = union([
|
|
|
202092
204019
|
var zElicitationAcceptAction = object({
|
|
202093
204020
|
content: record(string2(), zElicitationContentValue).nullish()
|
|
202094
204021
|
});
|
|
202095
|
-
var zCreateElicitationResponse = intersection(union([
|
|
204022
|
+
var zCreateElicitationResponse = preserveCustomPayload(intersection(union([
|
|
202096
204023
|
zElicitationAcceptAction.and(object({
|
|
202097
204024
|
action: literal("accept")
|
|
202098
204025
|
})),
|
|
@@ -202102,14 +204029,14 @@ var zCreateElicitationResponse = intersection(union([
|
|
|
202102
204029
|
object({
|
|
202103
204030
|
action: literal("cancel")
|
|
202104
204031
|
}),
|
|
202105
|
-
object({
|
|
204032
|
+
excludeKnownTags(object({
|
|
202106
204033
|
action: string2()
|
|
202107
|
-
})
|
|
204034
|
+
}), "action", ["accept", "cancel", "decline"])
|
|
202108
204035
|
]), object({
|
|
202109
204036
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
202110
204037
|
return;
|
|
202111
204038
|
})
|
|
202112
|
-
}));
|
|
204039
|
+
})), "action", ["accept", "cancel", "decline"]);
|
|
202113
204040
|
var zConnectMcpResponse = object({
|
|
202114
204041
|
connectionId: zMcpConnectionId,
|
|
202115
204042
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
@@ -202245,16 +204172,34 @@ var zCancelRequestNotification = object({
|
|
|
202245
204172
|
return;
|
|
202246
204173
|
})
|
|
202247
204174
|
});
|
|
204175
|
+
|
|
204176
|
+
// node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js
|
|
204177
|
+
var zGuardCreateElicitationRequestForm = zElicitationFormMode.and(object({ mode: literal("form") })).and(object({ message: string2() }));
|
|
204178
|
+
var zGuardCreateElicitationRequestUrl = zElicitationUrlMode.and(object({ mode: literal("url") })).and(object({ message: string2() }));
|
|
204179
|
+
var zGuardCreateElicitationRequestCustom = union([zElicitationSessionScope, zElicitationRequestScope]).and(object({ message: string2() }));
|
|
204180
|
+
var zGuardElicitationPropertySchemaString = zStringPropertySchema.and(object({ type: literal("string") }));
|
|
204181
|
+
var zGuardElicitationPropertySchemaNumber = zNumberPropertySchema.and(object({ type: literal("number") }));
|
|
204182
|
+
var zGuardElicitationPropertySchemaInteger = zIntegerPropertySchema.and(object({ type: literal("integer") }));
|
|
204183
|
+
var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({ type: literal("boolean") }));
|
|
204184
|
+
var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
|
|
204185
|
+
var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
|
|
204186
|
+
var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
|
|
204187
|
+
var zGuardCreateElicitationResponseDecline = object({
|
|
204188
|
+
action: literal("decline")
|
|
204189
|
+
});
|
|
204190
|
+
var zGuardCreateElicitationResponseCancel = object({
|
|
204191
|
+
action: literal("cancel")
|
|
204192
|
+
});
|
|
202248
204193
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
202249
204194
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
202250
|
-
function
|
|
204195
|
+
function isRecord9(value) {
|
|
202251
204196
|
return typeof value === "object" && value !== null;
|
|
202252
204197
|
}
|
|
202253
204198
|
function isJsonRpcId(value) {
|
|
202254
204199
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
202255
204200
|
}
|
|
202256
204201
|
function cancelRequestId(params) {
|
|
202257
|
-
if (!
|
|
204202
|
+
if (!isRecord9(params) || !isJsonRpcId(params["requestId"])) {
|
|
202258
204203
|
return;
|
|
202259
204204
|
}
|
|
202260
204205
|
return params["requestId"];
|
|
@@ -202478,12 +204423,12 @@ class Connection {
|
|
|
202478
204423
|
}
|
|
202479
204424
|
const id = this.nextRequestId++;
|
|
202480
204425
|
let cancel = () => {};
|
|
202481
|
-
const responsePromise = new Promise((
|
|
204426
|
+
const responsePromise = new Promise((resolve9, reject) => {
|
|
202482
204427
|
const pendingResponse = {
|
|
202483
204428
|
resolve: (response) => {
|
|
202484
204429
|
try {
|
|
202485
204430
|
const value = mapResponse ? mapResponse(response) : response;
|
|
202486
|
-
|
|
204431
|
+
resolve9(value);
|
|
202487
204432
|
} catch (error51) {
|
|
202488
204433
|
reject(error51);
|
|
202489
204434
|
}
|
|
@@ -202548,8 +204493,8 @@ class Connection {
|
|
|
202548
204493
|
initialize(stream, handlers) {
|
|
202549
204494
|
this.stream = stream;
|
|
202550
204495
|
this.staticHandlers = handlers;
|
|
202551
|
-
this.closedPromise = new Promise((
|
|
202552
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
204496
|
+
this.closedPromise = new Promise((resolve9) => {
|
|
204497
|
+
this.abortController.signal.addEventListener("abort", () => resolve9());
|
|
202553
204498
|
});
|
|
202554
204499
|
this.receive();
|
|
202555
204500
|
}
|
|
@@ -202601,7 +204546,7 @@ class Connection {
|
|
|
202601
204546
|
if (this.abortController.signal.aborted) {
|
|
202602
204547
|
return;
|
|
202603
204548
|
}
|
|
202604
|
-
if (!
|
|
204549
|
+
if (!isRecord9(message)) {
|
|
202605
204550
|
console.error("Invalid message", { message });
|
|
202606
204551
|
return;
|
|
202607
204552
|
}
|
|
@@ -202694,7 +204639,7 @@ class Connection {
|
|
|
202694
204639
|
pendingResponse.cleanup?.();
|
|
202695
204640
|
if ("result" in response) {
|
|
202696
204641
|
pendingResponse.resolve(response.result);
|
|
202697
|
-
} else if ("error" in response &&
|
|
204642
|
+
} else if ("error" in response && isRecord9(response.error)) {
|
|
202698
204643
|
const { code, message, data } = response.error;
|
|
202699
204644
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
202700
204645
|
} else {
|
|
@@ -202904,7 +204849,7 @@ function ndJsonStream(output2, input2) {
|
|
|
202904
204849
|
if (trimmedLine) {
|
|
202905
204850
|
try {
|
|
202906
204851
|
const message = JSON.parse(trimmedLine);
|
|
202907
|
-
if (
|
|
204852
|
+
if (isRecord9(message)) {
|
|
202908
204853
|
controller.enqueue(message);
|
|
202909
204854
|
} else {
|
|
202910
204855
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -203262,8 +205207,8 @@ class AsyncQueue {
|
|
|
203262
205207
|
if (this.failed) {
|
|
203263
205208
|
return Promise.reject(this.failure);
|
|
203264
205209
|
}
|
|
203265
|
-
return new Promise((
|
|
203266
|
-
this.waiters.push({ resolve:
|
|
205210
|
+
return new Promise((resolve9, reject) => {
|
|
205211
|
+
this.waiters.push({ resolve: resolve9, reject });
|
|
203267
205212
|
});
|
|
203268
205213
|
}
|
|
203269
205214
|
}
|
|
@@ -203965,7 +205910,7 @@ function isSeverity(value) {
|
|
|
203965
205910
|
return typeof value === "string" && severities.includes(value);
|
|
203966
205911
|
}
|
|
203967
205912
|
function normalizeCisaSecureByDesign(value) {
|
|
203968
|
-
if (!
|
|
205913
|
+
if (!isRecord10(value))
|
|
203969
205914
|
return;
|
|
203970
205915
|
const normalized = {};
|
|
203971
205916
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -204006,7 +205951,7 @@ function normalizeFindingFiles(value) {
|
|
|
204006
205951
|
if (!Array.isArray(value))
|
|
204007
205952
|
return;
|
|
204008
205953
|
const files = value.flatMap((item) => {
|
|
204009
|
-
if (!
|
|
205954
|
+
if (!isRecord10(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
204010
205955
|
return [];
|
|
204011
205956
|
}
|
|
204012
205957
|
const file2 = {
|
|
@@ -204025,7 +205970,7 @@ function normalizeFindingFiles(value) {
|
|
|
204025
205970
|
function normalizeLineNumber(value) {
|
|
204026
205971
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
204027
205972
|
}
|
|
204028
|
-
function
|
|
205973
|
+
function isRecord10(value) {
|
|
204029
205974
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
204030
205975
|
}
|
|
204031
205976
|
|
|
@@ -204228,14 +206173,14 @@ function resolveEffortConfigOption(agent, effort) {
|
|
|
204228
206173
|
return;
|
|
204229
206174
|
}
|
|
204230
206175
|
async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
204231
|
-
const workspaceRoot = await
|
|
206176
|
+
const workspaceRoot = await realpath3(workspaceDir);
|
|
204232
206177
|
const candidates = resolveReadablePaths(workspaceRoot, requestedPath);
|
|
204233
206178
|
let content;
|
|
204234
206179
|
for (const absolute of candidates) {
|
|
204235
206180
|
const readablePath = await resolveReadableFile(workspaceRoot, absolute);
|
|
204236
206181
|
if (!readablePath)
|
|
204237
206182
|
continue;
|
|
204238
|
-
content = await
|
|
206183
|
+
content = await readFile8(readablePath, "utf8").catch(() => {
|
|
204239
206184
|
return;
|
|
204240
206185
|
});
|
|
204241
206186
|
if (content !== undefined)
|
|
@@ -204251,8 +206196,8 @@ async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
|
204251
206196
|
`);
|
|
204252
206197
|
}
|
|
204253
206198
|
async function resolveReadableFile(workspaceRoot, absolute) {
|
|
204254
|
-
const realPath = await
|
|
204255
|
-
if (
|
|
206199
|
+
const realPath = await realpath3(absolute).catch((error51) => {
|
|
206200
|
+
if (isMissingPathError6(error51))
|
|
204256
206201
|
return;
|
|
204257
206202
|
throw error51;
|
|
204258
206203
|
});
|
|
@@ -204262,18 +206207,18 @@ async function resolveReadableFile(workspaceRoot, absolute) {
|
|
|
204262
206207
|
return realPath;
|
|
204263
206208
|
}
|
|
204264
206209
|
function resolveReadablePaths(workspaceRoot, requestedPath) {
|
|
204265
|
-
const primary =
|
|
206210
|
+
const primary = resolve9(workspaceRoot, requestedPath);
|
|
204266
206211
|
assertWithinWorkspace(workspaceRoot, primary);
|
|
204267
|
-
const relativePath =
|
|
206212
|
+
const relativePath = relative3(workspaceRoot, primary).replaceAll("\\", "/");
|
|
204268
206213
|
if (relativePath.startsWith("context/") || relativePath.startsWith("repo/")) {
|
|
204269
206214
|
return [primary];
|
|
204270
206215
|
}
|
|
204271
|
-
const repoPath =
|
|
206216
|
+
const repoPath = resolve9(workspaceRoot, "repo", relativePath);
|
|
204272
206217
|
assertWithinWorkspace(workspaceRoot, repoPath);
|
|
204273
|
-
return
|
|
206218
|
+
return isAbsolute4(requestedPath) ? [primary, repoPath] : [repoPath, primary];
|
|
204274
206219
|
}
|
|
204275
206220
|
function assertWithinWorkspace(workspaceRoot, absolute) {
|
|
204276
|
-
const relativePath =
|
|
206221
|
+
const relativePath = relative3(workspaceRoot, absolute);
|
|
204277
206222
|
if (relativePath.startsWith("..") || relativePath === "" || relativePath.startsWith("/")) {
|
|
204278
206223
|
throw RequestError.invalidRequest({
|
|
204279
206224
|
policy: "Kyoso only allows reads from the temporary snapshot."
|
|
@@ -204290,7 +206235,7 @@ function terminateChild(child) {
|
|
|
204290
206235
|
}, 2000);
|
|
204291
206236
|
killTimer.unref();
|
|
204292
206237
|
}
|
|
204293
|
-
function
|
|
206238
|
+
function isMissingPathError6(error51) {
|
|
204294
206239
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
204295
206240
|
}
|
|
204296
206241
|
function buildAgentFailure(rawDetail, fallbackMessage) {
|
|
@@ -204986,68 +206931,78 @@ function normalizeTitle(value) {
|
|
|
204986
206931
|
return value.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
204987
206932
|
}
|
|
204988
206933
|
|
|
204989
|
-
// src/audit/
|
|
204990
|
-
import {
|
|
204991
|
-
import {
|
|
204992
|
-
|
|
204993
|
-
|
|
204994
|
-
|
|
204995
|
-
|
|
204996
|
-
|
|
204997
|
-
|
|
204998
|
-
|
|
204999
|
-
|
|
205000
|
-
|
|
205001
|
-
|
|
205002
|
-
|
|
206934
|
+
// src/audit/safeTraceFile.ts
|
|
206935
|
+
import { constants as constants2 } from "node:fs";
|
|
206936
|
+
import { lstat as lstat3, open, realpath as realpath4, stat } from "node:fs/promises";
|
|
206937
|
+
import { join as join8 } from "node:path";
|
|
206938
|
+
var AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
|
|
206939
|
+
async function openVerifiedTraceFile(options) {
|
|
206940
|
+
const flags = secureOpenFlags(options.openConstants);
|
|
206941
|
+
if (flags === undefined)
|
|
206942
|
+
throw new Error("secure open capability unavailable");
|
|
206943
|
+
if (!isSafeTraceId(options.traceId))
|
|
206944
|
+
throw new Error("unsafe trace id");
|
|
206945
|
+
const traceDirectory = await ensureTrustedDirectory({
|
|
206946
|
+
root: options.kyosoRoot,
|
|
206947
|
+
segments: [
|
|
206948
|
+
"workspaces",
|
|
206949
|
+
options.workspaceHash,
|
|
206950
|
+
...options.logicalDirectory.split("/"),
|
|
206951
|
+
options.date
|
|
206952
|
+
],
|
|
206953
|
+
uid: options.uid,
|
|
206954
|
+
workspaceRoot: options.workspaceRoot
|
|
206955
|
+
});
|
|
206956
|
+
const tracePath = join8(traceDirectory, `${options.traceId}.jsonl`);
|
|
206957
|
+
if (await optionalLstat3(tracePath)) {
|
|
206958
|
+
throw new Error("trace path already exists");
|
|
205003
206959
|
}
|
|
205004
|
-
|
|
205005
|
-
|
|
205006
|
-
|
|
205007
|
-
|
|
205008
|
-
|
|
205009
|
-
|
|
205010
|
-
|
|
206960
|
+
await options.beforeOpen?.(tracePath);
|
|
206961
|
+
const handle = await open(tracePath, flags, 384);
|
|
206962
|
+
try {
|
|
206963
|
+
const [handleStat, pathStat, realTracePath] = await Promise.all([
|
|
206964
|
+
handle.stat({ bigint: true }),
|
|
206965
|
+
stat(tracePath, { bigint: true }),
|
|
206966
|
+
realpath4(tracePath)
|
|
206967
|
+
]);
|
|
206968
|
+
if (!handleStat.isFile() || !pathStat.isFile() || handleStat.dev !== pathStat.dev || handleStat.ino !== pathStat.ino || !isPathWithin(realTracePath, options.kyosoRoot)) {
|
|
206969
|
+
throw new Error("trace file identity could not be verified");
|
|
206970
|
+
}
|
|
206971
|
+
return { handle, tracePath };
|
|
206972
|
+
} catch (error51) {
|
|
206973
|
+
try {
|
|
206974
|
+
await handle.close();
|
|
206975
|
+
} catch {}
|
|
206976
|
+
throw error51;
|
|
205011
206977
|
}
|
|
205012
|
-
return normalized;
|
|
205013
206978
|
}
|
|
205014
|
-
function
|
|
205015
|
-
|
|
206979
|
+
function secureOpenFlags(provided) {
|
|
206980
|
+
const openConstants = provided ?? constants2;
|
|
206981
|
+
const required2 = [
|
|
206982
|
+
openConstants.O_CREAT,
|
|
206983
|
+
openConstants.O_EXCL,
|
|
206984
|
+
openConstants.O_APPEND,
|
|
206985
|
+
openConstants.O_WRONLY,
|
|
206986
|
+
openConstants.O_NOFOLLOW,
|
|
206987
|
+
openConstants.O_NONBLOCK
|
|
206988
|
+
];
|
|
206989
|
+
if (required2.some((flag) => typeof flag !== "number" || flag <= 0)) {
|
|
206990
|
+
return;
|
|
206991
|
+
}
|
|
206992
|
+
return required2.reduce((combined, flag) => combined | flag, 0);
|
|
205016
206993
|
}
|
|
205017
|
-
function
|
|
205018
|
-
|
|
205019
|
-
return true;
|
|
205020
|
-
return matchesPathPattern(path, allowPatterns, "allow");
|
|
206994
|
+
function isSafeTraceId(traceId) {
|
|
206995
|
+
return traceId.length > 0 && traceId !== "." && traceId !== ".." && !traceId.includes("/") && !traceId.includes("\\");
|
|
205021
206996
|
}
|
|
205022
|
-
function
|
|
205023
|
-
|
|
205024
|
-
|
|
205025
|
-
|
|
205026
|
-
|
|
205027
|
-
|
|
205028
|
-
const base = normalizedPattern.slice(0, -2);
|
|
205029
|
-
if (mode === "allow") {
|
|
205030
|
-
const firstSegment = segments[0] ?? "";
|
|
205031
|
-
return firstSegment === base || firstSegment.startsWith(`${base}.`);
|
|
205032
|
-
}
|
|
205033
|
-
return segments.some((segment) => segment === base || segment.startsWith(`${base}.`));
|
|
205034
|
-
}
|
|
205035
|
-
if (normalizedPattern.includes("*")) {
|
|
205036
|
-
const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
205037
|
-
const regex = mode === "allow" ? new RegExp(`^${escaped}($|/)`) : new RegExp(`(^|/)${escaped}($|/)`);
|
|
205038
|
-
return regex.test(normalized);
|
|
205039
|
-
}
|
|
205040
|
-
if (!normalizedPattern.includes("/")) {
|
|
205041
|
-
if (mode === "allow") {
|
|
205042
|
-
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
|
|
205043
|
-
}
|
|
205044
|
-
return segments.includes(normalizedPattern);
|
|
205045
|
-
}
|
|
205046
|
-
if (mode === "allow") {
|
|
205047
|
-
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
|
|
206997
|
+
async function optionalLstat3(path) {
|
|
206998
|
+
try {
|
|
206999
|
+
return await lstat3(path);
|
|
207000
|
+
} catch (error51) {
|
|
207001
|
+
if (typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT") {
|
|
207002
|
+
return;
|
|
205048
207003
|
}
|
|
205049
|
-
|
|
205050
|
-
}
|
|
207004
|
+
throw error51;
|
|
207005
|
+
}
|
|
205051
207006
|
}
|
|
205052
207007
|
|
|
205053
207008
|
// src/audit/sanitize.ts
|
|
@@ -205070,46 +207025,149 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
205070
207025
|
}
|
|
205071
207026
|
|
|
205072
207027
|
// src/audit/trace.ts
|
|
207028
|
+
var AUDIT_WARNING_WRITE_FAILED = "AUDIT_WRITE_FAILED: Audit trace writing failed; no further audit events will be written.";
|
|
207029
|
+
var AUDIT_WARNING_FINALIZE_FAILED = "AUDIT_FINALIZE_FAILED: Audit trace close failed.";
|
|
207030
|
+
var AUDIT_WARNING_WRITE_AFTER_FINALIZE = "AUDIT_WRITE_AFTER_FINALIZE: Audit trace is already finalized.";
|
|
205073
207031
|
function createTraceWriter(options) {
|
|
205074
207032
|
const warnings = [];
|
|
205075
|
-
|
|
205076
|
-
return {
|
|
205077
|
-
warnings,
|
|
205078
|
-
async write() {
|
|
205079
|
-
return;
|
|
205080
|
-
}
|
|
205081
|
-
};
|
|
205082
|
-
}
|
|
207033
|
+
const warningSet = new Set;
|
|
205083
207034
|
const date5 = new Date().toISOString().slice(0, 10);
|
|
205084
|
-
|
|
205085
|
-
|
|
207035
|
+
let tracePath;
|
|
207036
|
+
let handle;
|
|
207037
|
+
let disabled = !options.enabled;
|
|
207038
|
+
let finalizing = false;
|
|
207039
|
+
let finalized = false;
|
|
207040
|
+
let queue = Promise.resolve();
|
|
207041
|
+
let finalizePromise;
|
|
207042
|
+
const addWarning = (warning) => {
|
|
207043
|
+
if (warningSet.has(warning))
|
|
207044
|
+
return;
|
|
207045
|
+
warningSet.add(warning);
|
|
207046
|
+
warnings.push(warning);
|
|
207047
|
+
};
|
|
207048
|
+
const closeHandle = async () => {
|
|
207049
|
+
const current = handle;
|
|
207050
|
+
handle = undefined;
|
|
207051
|
+
if (!current)
|
|
207052
|
+
return;
|
|
207053
|
+
try {
|
|
207054
|
+
await (options.closeHandle ?? defaultCloseHandle)(current);
|
|
207055
|
+
} catch {
|
|
207056
|
+
addWarning(AUDIT_WARNING_FINALIZE_FAILED);
|
|
207057
|
+
}
|
|
207058
|
+
};
|
|
207059
|
+
const disableAfterWriteFailure = async () => {
|
|
207060
|
+
disabled = true;
|
|
207061
|
+
addWarning(AUDIT_WARNING_WRITE_FAILED);
|
|
207062
|
+
await closeHandle();
|
|
207063
|
+
};
|
|
207064
|
+
const openIfNeeded = async () => {
|
|
207065
|
+
if (handle || disabled)
|
|
207066
|
+
return;
|
|
207067
|
+
if (secureOpenFlags(options.openConstants) === undefined) {
|
|
207068
|
+
disabled = true;
|
|
207069
|
+
addWarning(AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY);
|
|
207070
|
+
return;
|
|
207071
|
+
}
|
|
207072
|
+
const stateRoot = await resolveAuditStateRoot({
|
|
207073
|
+
cwd: options.cwd,
|
|
207074
|
+
directory: options.directory,
|
|
207075
|
+
env: options.env,
|
|
207076
|
+
platform: options.platform,
|
|
207077
|
+
getuid: options.getuid
|
|
207078
|
+
});
|
|
207079
|
+
for (const warning of stateRoot.warnings)
|
|
207080
|
+
addWarning(warning);
|
|
207081
|
+
if (!isResolvedAuditStateRoot(stateRoot)) {
|
|
207082
|
+
disabled = true;
|
|
207083
|
+
return;
|
|
207084
|
+
}
|
|
207085
|
+
try {
|
|
207086
|
+
const opened = await openVerifiedTraceFile({
|
|
207087
|
+
kyosoRoot: stateRoot.kyosoRoot,
|
|
207088
|
+
workspaceHash: stateRoot.workspaceHash,
|
|
207089
|
+
logicalDirectory: stateRoot.logicalDirectory,
|
|
207090
|
+
date: date5,
|
|
207091
|
+
traceId: options.traceId,
|
|
207092
|
+
uid: stateRoot.uid,
|
|
207093
|
+
workspaceRoot: stateRoot.workspaceRoot,
|
|
207094
|
+
openConstants: options.openConstants,
|
|
207095
|
+
beforeOpen: options.beforeOpen
|
|
207096
|
+
});
|
|
207097
|
+
handle = opened.handle;
|
|
207098
|
+
tracePath = opened.tracePath;
|
|
207099
|
+
} catch {
|
|
207100
|
+
disabled = true;
|
|
207101
|
+
addWarning(AUDIT_WARNING_WRITE_FAILED);
|
|
207102
|
+
}
|
|
207103
|
+
};
|
|
207104
|
+
const writeOne = async (event) => {
|
|
207105
|
+
if (disabled)
|
|
207106
|
+
return;
|
|
207107
|
+
try {
|
|
207108
|
+
await openIfNeeded();
|
|
207109
|
+
if (disabled || !handle)
|
|
207110
|
+
return;
|
|
207111
|
+
const line = Buffer.from(`${JSON.stringify(sanitizeForAudit(event, {
|
|
207112
|
+
includeRawAgentOutput: options.includeRawAgentOutput
|
|
207113
|
+
}))}
|
|
207114
|
+
`, "utf8");
|
|
207115
|
+
await writeFully(handle, line, options.writeChunk);
|
|
207116
|
+
} catch {
|
|
207117
|
+
await disableAfterWriteFailure();
|
|
207118
|
+
}
|
|
207119
|
+
};
|
|
205086
207120
|
return {
|
|
205087
|
-
tracePath
|
|
207121
|
+
get tracePath() {
|
|
207122
|
+
return tracePath;
|
|
207123
|
+
},
|
|
205088
207124
|
warnings,
|
|
205089
|
-
|
|
205090
|
-
|
|
205091
|
-
|
|
205092
|
-
|
|
205093
|
-
|
|
205094
|
-
|
|
205095
|
-
|
|
205096
|
-
|
|
205097
|
-
|
|
205098
|
-
|
|
207125
|
+
write(event) {
|
|
207126
|
+
if (finalizing || finalized) {
|
|
207127
|
+
addWarning(AUDIT_WARNING_WRITE_AFTER_FINALIZE);
|
|
207128
|
+
return Promise.resolve();
|
|
207129
|
+
}
|
|
207130
|
+
const task = queue.then(() => writeOne(event));
|
|
207131
|
+
queue = task.catch(async () => {
|
|
207132
|
+
await disableAfterWriteFailure();
|
|
207133
|
+
});
|
|
207134
|
+
return task.catch(() => {
|
|
207135
|
+
return;
|
|
207136
|
+
});
|
|
207137
|
+
},
|
|
207138
|
+
finalize() {
|
|
207139
|
+
if (finalizePromise)
|
|
207140
|
+
return finalizePromise;
|
|
207141
|
+
finalizing = true;
|
|
207142
|
+
finalizePromise = queue.then(async () => {
|
|
207143
|
+
await closeHandle();
|
|
207144
|
+
finalized = true;
|
|
207145
|
+
}).catch(() => {
|
|
207146
|
+
disabled = true;
|
|
207147
|
+
addWarning(AUDIT_WARNING_FINALIZE_FAILED);
|
|
207148
|
+
finalized = true;
|
|
207149
|
+
});
|
|
207150
|
+
return finalizePromise;
|
|
205099
207151
|
}
|
|
205100
207152
|
};
|
|
205101
207153
|
}
|
|
205102
|
-
function
|
|
205103
|
-
|
|
205104
|
-
|
|
205105
|
-
|
|
207154
|
+
async function writeFully(handle, buffer, writeChunk) {
|
|
207155
|
+
let offset = 0;
|
|
207156
|
+
while (offset < buffer.byteLength) {
|
|
207157
|
+
const bytesWritten = await (writeChunk ?? defaultWriteChunk)(handle, buffer, offset);
|
|
207158
|
+
if (!Number.isInteger(bytesWritten) || bytesWritten <= 0) {
|
|
207159
|
+
throw new Error("partial audit write could not advance");
|
|
205106
207160
|
}
|
|
205107
|
-
|
|
205108
|
-
} catch {
|
|
205109
|
-
warnings.push(`Unsafe audit directory ignored: ${directory}`);
|
|
205110
|
-
return ".kyoso/traces";
|
|
207161
|
+
offset += bytesWritten;
|
|
205111
207162
|
}
|
|
205112
207163
|
}
|
|
207164
|
+
async function defaultWriteChunk(handle, buffer, offset) {
|
|
207165
|
+
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset, null);
|
|
207166
|
+
return bytesWritten;
|
|
207167
|
+
}
|
|
207168
|
+
async function defaultCloseHandle(handle) {
|
|
207169
|
+
await handle.close();
|
|
207170
|
+
}
|
|
205113
207171
|
|
|
205114
207172
|
// src/context/truncate.ts
|
|
205115
207173
|
function truncateUtf8(input2, maxBytes) {
|
|
@@ -205526,40 +207584,40 @@ function decide(input2) {
|
|
|
205526
207584
|
}
|
|
205527
207585
|
|
|
205528
207586
|
// src/workspace/createSnapshot.ts
|
|
205529
|
-
import { chmod, mkdir as
|
|
205530
|
-
import { dirname as
|
|
205531
|
-
import { tmpdir } from "node:os";
|
|
207587
|
+
import { chmod, mkdir as mkdir6, mkdtemp as mkdtemp2, writeFile as writeFile6 } from "node:fs/promises";
|
|
207588
|
+
import { dirname as dirname8, join as join9 } from "node:path";
|
|
207589
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
205532
207590
|
async function createSnapshot(traceId, tool, request, options = {}) {
|
|
205533
|
-
const root = await
|
|
205534
|
-
const repoDir =
|
|
205535
|
-
const contextDir =
|
|
205536
|
-
await
|
|
205537
|
-
await
|
|
207591
|
+
const root = await mkdtemp2(join9(tmpdir2(), `kyoso-${traceId}-`));
|
|
207592
|
+
const repoDir = join9(root, "repo");
|
|
207593
|
+
const contextDir = join9(root, "context");
|
|
207594
|
+
await mkdir6(repoDir, { recursive: true });
|
|
207595
|
+
await mkdir6(contextDir, { recursive: true });
|
|
205538
207596
|
let fileCount = 0;
|
|
205539
207597
|
for (const file2 of request.selectedFiles ?? []) {
|
|
205540
|
-
const
|
|
205541
|
-
if (isDeniedPath(
|
|
207598
|
+
const relative4 = normalizeRelativePath(file2.path);
|
|
207599
|
+
if (isDeniedPath(relative4, options.denyPatterns ?? []))
|
|
205542
207600
|
continue;
|
|
205543
|
-
if (!isAllowedPath(
|
|
207601
|
+
if (!isAllowedPath(relative4, options.allowPatterns ?? []))
|
|
205544
207602
|
continue;
|
|
205545
|
-
const dest =
|
|
205546
|
-
await
|
|
205547
|
-
await
|
|
207603
|
+
const dest = join9(repoDir, relative4);
|
|
207604
|
+
await mkdir6(dirname8(dest), { recursive: true });
|
|
207605
|
+
await writeFile6(dest, file2.content, "utf8");
|
|
205548
207606
|
await chmod(dest, 292).catch(() => {
|
|
205549
207607
|
return;
|
|
205550
207608
|
});
|
|
205551
207609
|
fileCount += 1;
|
|
205552
207610
|
}
|
|
205553
|
-
await
|
|
205554
|
-
await
|
|
205555
|
-
await
|
|
205556
|
-
await
|
|
207611
|
+
await writeFile6(join9(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
|
|
207612
|
+
await writeFile6(join9(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
|
|
207613
|
+
await writeFile6(join9(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
|
|
207614
|
+
await writeFile6(join9(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
|
|
205557
207615
|
if (request.repoSummary)
|
|
205558
|
-
await
|
|
207616
|
+
await writeFile6(join9(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
205559
207617
|
if (request.currentPlan)
|
|
205560
|
-
await
|
|
207618
|
+
await writeFile6(join9(contextDir, "current_plan.md"), request.currentPlan, "utf8");
|
|
205561
207619
|
if (request.diff?.unifiedDiff)
|
|
205562
|
-
await
|
|
207620
|
+
await writeFile6(join9(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
|
|
205563
207621
|
return { root, repoDir, contextDir, fileCount };
|
|
205564
207622
|
}
|
|
205565
207623
|
function stripContents(request) {
|
|
@@ -205581,17 +207639,17 @@ function buildSelectedFilesManifest(request) {
|
|
|
205581
207639
|
}
|
|
205582
207640
|
|
|
205583
207641
|
// src/workspace/cleanup.ts
|
|
205584
|
-
import { rm } from "node:fs/promises";
|
|
207642
|
+
import { rm as rm2 } from "node:fs/promises";
|
|
205585
207643
|
async function cleanupSnapshot(path) {
|
|
205586
207644
|
if (process.env.KYOSO_KEEP_TEMP === "1")
|
|
205587
207645
|
return;
|
|
205588
|
-
await
|
|
207646
|
+
await rm2(path, { recursive: true, force: true });
|
|
205589
207647
|
}
|
|
205590
207648
|
|
|
205591
207649
|
// src/utils/ids.ts
|
|
205592
|
-
import { randomUUID } from "node:crypto";
|
|
207650
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
205593
207651
|
function newTraceId() {
|
|
205594
|
-
return `tr_${
|
|
207652
|
+
return `tr_${randomUUID2()}`;
|
|
205595
207653
|
}
|
|
205596
207654
|
|
|
205597
207655
|
// src/core/verification.ts
|
|
@@ -205649,7 +207707,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
205649
207707
|
if (!Array.isArray(parsed.verdicts))
|
|
205650
207708
|
return;
|
|
205651
207709
|
return parsed.verdicts.flatMap((item) => {
|
|
205652
|
-
if (!
|
|
207710
|
+
if (!isRecord11(item))
|
|
205653
207711
|
return [];
|
|
205654
207712
|
if (typeof item.findingId !== "string")
|
|
205655
207713
|
return [];
|
|
@@ -205727,7 +207785,7 @@ function verificationNote(reasoning) {
|
|
|
205727
207785
|
function isVerdict(value) {
|
|
205728
207786
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
205729
207787
|
}
|
|
205730
|
-
function
|
|
207788
|
+
function isRecord11(value) {
|
|
205731
207789
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
205732
207790
|
}
|
|
205733
207791
|
|
|
@@ -205736,43 +207794,50 @@ async function runReview(tool, request, options = {}) {
|
|
|
205736
207794
|
const cwd = options.cwd ?? process.cwd();
|
|
205737
207795
|
const traceId = newTraceId();
|
|
205738
207796
|
const startedAt = new Date().toISOString();
|
|
207797
|
+
const auditEnv = { ...process.env, ...options.env };
|
|
207798
|
+
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
205739
207799
|
let snapshot;
|
|
205740
207800
|
try {
|
|
205741
207801
|
assertNotChildAgent(options.env ?? process.env);
|
|
205742
207802
|
} catch (error51) {
|
|
205743
207803
|
if (error51 instanceof KyosoRequestError) {
|
|
205744
207804
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
205745
|
-
const trace2 =
|
|
207805
|
+
const trace2 = traceWriterFactory({
|
|
205746
207806
|
enabled: config2.audit.enabled,
|
|
205747
207807
|
directory: config2.audit.directory,
|
|
205748
207808
|
traceId,
|
|
205749
|
-
cwd
|
|
205750
|
-
|
|
205751
|
-
await trace2.write({
|
|
205752
|
-
type: "request_received",
|
|
205753
|
-
traceId,
|
|
205754
|
-
tool,
|
|
205755
|
-
timestamp: new Date().toISOString()
|
|
205756
|
-
});
|
|
205757
|
-
return await buildPolicyBlockResult({
|
|
205758
|
-
tool,
|
|
205759
|
-
trace: trace2,
|
|
205760
|
-
traceId,
|
|
205761
|
-
startedAt,
|
|
205762
|
-
networkMode: config2.network.defaultMode,
|
|
205763
|
-
warning: error51.message,
|
|
205764
|
-
finding: {
|
|
205765
|
-
id: "KYOSO-1",
|
|
205766
|
-
severity: "critical",
|
|
205767
|
-
category: "other",
|
|
205768
|
-
title: "Recursive Kyoso invocation blocked",
|
|
205769
|
-
evidence: error51.message,
|
|
205770
|
-
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
205771
|
-
sourceAgents: ["kyoso_policy"],
|
|
205772
|
-
confidence: "high"
|
|
205773
|
-
},
|
|
205774
|
-
redactionsApplied: 0
|
|
207809
|
+
cwd,
|
|
207810
|
+
env: auditEnv
|
|
205775
207811
|
});
|
|
207812
|
+
try {
|
|
207813
|
+
await trace2.write({
|
|
207814
|
+
type: "request_received",
|
|
207815
|
+
traceId,
|
|
207816
|
+
tool,
|
|
207817
|
+
timestamp: new Date().toISOString()
|
|
207818
|
+
});
|
|
207819
|
+
return await buildPolicyBlockResult({
|
|
207820
|
+
tool,
|
|
207821
|
+
trace: trace2,
|
|
207822
|
+
traceId,
|
|
207823
|
+
startedAt,
|
|
207824
|
+
networkMode: config2.network.defaultMode,
|
|
207825
|
+
warning: error51.message,
|
|
207826
|
+
finding: {
|
|
207827
|
+
id: "KYOSO-1",
|
|
207828
|
+
severity: "critical",
|
|
207829
|
+
category: "other",
|
|
207830
|
+
title: "Recursive Kyoso invocation blocked",
|
|
207831
|
+
evidence: error51.message,
|
|
207832
|
+
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
207833
|
+
sourceAgents: ["kyoso_policy"],
|
|
207834
|
+
confidence: "high"
|
|
207835
|
+
},
|
|
207836
|
+
redactionsApplied: 0
|
|
207837
|
+
});
|
|
207838
|
+
} finally {
|
|
207839
|
+
await trace2.finalize();
|
|
207840
|
+
}
|
|
205776
207841
|
}
|
|
205777
207842
|
throw error51;
|
|
205778
207843
|
}
|
|
@@ -205797,12 +207862,13 @@ async function runReview(tool, request, options = {}) {
|
|
|
205797
207862
|
...baseLoaded,
|
|
205798
207863
|
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
205799
207864
|
} : baseLoaded;
|
|
205800
|
-
const trace =
|
|
207865
|
+
const trace = traceWriterFactory({
|
|
205801
207866
|
enabled: loaded.config.audit.enabled,
|
|
205802
207867
|
directory: loaded.config.audit.directory,
|
|
205803
207868
|
traceId,
|
|
205804
207869
|
cwd,
|
|
205805
|
-
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput
|
|
207870
|
+
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
207871
|
+
env: auditEnv
|
|
205806
207872
|
});
|
|
205807
207873
|
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
205808
207874
|
try {
|
|
@@ -205884,11 +207950,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
205884
207950
|
manager,
|
|
205885
207951
|
trace
|
|
205886
207952
|
});
|
|
205887
|
-
warnings.push(...agentResults.flatMap((
|
|
207953
|
+
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
205888
207954
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
205889
|
-
const agentsUsed = normalizedAgentResults.map((
|
|
207955
|
+
const agentsUsed = normalizedAgentResults.map((result) => result.agent);
|
|
205890
207956
|
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
205891
|
-
const completed = normalizedAgentResults.filter((
|
|
207957
|
+
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
205892
207958
|
const degraded = completed.length !== agentResults.length;
|
|
205893
207959
|
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
205894
207960
|
reviewMode
|
|
@@ -205915,7 +207981,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
205915
207981
|
severity: "critical",
|
|
205916
207982
|
category: "other",
|
|
205917
207983
|
title: "All backend agents failed",
|
|
205918
|
-
evidence: normalizedAgentResults.map((
|
|
207984
|
+
evidence: normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
205919
207985
|
recommendation: "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
205920
207986
|
sourceAgents: ["kyoso_policy"],
|
|
205921
207987
|
confidence: "high"
|
|
@@ -205965,7 +208031,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
205965
208031
|
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
205966
208032
|
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
205967
208033
|
] : aggregate.residualRisks,
|
|
205968
|
-
agentOpinions: normalizedAgentResults.map((
|
|
208034
|
+
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
205969
208035
|
audit: {
|
|
205970
208036
|
traceId,
|
|
205971
208037
|
startedAt,
|
|
@@ -205997,15 +208063,10 @@ async function runReview(tool, request, options = {}) {
|
|
|
205997
208063
|
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
205998
208064
|
}));
|
|
205999
208065
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
206000
|
-
const
|
|
208066
|
+
const resultAfterJudge = {
|
|
206001
208067
|
...resultWithoutMarkdown,
|
|
206002
208068
|
disagreements,
|
|
206003
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
206004
|
-
summaryMarkdown: renderMarkdownResult(tool, {
|
|
206005
|
-
...resultWithoutMarkdown,
|
|
206006
|
-
disagreements,
|
|
206007
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
206008
|
-
}, { summaryText: judge.output.summaryText })
|
|
208069
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
206009
208070
|
};
|
|
206010
208071
|
const judgeEvent = {
|
|
206011
208072
|
type: "judge_completed",
|
|
@@ -206017,7 +208078,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
206017
208078
|
if (judge.error)
|
|
206018
208079
|
judgeEvent.error = judge.error;
|
|
206019
208080
|
await trace.write(judgeEvent);
|
|
206020
|
-
|
|
208081
|
+
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
206021
208082
|
await trace.write({
|
|
206022
208083
|
type: "decision_completed",
|
|
206023
208084
|
traceId,
|
|
@@ -206029,8 +208090,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
206029
208090
|
traceId,
|
|
206030
208091
|
timestamp: new Date().toISOString()
|
|
206031
208092
|
});
|
|
206032
|
-
return
|
|
208093
|
+
return await finalizeReviewResult({
|
|
208094
|
+
tool,
|
|
208095
|
+
trace,
|
|
208096
|
+
result: resultAfterJudge,
|
|
208097
|
+
summaryText: judge.output.summaryText
|
|
208098
|
+
});
|
|
206033
208099
|
} finally {
|
|
208100
|
+
await trace.finalize();
|
|
206034
208101
|
if (snapshot)
|
|
206035
208102
|
await cleanupSnapshot(snapshot.root);
|
|
206036
208103
|
}
|
|
@@ -206279,10 +208346,6 @@ async function buildSecretBlockResult(input2) {
|
|
|
206279
208346
|
warnings: input2.warnings
|
|
206280
208347
|
}
|
|
206281
208348
|
};
|
|
206282
|
-
const result = {
|
|
206283
|
-
...resultWithoutMarkdown,
|
|
206284
|
-
summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
|
|
206285
|
-
};
|
|
206286
208349
|
await input2.trace.write({
|
|
206287
208350
|
type: "decision_completed",
|
|
206288
208351
|
traceId: input2.traceId,
|
|
@@ -206294,7 +208357,11 @@ async function buildSecretBlockResult(input2) {
|
|
|
206294
208357
|
traceId: input2.traceId,
|
|
206295
208358
|
timestamp: new Date().toISOString()
|
|
206296
208359
|
});
|
|
206297
|
-
return
|
|
208360
|
+
return await finalizeReviewResult({
|
|
208361
|
+
tool: input2.tool,
|
|
208362
|
+
trace: input2.trace,
|
|
208363
|
+
result: resultWithoutMarkdown
|
|
208364
|
+
});
|
|
206298
208365
|
}
|
|
206299
208366
|
function buildSecretFinding(secretScan, options) {
|
|
206300
208367
|
return {
|
|
@@ -206344,10 +208411,6 @@ async function buildPolicyBlockResult(input2) {
|
|
|
206344
208411
|
warnings: [input2.warning]
|
|
206345
208412
|
}
|
|
206346
208413
|
};
|
|
206347
|
-
const result = {
|
|
206348
|
-
...resultWithoutMarkdown,
|
|
206349
|
-
summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
|
|
206350
|
-
};
|
|
206351
208414
|
await input2.trace.write({
|
|
206352
208415
|
type: "decision_completed",
|
|
206353
208416
|
traceId: input2.traceId,
|
|
@@ -206359,7 +208422,30 @@ async function buildPolicyBlockResult(input2) {
|
|
|
206359
208422
|
traceId: input2.traceId,
|
|
206360
208423
|
timestamp: new Date().toISOString()
|
|
206361
208424
|
});
|
|
206362
|
-
return
|
|
208425
|
+
return await finalizeReviewResult({
|
|
208426
|
+
tool: input2.tool,
|
|
208427
|
+
trace: input2.trace,
|
|
208428
|
+
result: resultWithoutMarkdown
|
|
208429
|
+
});
|
|
208430
|
+
}
|
|
208431
|
+
async function finalizeReviewResult(input2) {
|
|
208432
|
+
await input2.trace.finalize();
|
|
208433
|
+
const result = {
|
|
208434
|
+
...input2.result,
|
|
208435
|
+
audit: {
|
|
208436
|
+
...input2.result.audit,
|
|
208437
|
+
warnings: Array.from(new Set([
|
|
208438
|
+
...input2.result.audit.warnings ?? [],
|
|
208439
|
+
...input2.trace.warnings
|
|
208440
|
+
]))
|
|
208441
|
+
}
|
|
208442
|
+
};
|
|
208443
|
+
return {
|
|
208444
|
+
...result,
|
|
208445
|
+
summaryMarkdown: renderMarkdownResult(input2.tool, result, {
|
|
208446
|
+
summaryText: input2.summaryText
|
|
208447
|
+
})
|
|
208448
|
+
};
|
|
206363
208449
|
}
|
|
206364
208450
|
function mergeDenyPatterns(configDeny, requestDeny) {
|
|
206365
208451
|
return Array.from(new Set([...configDeny, ...requestDeny ?? []]));
|
|
@@ -206367,7 +208453,7 @@ function mergeDenyPatterns(configDeny, requestDeny) {
|
|
|
206367
208453
|
function assertTrustedWorkspaceRoot(requestRoot, configRoot, cwd) {
|
|
206368
208454
|
if (!requestRoot)
|
|
206369
208455
|
return;
|
|
206370
|
-
if (
|
|
208456
|
+
if (resolve10(cwd, requestRoot) !== resolve10(cwd, configRoot)) {
|
|
206371
208457
|
throw new KyosoRequestError("workspace.root is not trusted by config", "UNTRUSTED_WORKSPACE_ROOT");
|
|
206372
208458
|
}
|
|
206373
208459
|
}
|
|
@@ -206485,7 +208571,9 @@ async function main() {
|
|
|
206485
208571
|
write: booleanFlag(parsed.flags, "write"),
|
|
206486
208572
|
global: booleanFlag(parsed.flags, "global"),
|
|
206487
208573
|
runner: stringFlag(parsed.flags, "runner"),
|
|
206488
|
-
command: stringFlag(parsed.flags, "command")
|
|
208574
|
+
command: stringFlag(parsed.flags, "command"),
|
|
208575
|
+
skillOnly: booleanFlag(parsed.flags, "skill-only"),
|
|
208576
|
+
force: booleanFlag(parsed.flags, "force")
|
|
206489
208577
|
}));
|
|
206490
208578
|
return;
|
|
206491
208579
|
}
|
|
@@ -206542,7 +208630,7 @@ async function buildDiff(tool, flags) {
|
|
|
206542
208630
|
return;
|
|
206543
208631
|
const base = stringFlag(flags, "base") ?? "main";
|
|
206544
208632
|
const head = stringFlag(flags, "head") ?? "HEAD";
|
|
206545
|
-
const result =
|
|
208633
|
+
const result = spawnSync3("git", ["diff", base, head], { encoding: "utf8" });
|
|
206546
208634
|
if (result.status !== 0) {
|
|
206547
208635
|
throw new Error(result.stderr || `git diff failed for ${base}..${head}`);
|
|
206548
208636
|
}
|
|
@@ -206588,7 +208676,8 @@ var HELP = `Kyoso
|
|
|
206588
208676
|
|
|
206589
208677
|
Usage:
|
|
206590
208678
|
kyoso mcp [--config kyoso.toml|kyoso.config.ts] [--ignore-config] [--trust-config] [--allow-unknown-config] [--network model_only|unrestricted]
|
|
206591
|
-
kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global]
|
|
208679
|
+
kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]
|
|
208680
|
+
kyoso setup codex|claude-code --skill-only [--write] [--global] [--force]
|
|
206592
208681
|
kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|
|
206593
208682
|
kyoso security --goal <text> [--diff <path>] [--file <path>] [--set <key>=<value>]... [--json] [--allow-secret-redaction] [--trust-config] [--allow-unknown-config]
|
|
206594
208683
|
kyoso diff --base main --head HEAD [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|