feat(projects): add interactive AI app scaffold skill

This commit is contained in:
2026-09-04 20:36:28 +08:00
parent 336e0bb0ca
commit 959b6333b5
40 changed files with 2647 additions and 65 deletions

View File

@@ -21,7 +21,13 @@ const PLAN_TTL_MS = 10 * 60 * 1000;
const INSTALL_OUTPUT_BYTES = 64 * 1024;
const INSTALL_TIMEOUT_MS = 2 * 60 * 1000;
const INDEX_SCHEMA_VERSION = 1;
const EXECUTABLE_WARNING = '此包包含可执行 Pi extension,将获得完整桌面权限,包括当前进程可用的文件、网络和进程权限。';
const EXECUTABLE_WARNING = '此包包含可执行代码,将获得完整桌面权限,包括当前进程可用的文件、网络和进程权限。';
const SKILL_SCRIPT_WARNING = '此包包含可执行 Skill 脚本Skill 被调用时,脚本可能使用当前桌面用户可用的文件、网络和进程权限。';
function executableWarning(kind: InstallPreviewV1['kind'], includesExecutableCode: boolean): string | null {
if (!includesExecutableCode) return null;
return kind === 'skill-only' ? SKILL_SCRIPT_WARNING : EXECUTABLE_WARNING;
}
export class DevicePackageError extends Error {
constructor(
@@ -375,7 +381,10 @@ export class DevicePackageManager {
resolvedSource: string;
inspected: Awaited<ReturnType<typeof inspectDevicePackage>>;
}): Promise<InstallPreviewV1> {
const includesExecutableCode = input.inspected.extensionEntries.length > 0;
const kind = devicePackageKind(input.inspected);
const includesExecutableCode = input.inspected.extensionEntries.length > 0
|| input.inspected.hasSkillScripts;
const warning = executableWarning(kind, includesExecutableCode);
const resolvedVersion = input.inspected.resolvedVersion
?? `local-${new Date(this.now()).toISOString().replace(/[-:.]/gu, '')}`;
safeStorageSegment(resolvedVersion);
@@ -388,12 +397,12 @@ export class DevicePackageManager {
packageId: input.inspected.packageId,
displayName: input.inspected.displayName,
resolvedVersion,
kind: devicePackageKind(input.inspected),
kind,
skillEntries: input.inspected.skillEntries,
extensionEntries: input.inspected.extensionEntries,
includesExecutableCode,
ignoredLifecycleScripts: input.inspected.ignoredLifecycleScripts,
warnings: includesExecutableCode ? [EXECUTABLE_WARNING] : [],
warnings: warning ? [warning] : [],
scope: 'device-parent-workers',
};
const record: DevicePackageRecordV1 = {
@@ -435,7 +444,8 @@ export class DevicePackageManager {
if (Date.parse(plan.preview.expiresAt) <= this.now()) {
throw new DevicePackageError('local_package_plan_expired', 'Install preview has expired');
}
if (plan.preview.includesExecutableCode && !plan.preview.warnings.includes(EXECUTABLE_WARNING)) {
const warning = executableWarning(plan.preview.kind, plan.preview.includesExecutableCode);
if (warning && !plan.preview.warnings.includes(warning)) {
throw new DevicePackageError('local_package_confirmation_required', 'Executable permission warning is missing');
}
const stagedPackage = path.join(this.stagingDir, planId, 'package');