@miphamai/cli 0.77.1 → 0.77.2
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/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const PACKAGE_NAME = '@miphamai/cli' as const
|
|
10
10
|
|
|
11
11
|
/** 当前发布版本 */
|
|
12
|
-
export const PACKAGE_VERSION = '0.77.
|
|
12
|
+
export const PACKAGE_VERSION = '0.77.2' as const
|
|
13
13
|
|
|
14
14
|
/** npm install 全局安装命令 */
|
|
15
15
|
export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
|
|
@@ -53,6 +53,17 @@ export function isValidMarketplaceRef(owner: string, repo: string): boolean {
|
|
|
53
53
|
return valid(owner) && valid(repo)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* A skill name is used as a filename segment when installing from a marketplace.
|
|
58
|
+
* It must be a single safe segment: start with an alphanumeric and contain only
|
|
59
|
+
* alphanumerics, dots, dashes, and underscores — no path separators (`/`, `\`),
|
|
60
|
+
* no leading dot (so `..` cannot traverse), no whitespace. Marketplace frontmatter
|
|
61
|
+
* is untrusted, so this gate fails closed before the name ever touches a path.
|
|
62
|
+
*/
|
|
63
|
+
export function isValidSkillName(name: string): boolean {
|
|
64
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
/**
|
|
57
68
|
* Read the marketplace sources from disk. A missing or corrupt file falls back
|
|
58
69
|
* to the default Anthropic sources.
|
package/src/skills/registry.ts
CHANGED
|
@@ -17,7 +17,12 @@ import { join } from 'node:path'
|
|
|
17
17
|
import { homedir } from 'node:os'
|
|
18
18
|
import type { MiphamConfig } from '../shared/types.js'
|
|
19
19
|
import communitySkills from './community-registry.json'
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
readMarketplaces,
|
|
22
|
+
findSkillInMarketplaces,
|
|
23
|
+
downloadFile,
|
|
24
|
+
isValidSkillName,
|
|
25
|
+
} from './marketplace'
|
|
21
26
|
|
|
22
27
|
const SKILLS_DIR = join(homedir(), '.mipham', 'skills')
|
|
23
28
|
|
|
@@ -221,6 +226,14 @@ async function installFromMarketplace(
|
|
|
221
226
|
}
|
|
222
227
|
}
|
|
223
228
|
|
|
229
|
+
if (!isValidSkillName(found.name)) {
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
name: found.name,
|
|
233
|
+
message: `Refusing to install skill with unsafe name "${found.name}": skill names must be a single safe filename segment (letters, digits, dots, dashes, underscores).`,
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
224
237
|
const destPath = join(SKILLS_DIR, `${found.name}.SKILL.md`)
|
|
225
238
|
if (existsSync(destPath)) {
|
|
226
239
|
return {
|
|
@@ -279,6 +292,14 @@ export async function installSkillFromUrl(
|
|
|
279
292
|
.pop()
|
|
280
293
|
?.replace(/\.(SKILL\.)?md$/i, '') || 'custom-skill'
|
|
281
294
|
|
|
295
|
+
if (!isValidSkillName(name)) {
|
|
296
|
+
return {
|
|
297
|
+
success: false,
|
|
298
|
+
name,
|
|
299
|
+
message: `Refusing to install from URL with unsafe name "${name}": skill names must be a single safe filename segment (letters, digits, dots, dashes, underscores).`,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
282
303
|
const destDir = join(SKILLS_DIR)
|
|
283
304
|
const destPath = join(destDir, `${name}.SKILL.md`)
|
|
284
305
|
|
|
@@ -322,6 +343,14 @@ export function listInstalledSkills(): string[] {
|
|
|
322
343
|
* Remove an installed skill.
|
|
323
344
|
*/
|
|
324
345
|
export function removeSkill(skillName: string): InstallResult {
|
|
346
|
+
if (!isValidSkillName(skillName)) {
|
|
347
|
+
return {
|
|
348
|
+
success: false,
|
|
349
|
+
name: skillName,
|
|
350
|
+
message: `Refusing to remove skill with unsafe name "${skillName}": skill names must be a single safe filename segment (letters, digits, dots, dashes, underscores).`,
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
325
354
|
const destPath = join(SKILLS_DIR, `${skillName}.SKILL.md`)
|
|
326
355
|
if (!existsSync(destPath)) {
|
|
327
356
|
return { success: false, name: skillName, message: `Skill "${skillName}" is not installed.` }
|
package/src/tools/exec/bash.ts
CHANGED
|
@@ -320,7 +320,7 @@ export function createBashTool(credentialConfig?: CredentialMaskingConfig): Tool
|
|
|
320
320
|
command: { type: 'string', description: 'The bash command to execute' },
|
|
321
321
|
description: {
|
|
322
322
|
type: 'string',
|
|
323
|
-
description: 'What this command does (
|
|
323
|
+
description: 'What this command does, in plain words (do not echo the command itself)',
|
|
324
324
|
},
|
|
325
325
|
timeout: {
|
|
326
326
|
type: 'integer',
|