Files
market/skills/app-developer/scripts/create-release.mjs
Yige a514b55b68 feat: 收录应用开发发布技能与双语示例 (#214)
## 中文

新增按需调用的内置技能 `app-developer`(应用开发与发布):中英正文、sidecar、MIT 许可、静态与服务两个通用工作台示例,以及作者接口与分发参考,并登记进 `builtin-skills.json`。作者管理优先使用 AppManage 的有限动作,执行前须通过 `authoring_capabilities` 确认 `app-authoring-v1`,能力不可用时停止受影响的操作;外部 Node/Docker 运行时、托管、Git 服务与数据库的条款和可能费用已在发现文案、compatibility 与正文披露。另将 dev-environment-setup 探测参考中的三处用户 home 示例改为通用路径。

与 #220、#212 合入后的 main 解决 manifest 冲突:技能总数 144,市场版本 1.5.13。验证:catalog 与 i18n 校验 0 errors / 106 warnings(含新技能尚未 ref-bound review 的治理提醒),translate --check 与 git diff --check 通过;远端 validate、translate、CodeQL 通过。Copilot 因额度用尽未能审查。

## English

Add the on-demand builtin `app-developer` skill (application development and publishing): English/Chinese bodies, sidecar, MIT license, static and service workbench examples, plus authoring-interface and distribution references, registered in `builtin-skills.json`. Authoring prefers AppManage's finite actions and must confirm `app-authoring-v1` through `authoring_capabilities` first, stopping affected operations when the capability is unavailable. External Node/Docker runtime, hosting, Git service and database terms and possible costs are disclosed. Three user-home examples in the dev-environment-setup probe reference are neutralized.

The manifest conflict with main after #220 and #212 is resolved: 144 skills, market version 1.5.13. Catalog and i18n validation report 0 errors with 106 warnings (including the new skill's pending ref-bound review notice); translate --check and git diff --check pass; remote validate, translate and CodeQL passed. Copilot could not review because of its quota limit.
2026-10-18 17:53:37 +08:00

35 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 教学清单生成器:读取显式inputs,固定原始字节;不是平台自动build/install器。 */
import { createHash } from 'node:crypto'
import { lstatSync, readFileSync, writeFileSync } from 'node:fs'
import { resolve, join } from 'node:path'
if (!process.argv[2]) throw new Error('example_directory_required')
const directory = resolve(process.argv[2])
const input = JSON.parse(readFileSync(join(directory, 'release-inputs.json'), 'utf8'))
const hash = bytes => createHash('sha256').update(bytes).digest('hex')
const canonical = value => Array.isArray(value) ? value.map(canonical) : value && typeof value === 'object'
? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])])) : value
const files = input.files.map(file => {
if (typeof file.path !== 'string' || /[\\\x00-\x1f?#:]/.test(file.path) || file.path.startsWith('/') ||
file.path.split('/').some(part => !part || part === '.' || part === '..') || file.path === 'app-release.json') throw new Error('invalid_payload_path')
const parts = file.path.split('/')
for (let i = 1; i <= parts.length; i++) if (lstatSync(join(directory, ...parts.slice(0, i))).isSymbolicLink()) throw new Error('payload_link_rejected')
const bytes = readFileSync(join(directory, file.path))
return { path: file.path, role: file.role, sha256: hash(bytes), size: bytes.length }
}).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)
if (new Set(files.map(file => file.path)).size !== files.length) throw new Error('duplicate_payload_path')
if (!files.some(file => file.path === 'application.json' && file.role === 'definition') ||
!files.some(file => file.path === '说明.md') ||
(input.dataContractPath && !files.some(file => file.path === input.dataContractPath && file.role === 'data-contract'))) throw new Error('missing_declared_payload')
const definition = JSON.parse(readFileSync(join(directory, 'application.json'), 'utf8'))
const manifest = { schemaVersion: 1, canonicalization: 'app-release-c14n-v1', definition, delivery: input.delivery, files,
source: { kind: 'worktree', sha256: hash(JSON.stringify(canonical(files))) }, supportedPlatforms: input.supportedPlatforms,
minimumClientVersion: '10.0.180', requiredCapabilities: input.requiredCapabilities,
...(input.dataContractPath ? { dataContract: JSON.parse(readFileSync(join(directory, input.dataContractPath), 'utf8')) } : {}),
...(input.bridgeCapabilities ? { bridgeCapabilities: input.bridgeCapabilities } : {}),
...(input.viewStateContract ? { viewStateContract: input.viewStateContract } : {}),
instructionsPath: '说明.md' }
manifest.releaseDigest = hash(JSON.stringify(canonical(manifest)))
writeFileSync(join(directory, 'app-release.json'), JSON.stringify(manifest, null, 2) + '\n')
console.log(JSON.stringify({ appId: definition.id, version: definition.version, releaseDigest: manifest.releaseDigest, files: files.length }))