Skip to content

Commit 327160f

Browse files
committed
fix: unify skills zip target resolution
1 parent 0e22e4f commit 327160f

2 files changed

Lines changed: 63 additions & 28 deletions

File tree

cli.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,18 +2176,21 @@ async function importSkillsFromZip(payload = {}) {
21762176
if (!payload || typeof payload.fileBase64 !== 'string' || !payload.fileBase64.trim()) {
21772177
return { error: '缺少技能压缩包内容' };
21782178
}
2179-
const rawTargetApp = payload.targetApp || payload.target || 'codex';
2180-
const fallbackTargetApp = normalizeSkillTargetApp(rawTargetApp) || 'codex';
2179+
const fallbackTarget = resolveSkillTarget(payload, 'codex');
2180+
const fallbackTargetApp = fallbackTarget ? fallbackTarget.app : 'codex';
21812181
const fallbackName = payload.fileName || `${fallbackTargetApp}-skills.zip`;
21822182
const upload = writeUploadZip(payload.fileBase64, 'codex-skills-import', fallbackName);
21832183
if (upload.error) {
21842184
return { error: upload.error };
21852185
}
2186-
return importSkillsFromZipFile(upload.zipPath, {
2187-
tempDir: upload.tempDir,
2188-
fallbackName,
2189-
targetApp: rawTargetApp
2190-
});
2186+
const importOptions = { tempDir: upload.tempDir, fallbackName };
2187+
if (Object.prototype.hasOwnProperty.call(payload, 'targetApp')) {
2188+
importOptions.targetApp = payload.targetApp;
2189+
}
2190+
if (Object.prototype.hasOwnProperty.call(payload, 'target')) {
2191+
importOptions.target = payload.target;
2192+
}
2193+
return importSkillsFromZipFile(upload.zipPath, importOptions);
21912194
}
21922195

21932196
async function importCodexSkillsFromZip(payload = {}) {
@@ -9833,13 +9836,18 @@ function resolveUploadFileNameFromRequest(req, fallbackName = 'codex-skills.zip'
98339836
}
98349837

98359838
function resolveSkillTargetAppFromRequest(req, fallbackApp = 'codex') {
9836-
const fallback = normalizeSkillTargetApp(fallbackApp) || 'codex';
9839+
const fallbackTarget = resolveSkillTarget({}, fallbackApp);
9840+
const fallback = fallbackTarget ? fallbackTarget.app : 'codex';
98379841
try {
98389842
const parsed = new URL(req.url || '/', 'http://localhost');
9839-
if (parsed.searchParams.has('targetApp') || parsed.searchParams.has('target')) {
9840-
return normalizeSkillTargetApp(
9841-
parsed.searchParams.get('targetApp') || parsed.searchParams.get('target')
9842-
) || null;
9843+
const hasTargetApp = parsed.searchParams.has('targetApp');
9844+
const hasTarget = parsed.searchParams.has('target');
9845+
if (hasTargetApp || hasTarget) {
9846+
const target = resolveSkillTarget({
9847+
...(hasTargetApp ? { targetApp: parsed.searchParams.get('targetApp') } : {}),
9848+
...(hasTarget ? { target: parsed.searchParams.get('target') } : {})
9849+
}, fallback);
9850+
return target ? target.app : null;
98439851
}
98449852
return fallback;
98459853
} catch (_) {

tests/unit/web-run-host.test.mjs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ const resolveSkillTargetAppFromRequest = instantiateFunction(
291291
'resolveSkillTargetAppFromRequest',
292292
{
293293
URL,
294-
normalizeSkillTargetApp
294+
resolveSkillTarget
295295
}
296296
);
297297
const resolveCopyTargetRootSource = extractFunctionBySignature(
@@ -723,10 +723,7 @@ test('importSkillsFromZip reuses a target-specific fallback zip name for base64
723723
let uploadArgs = null;
724724
let importedOptions = null;
725725
const importSkillsFromZip = instantiateFunction(importSkillsFromZipSource, 'importSkillsFromZip', {
726-
normalizeSkillTargetApp(app) {
727-
const value = typeof app === 'string' ? app.trim().toLowerCase() : '';
728-
return value === 'codex' || value === 'claude' ? value : '';
729-
},
726+
resolveSkillTarget,
730727
writeUploadZip(fileBase64, prefix, fileName) {
731728
uploadArgs = { fileBase64, prefix, fileName };
732729
return { zipPath: '/tmp/claude.zip', tempDir: '/tmp/claude-upload' };
@@ -750,7 +747,7 @@ test('importSkillsFromZip reuses a target-specific fallback zip name for base64
750747
assert.deepStrictEqual(importedOptions, {
751748
tempDir: '/tmp/claude-upload',
752749
fallbackName: 'claude-skills.zip',
753-
targetApp: 'claude'
750+
target: 'claude'
754751
});
755752
assert.deepStrictEqual(result, { imported: [] });
756753
});
@@ -759,10 +756,7 @@ test('importSkillsFromZip keeps the raw explicit target for downstream validatio
759756
let uploadArgs = null;
760757
let importedOptions = null;
761758
const importSkillsFromZip = instantiateFunction(importSkillsFromZipSource, 'importSkillsFromZip', {
762-
normalizeSkillTargetApp(app) {
763-
const value = typeof app === 'string' ? app.trim().toLowerCase() : '';
764-
return value === 'codex' || value === 'claude' ? value : '';
765-
},
759+
resolveSkillTarget,
766760
writeUploadZip(fileBase64, prefix, fileName) {
767761
uploadArgs = { fileBase64, prefix, fileName };
768762
return { zipPath: '/tmp/upload.zip', tempDir: '/tmp/upload-dir' };
@@ -786,7 +780,7 @@ test('importSkillsFromZip keeps the raw explicit target for downstream validatio
786780
assert.deepStrictEqual(importedOptions, {
787781
tempDir: '/tmp/upload-dir',
788782
fallbackName: 'codex-skills.zip',
789-
targetApp: 'claud'
783+
target: 'claud'
790784
});
791785
assert.deepStrictEqual(result, { error: '目标宿主不支持' });
792786
});
@@ -795,10 +789,7 @@ test('importSkillsFromZip keeps targetApp precedence over target', async () => {
795789
let uploadArgs = null;
796790
let importedOptions = null;
797791
const importSkillsFromZip = instantiateFunction(importSkillsFromZipSource, 'importSkillsFromZip', {
798-
normalizeSkillTargetApp(app) {
799-
const value = typeof app === 'string' ? app.trim().toLowerCase() : '';
800-
return value === 'codex' || value === 'claude' ? value : '';
801-
},
792+
resolveSkillTarget,
802793
writeUploadZip(fileBase64, prefix, fileName) {
803794
uploadArgs = { fileBase64, prefix, fileName };
804795
return { zipPath: '/tmp/claude.zip', tempDir: '/tmp/claude-upload' };
@@ -823,11 +814,47 @@ test('importSkillsFromZip keeps targetApp precedence over target', async () => {
823814
assert.deepStrictEqual(importedOptions, {
824815
tempDir: '/tmp/claude-upload',
825816
fallbackName: 'claude-skills.zip',
826-
targetApp: 'claude'
817+
targetApp: 'claude',
818+
target: 'codex'
827819
});
828820
assert.deepStrictEqual(result, { imported: [] });
829821
});
830822

823+
test('importSkillsFromZip preserves conflicting explicit target keys for downstream rejection', async () => {
824+
let uploadArgs = null;
825+
let importedOptions = null;
826+
const importSkillsFromZip = instantiateFunction(importSkillsFromZipSource, 'importSkillsFromZip', {
827+
resolveSkillTarget,
828+
writeUploadZip(fileBase64, prefix, fileName) {
829+
uploadArgs = { fileBase64, prefix, fileName };
830+
return { zipPath: '/tmp/upload.zip', tempDir: '/tmp/upload-dir' };
831+
},
832+
importSkillsFromZipFile: async (_zipPath, options) => {
833+
importedOptions = options;
834+
return { error: '目标宿主不支持' };
835+
}
836+
});
837+
838+
const result = await importSkillsFromZip({
839+
fileBase64: 'QUJD',
840+
targetApp: 'claud',
841+
target: 'claude'
842+
});
843+
844+
assert.deepStrictEqual(uploadArgs, {
845+
fileBase64: 'QUJD',
846+
prefix: 'codex-skills-import',
847+
fileName: 'codex-skills.zip'
848+
});
849+
assert.deepStrictEqual(importedOptions, {
850+
tempDir: '/tmp/upload-dir',
851+
fallbackName: 'codex-skills.zip',
852+
targetApp: 'claud',
853+
target: 'claude'
854+
});
855+
assert.deepStrictEqual(result, { error: '目标宿主不支持' });
856+
});
857+
831858
test('codex-only zip upload route pins target app before request fallback resolution', () => {
832859
assert.match(
833860
cliContent,

0 commit comments

Comments
 (0)