@promptframe/cli 0.1.64 → 0.1.67
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/README.md +36 -1
- package/dist/configuration.d.ts +13 -0
- package/dist/configuration.d.ts.map +1 -0
- package/dist/configuration.js +36 -0
- package/dist/configuration.js.map +1 -0
- package/dist/index.js +130 -29
- package/dist/index.js.map +1 -1
- package/dist/preview-resource-draft.d.ts +31 -0
- package/dist/preview-resource-draft.d.ts.map +1 -0
- package/dist/preview-resource-draft.js +185 -0
- package/dist/preview-resource-draft.js.map +1 -0
- package/dist/preview-resource-server.d.ts +23 -0
- package/dist/preview-resource-server.d.ts.map +1 -0
- package/dist/preview-resource-server.js +278 -0
- package/dist/preview-resource-server.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# @promptframe/cli
|
|
2
2
|
|
|
3
|
+
`package` 和目录形式的 `upload` 使用所选组件目录及其子目录中的 `.gitignore`,支持嵌套覆盖、`!` 和目录规则;不读取组件目录外的 Git 配置。`node_modules`、`dist`、`.git`、`.promptframe`、`.component-packages` 仍固定排除。将本地出图和测试产物放在明确忽略的目录中。
|
|
4
|
+
|
|
5
|
+
必需文件、manifest 声明的源码/props schema 和 `public/` 资源不能被静默排除;规则命中时返回 `package.required_file.ignored`,应修正规则后再打包。保留文件仍接受原安全扫描,原始 lockfile 不入包,脱敏 lockfile evidence 保留。
|
|
6
|
+
|
|
3
7
|
PromptFrame component authoring CLI.
|
|
4
8
|
|
|
5
9
|
Use it to inspect the public component standard, check component folders, validate manifests, inspect the local preview envelope, package source archives, upload components, check build status, rebuild evidence indexes, and rerun layout/security probes.
|
|
6
10
|
|
|
7
|
-
Current source baseline is `@promptframe/cli@0.1.
|
|
11
|
+
Current source baseline is `@promptframe/cli@0.1.67`, `@promptframe/contracts@0.1.35`, `@promptframe/component-kit@0.1.29`, and `create-promptframe-component@0.1.57`. The published CLI consumes the contracts AST-aware public security policy evaluator, reports `securityPolicyDigest` / `securityEvaluatorMode` in JSON output, rejects red-team browser capability vectors such as Image/Audio beacons, Worker/SharedWorker, `window.open`, `postMessage`, dynamic script/iframe creation, browser fingerprinting, storage/cookie access, and dangerous HTML, reports `publicResources` for component-level public assets, keeps native `<img>` / `<video>` source checks case-sensitive so Remotion `<Img>` / `<Video>` remain valid, validates preview props against statically declared `propsSchema` fields and requires readable public prop descriptions, enforces strict Slot-first layout/source diagnostics, supports endpoint discovery, project list/current diagnostics, self-service CI token create/list/revoke, single-component and workspace GitHub workflow setup with `promptframe-workflow-version: 2`, `setup-ci --upgrade` stale-template repair, dynamic workspace matrix discovery, `RUNNER_LABELS` and pnpm 10 workflow support, pnpm workspace root lockfile evidence without component symlinks, `promptframe-workspace.json` validation, workspace root `check .` / `upload .` auto-detection, explicit `workspace:*` shared-package diagnostics, workspace upload source metadata headers, packages sanitized lockfile evidence for platform admission, release notes forwarding to upload, stderr NDJSON upload progress for `--json`, human-readable token kind/expiry summaries for `login`, `whoami`, and authenticated `upload`, server version auto-bump notes, and supports `status --fail-on-build-failed` for CI build-status gating.
|
|
8
12
|
|
|
9
13
|
```bash
|
|
10
14
|
npm install -D @promptframe/cli
|
|
@@ -202,3 +206,34 @@ promptframe dev stop .
|
|
|
202
206
|
关闭开发面板不停止连接;切换账号或作品、关闭页面会断开。刷新页面后重新粘贴连接JSON。停止或替换仅操作本工作区的实际实例;无法确认实例退出时命令报错,不自动强制终止进程。
|
|
203
207
|
|
|
204
208
|
上传组件后,用准确的正式 `componentRef` 替换作品节点引用并移除该节点的 `componentSource`。仅上传成功不会自动改变作品的选源。确认作品已使用正式引用且不再需要本地预览后,可执行 `promptframe dev stop .`。
|
|
209
|
+
|
|
210
|
+
## 素材引用槽与本地私有预览
|
|
211
|
+
|
|
212
|
+
在 schema 中用 `promptFrameResourceSchema` 声明素材参数,在组件中用 `promptFrameResource` 消费。参数保存 public 路径或 `asset-proxy://` 引用;组件不读取凭据,也不调用平台解析接口。
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { z } from 'zod';
|
|
216
|
+
import { promptFrameResourceSchema } from '@promptframe/contracts';
|
|
217
|
+
export const propsSchema = z.object({
|
|
218
|
+
illustration: promptFrameResourceSchema({
|
|
219
|
+
kinds: ['image'], sources: ['public', 'asset'],
|
|
220
|
+
accept: ['image/png', 'image/jpeg'], maxFileBytes: 16 * 1024 * 1024,
|
|
221
|
+
}).describe('展示图片').default('/example.png'),
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
组件使用 `<Img src={promptFrameResource(props, props.illustration)} />`。本地、Studio 预览和正式渲染使用同一写法;解析由宿主完成。正式渲染缺失、错误类型或越权引用会报错,不自动替换作品内容。
|
|
226
|
+
|
|
227
|
+
新模板已启用 Vite 适配器。自定义旧项目在 `vite.config.ts` 的 plugins 中加入 `promptFramePreviewResources()`(从 `@promptframe/cli/preview` 导入),再运行 `promptframe sync . --apply` 更新预览壳;已有 React 插件等设置继续保留。适配器仅在本机 loopback 开发服务中运行,不导入组件业务代码。
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
promptframe resource set . --prop /illustration --file ./my-selected.png
|
|
231
|
+
promptframe resource status .
|
|
232
|
+
promptframe resource clear .
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
本人明确选择的 PNG、JPEG、WebP 文件最大 16MiB,并受槽声明的 MIME/大小限制。CLI 记录原文件身份和 SHA,不复制原件;每次读取重新核对。选择存入 ignored 的 `.promptframe/local-previews/resources.json`,不会改公开 `preview-props.json`,打包固定排除此目录。选择最多 16 项,24 小时后不再提供读取;`resource clear` 只移除受管选择记录,不删除原始文件。没有后台清理进程。
|
|
236
|
+
|
|
237
|
+
平台素材使用同一选择命令的 `--asset-ref` 和原作品 `--session-id`,不得与 `--file` 同用。项目来自正常 `promptframe init` 写入的无凭据上下文。需要先沿正常 `promptframe login --endpoint <平台API> --with-assets` 授予 `asset.read`;旧组件上传凭据不会自动扩大权限。开发服务复用现有 CLI 凭据,逐次请求当前用户、项目与作品范围内的短时只读表示,核字节 SHA 后向浏览器提供本机地址。凭据、签名 URL 和私有文件路径不会发送给组件。断网、撤权、文件变化或选择过期时,预览明确提示不可用并标明正在展示包内示例;恢复条件后刷新。
|
|
238
|
+
|
|
239
|
+
市场预览的素材选择读取访问者当前作品中的就绪素材,不改变组件包或上传新素材。正式使用时将合法素材引用冻结在 Spec,渲染入口沿原授权、素材存储和预下载合同准备本地表示;签名 URL 不保存到 Spec。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class CliCredentialError extends Error {
|
|
2
|
+
readonly code: 'cli.auth.token_expired';
|
|
3
|
+
constructor(code: 'cli.auth.token_expired');
|
|
4
|
+
}
|
|
5
|
+
export declare function readCliConfiguration(path: string): Record<string, unknown>;
|
|
6
|
+
/** Shared read-only access to the existing credential; never logs or writes it. */
|
|
7
|
+
export declare function cliBearerToken(input: {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
explicit?: string;
|
|
10
|
+
configuration: Record<string, unknown>;
|
|
11
|
+
now?: () => number;
|
|
12
|
+
}): string | undefined;
|
|
13
|
+
//# sourceMappingURL=configuration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../src/configuration.ts"],"names":[],"mappings":"AAEA,qBAAa,kBAAmB,SAAQ,KAAK;IAC/B,QAAQ,CAAC,IAAI,EAAE,wBAAwB;gBAA9B,IAAI,EAAE,wBAAwB;CACpD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM1E;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACjG,GAAG,MAAM,GAAG,SAAS,CAWrB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
export class CliCredentialError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code) {
|
|
5
|
+
super(code);
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function readCliConfiguration(path) {
|
|
10
|
+
if (!existsSync(path))
|
|
11
|
+
return {};
|
|
12
|
+
try {
|
|
13
|
+
const value = JSON.parse(readFileSync(path, 'utf8'));
|
|
14
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Shared read-only access to the existing credential; never logs or writes it. */
|
|
21
|
+
export function cliBearerToken(input) {
|
|
22
|
+
if (input.explicit)
|
|
23
|
+
return input.explicit;
|
|
24
|
+
const credential = input.configuration.credential;
|
|
25
|
+
if (!credential || typeof credential !== 'object' || Array.isArray(credential))
|
|
26
|
+
return undefined;
|
|
27
|
+
const row = credential;
|
|
28
|
+
const normalize = (value) => value.replace(/\/+$/, '');
|
|
29
|
+
if (typeof row.endpoint !== 'string' || normalize(row.endpoint) !== normalize(input.endpoint))
|
|
30
|
+
return undefined;
|
|
31
|
+
if (typeof row.expiresAt === 'string' && Date.parse(row.expiresAt) <= (input.now ?? Date.now)()) {
|
|
32
|
+
throw new CliCredentialError('cli.auth.token_expired');
|
|
33
|
+
}
|
|
34
|
+
return typeof row.tokenSecret === 'string' ? row.tokenSecret : undefined;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../src/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACtB;IAArB,YAAqB,IAA8B;QAAI,KAAK,CAAC,IAAI,CAAC,CAAC;QAA9C,SAAI,GAAJ,IAAI,CAA0B;IAAiB,CAAC;CACtE;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9D,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAgC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7G,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACxB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,KAE9B;IACC,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;IAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAC;IACjG,MAAM,GAAG,GAAG,UAAqC,CAAC;IAClD,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAChH,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QAChG,MAAM,IAAI,kBAAkB,CAAC,wBAAwB,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,15 @@ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync,
|
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
8
8
|
import { inflateRawSync } from 'node:zlib';
|
|
9
|
-
import {
|
|
9
|
+
import { cliBearerToken, readCliConfiguration } from './configuration.js';
|
|
10
|
+
import ignore from 'ignore';
|
|
11
|
+
import { COMPONENT_MANIFEST_SCHEMA_VERSION, COMPONENT_PUBLIC_RESOURCES_CONTRACT_VERSION, COMPONENT_REF_VERSION, COMPONENT_STANDARD_SOURCE_HASH, COMPONENT_STANDARD_VERSION, PROMPTFRAME_AUTHORING_STANDARD_RELEASE, PROMPTFRAME_PUBLIC_RESOURCE_LIMITS, PROMPTFRAME_PUBLIC_RESOURCE_POLICY, PROMPTFRAME_PUBLIC_SECURITY_POLICY, PROMPTFRAME_PUBLIC_SECURITY_POLICY_DIGEST, PROMPTFRAME_PUBLIC_STANDARD_POLICY, PROMPTFRAME_LAYOUT_POLICY_RULE_IDS, PROMPTFRAME_CONTRACTS_VERSION, PROMPTFRAME_PUBLIC_DEPENDENCY_POLICY, detectPromptFrameUnknownCustomStyleProps, evaluatePromptFrameLayoutPolicy, evaluatePromptFrameDependencyPolicy, collectPromptFrameResourceReferences, authoringUploadTargetSchema, parseComponentManifest, } from '@promptframe/contracts';
|
|
10
12
|
import { evaluatePromptFrameSecurityPolicySource, } from '@promptframe/contracts/security-evaluator';
|
|
11
13
|
import { evaluatePromptFrameSchemaSource, } from '@promptframe/contracts/schema-evaluator';
|
|
12
14
|
import { applyPackageChanges, buildFreshnessDecision, computePackageChanges, computePackageFreshnessDiagnostics, computeScaffoldFreshnessDiagnostics, resolveLocalPreviewScript, } from './lifecycle.js';
|
|
13
15
|
import { evaluateLocalReusability, reusabilityDiagnostics, } from './reusability.js';
|
|
14
16
|
import { assertPreviewEnvelope, writeLocalPreviewReport, } from './preview-report.js';
|
|
17
|
+
import { capturePreviewResourceFile, clearPreviewResourceDraft, previewResourcePropPath, readPreviewResourceDraft, writePreviewResourceSelection } from './preview-resource-draft.js';
|
|
15
18
|
const command = process.argv[2] ?? 'help';
|
|
16
19
|
const args = process.argv.slice(3);
|
|
17
20
|
const REQUIRED_COMPONENT_FILES = PROMPTFRAME_PUBLIC_STANDARD_POLICY.requiredFiles;
|
|
@@ -96,6 +99,9 @@ async function run(name, argv) {
|
|
|
96
99
|
case 'preview':
|
|
97
100
|
preview(argv);
|
|
98
101
|
break;
|
|
102
|
+
case 'resource':
|
|
103
|
+
resourceCommand(argv);
|
|
104
|
+
break;
|
|
99
105
|
case 'dev':
|
|
100
106
|
await dev(argv);
|
|
101
107
|
break;
|
|
@@ -383,7 +389,7 @@ function sync(argv) {
|
|
|
383
389
|
const nextFiles = [
|
|
384
390
|
{
|
|
385
391
|
path: GENERATED_PREVIEW_ROOT_PATH,
|
|
386
|
-
contents: renderPromptFrameThinPreviewRoot(),
|
|
392
|
+
contents: renderPromptFrameThinPreviewRoot(hasResourcePreviewAdapter(dir)),
|
|
387
393
|
},
|
|
388
394
|
{
|
|
389
395
|
path: DEV_PUBLIC_RESOURCES_MODULE_PATH,
|
|
@@ -541,6 +547,59 @@ async function dev(argv) {
|
|
|
541
547
|
return;
|
|
542
548
|
await runDevServer(dir, devCommand);
|
|
543
549
|
}
|
|
550
|
+
function resourceCommand(argv) {
|
|
551
|
+
const [action, directory] = positionalArgs(argv), root = resolve(directory ?? '.');
|
|
552
|
+
if (action === 'clear') {
|
|
553
|
+
clearPreviewResourceDraft(root);
|
|
554
|
+
printJson({ command: 'resource.clear', status: 'cleared', originalFilesDeleted: 0 });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (action === 'status') {
|
|
558
|
+
const draft = readPreviewResourceDraft(root);
|
|
559
|
+
printJson({ command: 'resource.status', selectedCount: draft?.selections.length ?? 0,
|
|
560
|
+
updatedAt: draft?.updatedAt, credentialsIncluded: false });
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (action !== 'set')
|
|
564
|
+
fail('Use resource set|status|clear <component-directory>.', 'preview.resource.command_invalid', 2);
|
|
565
|
+
const prop = valueAfter(argv, '--prop'), file = valueAfter(argv, '--file'), reference = valueAfter(argv, '--asset-ref');
|
|
566
|
+
if (!prop || !!file === !!reference)
|
|
567
|
+
fail('Select --prop /field with exactly one --file or --asset-ref.', 'preview.resource.selection_invalid', 2);
|
|
568
|
+
const manifest = validateComponentDirectory(root), envelope = readPreviewProps(root);
|
|
569
|
+
const facts = evaluatePromptFrameSchemaSource(readFileSync(join(root, manifest.entry.propsSchemaPath), 'utf8'));
|
|
570
|
+
const props = structuredClone(envelope.props ?? {}), keys = previewResourcePropPath(prop);
|
|
571
|
+
let object = props;
|
|
572
|
+
for (const key of keys.slice(0, -1)) {
|
|
573
|
+
if (!object || typeof object !== 'object' || !Object.hasOwn(object, key))
|
|
574
|
+
fail('Resource field is missing.', 'preview.resource.prop_missing');
|
|
575
|
+
object = object[key];
|
|
576
|
+
}
|
|
577
|
+
if (!object || typeof object !== 'object')
|
|
578
|
+
fail('Resource field is missing.', 'preview.resource.prop_missing');
|
|
579
|
+
object[keys.at(-1)] = reference ?? '/local-preview.png';
|
|
580
|
+
const slot = collectPromptFrameResourceReferences({ type: 'object', properties: facts.properties }, props)
|
|
581
|
+
.find(row => JSON.stringify(row.path) === JSON.stringify(keys));
|
|
582
|
+
if (facts.status !== 'resolved' || !slot)
|
|
583
|
+
fail('Use promptFrameResourceSchema for this field.', 'preview.resource.slot_missing');
|
|
584
|
+
const selectedFile = file ? capturePreviewResourceFile(file) : undefined;
|
|
585
|
+
if (selectedFile && (!slot.slot.sources.includes('public') || !slot.slot.kinds.includes('image')
|
|
586
|
+
|| slot.slot.maxFileBytes !== undefined && selectedFile.size > slot.slot.maxFileBytes
|
|
587
|
+
|| slot.slot.accept?.length && !slot.slot.accept.some(type => type === '*/*' || type === selectedFile.contentType
|
|
588
|
+
|| type.endsWith('/*') && selectedFile.contentType.startsWith(type.slice(0, -1)))))
|
|
589
|
+
fail('Selected file does not match the declared slot.', 'preview.resource.file_rejected');
|
|
590
|
+
const sessionId = valueAfter(argv, '--session-id');
|
|
591
|
+
if (reference && !sessionId)
|
|
592
|
+
fail('A platform asset needs its original --session-id.', 'preview.resource.scope_required', 2);
|
|
593
|
+
const ignored = join(root, '.gitignore'), oldIgnore = existsSync(ignored) ? readFileSync(ignored, 'utf8') : '';
|
|
594
|
+
if (!ignore().add(oldIgnore).ignores('.promptframe/local-previews/resources.json'))
|
|
595
|
+
writeFileSync(ignored, oldIgnore.replace(/\n?$/, '\n') + '.promptframe/\n');
|
|
596
|
+
writePreviewResourceSelection(root, { prop, ...(selectedFile ? { file: selectedFile } : { reference, sessionId }) });
|
|
597
|
+
const config = existsSync(join(root, 'vite.config.ts')) ? readFileSync(join(root, 'vite.config.ts'), 'utf8') : '';
|
|
598
|
+
printJson({ command: 'resource.set', status: 'selected', prop, source: selectedFile ? 'local_file' : 'platform_asset',
|
|
599
|
+
draftOnly: true, sourcePreviewPropsChanged: false, platformWrites: 0,
|
|
600
|
+
previewAdapter: config.includes('@promptframe/cli/preview') ? 'configured' : 'setup_required',
|
|
601
|
+
nextAction: 'Refresh the local preview. Existing custom Vite shells need promptFramePreviewResources().' });
|
|
602
|
+
}
|
|
544
603
|
async function devWorkspace(root, argv) {
|
|
545
604
|
const studioOrigin = valueAfter(argv, '--studio-origin')
|
|
546
605
|
?? fail('Workspace dev requires --studio-origin <exact-origin>.', 'dev.workspace.origin_required', 2);
|
|
@@ -1685,6 +1744,7 @@ function buildVersionNotesHeaders(versionNotes) {
|
|
|
1685
1744
|
return versionNotes ? { 'x-promptframe-version-notes': versionNotes } : {};
|
|
1686
1745
|
}
|
|
1687
1746
|
const POSITIONAL_VALUE_FLAGS = new Set([
|
|
1747
|
+
'--prop', '--file', '--asset-ref',
|
|
1688
1748
|
'--allowed-upload-target',
|
|
1689
1749
|
'--auth-permissions',
|
|
1690
1750
|
'--auth-roles',
|
|
@@ -1874,7 +1934,8 @@ async function loginWithDeviceCode(endpoint, argv) {
|
|
|
1874
1934
|
contractVersion: CLI_AUTH_CONTRACT_VERSION,
|
|
1875
1935
|
endpoint,
|
|
1876
1936
|
clientName: `PromptFrame CLI on ${os.hostname()}`,
|
|
1877
|
-
requestedScopes: DEFAULT_CLI_LOGIN_SCOPES,
|
|
1937
|
+
requestedScopes: [...new Set([...(valuesAfter(argv, '--scope').length ? valuesAfter(argv, '--scope') : DEFAULT_CLI_LOGIN_SCOPES),
|
|
1938
|
+
...(hasFlag(argv, '--with-assets') ? ['asset.read'] : [])])],
|
|
1878
1939
|
}),
|
|
1879
1940
|
}, 'login.device_start.failed');
|
|
1880
1941
|
const deviceCode = requiredPayloadString(startPayload, 'deviceCode', 'login.device_start.invalid');
|
|
@@ -2015,6 +2076,7 @@ async function initProjectContext(argv) {
|
|
|
2015
2076
|
projectId,
|
|
2016
2077
|
projectNamespace: valueAfter(argv, '--project-namespace')
|
|
2017
2078
|
?? stringValue(project.projectNamespace)
|
|
2079
|
+
?? stringValue(project.namespace)
|
|
2018
2080
|
?? normalizeProjectNamespace(projectId),
|
|
2019
2081
|
defaultUploadTarget: valueAfter(argv, '--target')
|
|
2020
2082
|
?? valueAfter(argv, '--upload-target')
|
|
@@ -3018,18 +3080,12 @@ function resolveBearerToken(endpoint, argv) {
|
|
|
3018
3080
|
?? process.env.PROMPTFRAME_CI_TOKEN
|
|
3019
3081
|
?? process.env.PROMPTFRAME_CLI_TOKEN
|
|
3020
3082
|
?? process.env.PROMPTFRAME_AUTH_TOKEN;
|
|
3021
|
-
|
|
3022
|
-
return explicit;
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
return
|
|
3026
|
-
if (normalizeEndpoint(stringValue(credential.endpoint) ?? '') !== normalizeEndpoint(endpoint))
|
|
3027
|
-
return undefined;
|
|
3028
|
-
const expiresAt = stringValue(credential.expiresAt);
|
|
3029
|
-
if (expiresAt && Date.parse(expiresAt) <= Date.now()) {
|
|
3030
|
-
fail('Stored PromptFrame CLI credential is expired. Run promptframe login again.', 'cli.auth.token_expired', 2);
|
|
3083
|
+
try {
|
|
3084
|
+
return cliBearerToken({ endpoint, explicit, configuration: readConfig(argv) });
|
|
3085
|
+
}
|
|
3086
|
+
catch {
|
|
3087
|
+
return fail('Stored PromptFrame CLI credential is expired. Run promptframe login again.', 'cli.auth.token_expired', 2);
|
|
3031
3088
|
}
|
|
3032
|
-
return stringValue(credential.tokenSecret);
|
|
3033
3089
|
}
|
|
3034
3090
|
function describeAuthForEndpoint(endpoint, argv) {
|
|
3035
3091
|
const credential = asRecord(readConfig(argv).credential);
|
|
@@ -3168,15 +3224,7 @@ function fileCredentialWarning() {
|
|
|
3168
3224
|
};
|
|
3169
3225
|
}
|
|
3170
3226
|
function readConfig(argv) {
|
|
3171
|
-
|
|
3172
|
-
if (!existsSync(path))
|
|
3173
|
-
return {};
|
|
3174
|
-
try {
|
|
3175
|
-
return JSON.parse(readFileSync(path, 'utf8'));
|
|
3176
|
-
}
|
|
3177
|
-
catch {
|
|
3178
|
-
return {};
|
|
3179
|
-
}
|
|
3227
|
+
return readCliConfiguration(resolveConfigPath(argv));
|
|
3180
3228
|
}
|
|
3181
3229
|
function writeConfig(argv, config) {
|
|
3182
3230
|
const path = resolveConfigPath(argv);
|
|
@@ -3315,6 +3363,15 @@ function validatePreviewProps(dir, manifest) {
|
|
|
3315
3363
|
assertStaticSchemaResolved(schemaFacts, manifest.entry.propsSchemaPath);
|
|
3316
3364
|
assertPreviewPropsMatchStaticSchema(manifest, preview, schemaFacts);
|
|
3317
3365
|
assertStaticPropsHaveDescriptions(schemaFacts, manifest.entry.propsSchemaPath);
|
|
3366
|
+
let references;
|
|
3367
|
+
try {
|
|
3368
|
+
references = collectPromptFrameResourceReferences({ type: 'object', properties: schemaFacts.properties }, preview.props ?? {});
|
|
3369
|
+
}
|
|
3370
|
+
catch {
|
|
3371
|
+
fail('Public preview resource fields must use valid example paths.', 'component_standard.preview.resource_invalid');
|
|
3372
|
+
}
|
|
3373
|
+
if (references.some(row => row.source !== 'public'))
|
|
3374
|
+
fail('Private resource selections belong in resource set drafts; public preview props must use package examples.', 'component_standard.preview.private_resource_reference');
|
|
3318
3375
|
}
|
|
3319
3376
|
function assertPreviewDurationMatchesManifest(preview, manifest) {
|
|
3320
3377
|
const durationFrames = Number(preview.durationFrames);
|
|
@@ -3729,8 +3786,8 @@ function classifyPreviewShell(source) {
|
|
|
3729
3786
|
}
|
|
3730
3787
|
return 'custom_or_unknown';
|
|
3731
3788
|
}
|
|
3732
|
-
function renderPromptFrameThinPreviewRoot() {
|
|
3733
|
-
|
|
3789
|
+
function renderPromptFrameThinPreviewRoot(localResources = false) {
|
|
3790
|
+
const source = `import { Player } from '@remotion/player';
|
|
3734
3791
|
import {
|
|
3735
3792
|
PromptFramePreviewApp,
|
|
3736
3793
|
resolvePromptFramePreviewLocale,
|
|
@@ -3799,6 +3856,17 @@ createRoot(root).render(
|
|
|
3799
3856
|
</StrictMode>,
|
|
3800
3857
|
);
|
|
3801
3858
|
`;
|
|
3859
|
+
if (!localResources)
|
|
3860
|
+
return source;
|
|
3861
|
+
return source
|
|
3862
|
+
.replace("import { propsSchema, type ComponentProps } from './schema';", "import { propsSchema, type ComponentProps } from './schema';\nimport { promptFrameLocalPreview } from 'virtual:promptframe-local-preview';")
|
|
3863
|
+
.replace('const initialPropsParse = propsSchema.safeParse(preview.props ?? {});', 'async function mountPreview() {\nconst localPreview = await promptFrameLocalPreview;\nconst initialPropsParse = propsSchema.safeParse(localPreview.props ?? preview.props ?? {});')
|
|
3864
|
+
.replace(' <StrictMode>\n <PromptFramePreviewApp', ' <StrictMode>\n {localPreview.issues.length > 0 && <p role="alert" style={{ padding: 12 }}>\n 本地预览素材不可用,当前显示包内示例。请核对所选文件、作品与 CLI 素材读取授权后刷新。\n </p>}\n <PromptFramePreviewApp')
|
|
3865
|
+
+ '\n}\nvoid mountPreview();\n';
|
|
3866
|
+
}
|
|
3867
|
+
function hasResourcePreviewAdapter(dir) {
|
|
3868
|
+
const path = join(dir, 'vite.config.ts');
|
|
3869
|
+
return existsSync(path) && readFileSync(path, 'utf8').includes('@promptframe/cli/preview');
|
|
3802
3870
|
}
|
|
3803
3871
|
function assertPublicResourcesAccepted(report) {
|
|
3804
3872
|
const firstError = report.diagnostics.find((item) => item.severity === 'error');
|
|
@@ -3962,13 +4030,43 @@ function decodeZipEntry(data, method) {
|
|
|
3962
4030
|
}
|
|
3963
4031
|
function collectPackageFiles(root) {
|
|
3964
4032
|
const files = [];
|
|
3965
|
-
|
|
4033
|
+
const manifest = parseComponentManifest(normalizeLegacyManifest(JSON.parse(readFileSync(join(root, 'manifest.json'), 'utf8'))));
|
|
4034
|
+
const requiredPaths = new Set([
|
|
4035
|
+
...REQUIRED_COMPONENT_FILES,
|
|
4036
|
+
manifest.entry.sourcePath,
|
|
4037
|
+
manifest.entry.propsSchemaPath,
|
|
4038
|
+
]);
|
|
4039
|
+
function walk(dir, inherited) {
|
|
4040
|
+
const rules = [...inherited];
|
|
4041
|
+
const ignorePath = join(dir, '.gitignore');
|
|
4042
|
+
if (existsSync(ignorePath)) {
|
|
4043
|
+
if (!lstatSync(ignorePath).isFile()) {
|
|
4044
|
+
fail('.gitignore must be an ordinary file inside the component directory.', 'package.gitignore.invalid');
|
|
4045
|
+
}
|
|
4046
|
+
rules.push({ root: dir, matcher: ignore({ ignorecase: false }).add(readFileSync(ignorePath, 'utf8')) });
|
|
4047
|
+
}
|
|
3966
4048
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3967
4049
|
if (['node_modules', 'dist', '.git', '.component-packages', '.promptframe'].includes(entry.name))
|
|
3968
4050
|
continue;
|
|
3969
4051
|
const full = join(dir, entry.name);
|
|
4052
|
+
const name = relative(root, full).replace(/\\/g, '/');
|
|
4053
|
+
let ignored = false;
|
|
4054
|
+
for (const rule of rules) {
|
|
4055
|
+
const result = rule.matcher.test(`${relative(rule.root, full).replace(/\\/g, '/')}${entry.isDirectory() ? '/' : ''}`);
|
|
4056
|
+
if (result.ignored)
|
|
4057
|
+
ignored = true;
|
|
4058
|
+
else if (result.unignored)
|
|
4059
|
+
ignored = false;
|
|
4060
|
+
}
|
|
4061
|
+
if (ignored) {
|
|
4062
|
+
const containsRequiredPath = [...requiredPaths].some((requiredPath) => requiredPath === name || (entry.isDirectory() && requiredPath.startsWith(`${name}/`)));
|
|
4063
|
+
if (containsRequiredPath || name === 'public' || name.startsWith('public/')) {
|
|
4064
|
+
fail(`.gitignore excludes required component content: ${name}. Remove that rule or explicitly include the required source/resource.`, 'package.required_file.ignored');
|
|
4065
|
+
}
|
|
4066
|
+
continue;
|
|
4067
|
+
}
|
|
3970
4068
|
if (entry.isDirectory()) {
|
|
3971
|
-
walk(full);
|
|
4069
|
+
walk(full, rules);
|
|
3972
4070
|
}
|
|
3973
4071
|
else if (entry.isFile()) {
|
|
3974
4072
|
if (entry.name === LOCKFILE_EVIDENCE_FILE_NAME)
|
|
@@ -3976,13 +4074,13 @@ function collectPackageFiles(root) {
|
|
|
3976
4074
|
if (isPackageManagerLockfile(entry.name))
|
|
3977
4075
|
continue;
|
|
3978
4076
|
files.push({
|
|
3979
|
-
name
|
|
4077
|
+
name,
|
|
3980
4078
|
data: readFileSync(full),
|
|
3981
4079
|
});
|
|
3982
4080
|
}
|
|
3983
4081
|
}
|
|
3984
4082
|
}
|
|
3985
|
-
walk(root);
|
|
4083
|
+
walk(root, []);
|
|
3986
4084
|
const evidence = buildLockfileEvidenceFile(root);
|
|
3987
4085
|
if (evidence)
|
|
3988
4086
|
files.push(evidence);
|
|
@@ -4329,6 +4427,8 @@ Commands:
|
|
|
4329
4427
|
sync <dir> Refresh PromptFrame-generated authoring shell files (--dry-run by default; use --apply)
|
|
4330
4428
|
preview <dir> Validate and print local Remotion preview envelope
|
|
4331
4429
|
--write-local-report Write .promptframe/local-previews/preview-report.json
|
|
4430
|
+
resource set <dir> Select a private local preview resource (--prop /field, --file path OR --asset-ref ref --session-id id)
|
|
4431
|
+
resource status|clear <dir> Inspect or clear private preview selections; original files are retained
|
|
4332
4432
|
dev <dir> Start a local preview or workspace checked-module connection
|
|
4333
4433
|
--studio-origin <origin> Required for a multi-component workspace
|
|
4334
4434
|
--idle-timeout <minutes> Workspace idle exit (default 30; 0 disables)
|
|
@@ -4345,6 +4445,7 @@ Commands:
|
|
|
4345
4445
|
probe <buildId> --level <level> Rerun component layout/security probe
|
|
4346
4446
|
login --endpoint <url> Start browser login code flow and store a CLI token
|
|
4347
4447
|
--token <token> Verify and store an already issued CLI/CI token
|
|
4448
|
+
--with-assets Explicitly request asset.read in the normal browser authorization
|
|
4348
4449
|
whoami Show the current PromptFrame CLI identity
|
|
4349
4450
|
logout Revoke the current CLI token and clear local config
|
|
4350
4451
|
discovery Fetch platform endpoint and self-service capabilities
|