@promptframe/cli 0.1.66 → 0.1.68

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 CHANGED
@@ -8,7 +8,7 @@ PromptFrame component authoring CLI.
8
8
 
9
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.
10
10
 
11
- Current source baseline is `@promptframe/cli@0.1.66`, `@promptframe/contracts@0.1.34`, `@promptframe/component-kit@0.1.28`, and `create-promptframe-component@0.1.56`. 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.
11
+ Current source baseline is `@promptframe/cli@0.1.68`, `@promptframe/contracts@0.1.36`, `@promptframe/component-kit@0.1.30`, and `create-promptframe-component@0.1.58`. 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.
12
12
 
13
13
  ```bash
14
14
  npm install -D @promptframe/cli
@@ -206,3 +206,34 @@ promptframe dev stop .
206
206
  关闭开发面板不停止连接;切换账号或作品、关闭页面会断开。刷新页面后重新粘贴连接JSON。停止或替换仅操作本工作区的实际实例;无法确认实例退出时命令报错,不自动强制终止进程。
207
207
 
208
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,13 +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 { cliBearerToken, readCliConfiguration } from './configuration.js';
9
10
  import ignore from 'ignore';
10
- 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, authoringUploadTargetSchema, parseComponentManifest, } from '@promptframe/contracts';
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';
11
12
  import { evaluatePromptFrameSecurityPolicySource, } from '@promptframe/contracts/security-evaluator';
12
13
  import { evaluatePromptFrameSchemaSource, } from '@promptframe/contracts/schema-evaluator';
13
14
  import { applyPackageChanges, buildFreshnessDecision, computePackageChanges, computePackageFreshnessDiagnostics, computeScaffoldFreshnessDiagnostics, resolveLocalPreviewScript, } from './lifecycle.js';
14
15
  import { evaluateLocalReusability, reusabilityDiagnostics, } from './reusability.js';
15
16
  import { assertPreviewEnvelope, writeLocalPreviewReport, } from './preview-report.js';
17
+ import { capturePreviewResourceFile, clearPreviewResourceDraft, previewResourcePropPath, readPreviewResourceDraft, writePreviewResourceSelection } from './preview-resource-draft.js';
16
18
  const command = process.argv[2] ?? 'help';
17
19
  const args = process.argv.slice(3);
18
20
  const REQUIRED_COMPONENT_FILES = PROMPTFRAME_PUBLIC_STANDARD_POLICY.requiredFiles;
@@ -97,6 +99,9 @@ async function run(name, argv) {
97
99
  case 'preview':
98
100
  preview(argv);
99
101
  break;
102
+ case 'resource':
103
+ resourceCommand(argv);
104
+ break;
100
105
  case 'dev':
101
106
  await dev(argv);
102
107
  break;
@@ -384,7 +389,7 @@ function sync(argv) {
384
389
  const nextFiles = [
385
390
  {
386
391
  path: GENERATED_PREVIEW_ROOT_PATH,
387
- contents: renderPromptFrameThinPreviewRoot(),
392
+ contents: renderPromptFrameThinPreviewRoot(hasResourcePreviewAdapter(dir)),
388
393
  },
389
394
  {
390
395
  path: DEV_PUBLIC_RESOURCES_MODULE_PATH,
@@ -542,6 +547,59 @@ async function dev(argv) {
542
547
  return;
543
548
  await runDevServer(dir, devCommand);
544
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
+ }
545
603
  async function devWorkspace(root, argv) {
546
604
  const studioOrigin = valueAfter(argv, '--studio-origin')
547
605
  ?? fail('Workspace dev requires --studio-origin <exact-origin>.', 'dev.workspace.origin_required', 2);
@@ -1686,6 +1744,7 @@ function buildVersionNotesHeaders(versionNotes) {
1686
1744
  return versionNotes ? { 'x-promptframe-version-notes': versionNotes } : {};
1687
1745
  }
1688
1746
  const POSITIONAL_VALUE_FLAGS = new Set([
1747
+ '--prop', '--file', '--asset-ref',
1689
1748
  '--allowed-upload-target',
1690
1749
  '--auth-permissions',
1691
1750
  '--auth-roles',
@@ -1875,7 +1934,8 @@ async function loginWithDeviceCode(endpoint, argv) {
1875
1934
  contractVersion: CLI_AUTH_CONTRACT_VERSION,
1876
1935
  endpoint,
1877
1936
  clientName: `PromptFrame CLI on ${os.hostname()}`,
1878
- 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'] : [])])],
1879
1939
  }),
1880
1940
  }, 'login.device_start.failed');
1881
1941
  const deviceCode = requiredPayloadString(startPayload, 'deviceCode', 'login.device_start.invalid');
@@ -3020,18 +3080,12 @@ function resolveBearerToken(endpoint, argv) {
3020
3080
  ?? process.env.PROMPTFRAME_CI_TOKEN
3021
3081
  ?? process.env.PROMPTFRAME_CLI_TOKEN
3022
3082
  ?? process.env.PROMPTFRAME_AUTH_TOKEN;
3023
- if (explicit)
3024
- return explicit;
3025
- const credential = asRecord(readConfig(argv).credential);
3026
- if (!credential)
3027
- return undefined;
3028
- if (normalizeEndpoint(stringValue(credential.endpoint) ?? '') !== normalizeEndpoint(endpoint))
3029
- return undefined;
3030
- const expiresAt = stringValue(credential.expiresAt);
3031
- if (expiresAt && Date.parse(expiresAt) <= Date.now()) {
3032
- 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);
3033
3088
  }
3034
- return stringValue(credential.tokenSecret);
3035
3089
  }
3036
3090
  function describeAuthForEndpoint(endpoint, argv) {
3037
3091
  const credential = asRecord(readConfig(argv).credential);
@@ -3170,15 +3224,7 @@ function fileCredentialWarning() {
3170
3224
  };
3171
3225
  }
3172
3226
  function readConfig(argv) {
3173
- const path = resolveConfigPath(argv);
3174
- if (!existsSync(path))
3175
- return {};
3176
- try {
3177
- return JSON.parse(readFileSync(path, 'utf8'));
3178
- }
3179
- catch {
3180
- return {};
3181
- }
3227
+ return readCliConfiguration(resolveConfigPath(argv));
3182
3228
  }
3183
3229
  function writeConfig(argv, config) {
3184
3230
  const path = resolveConfigPath(argv);
@@ -3317,6 +3363,15 @@ function validatePreviewProps(dir, manifest) {
3317
3363
  assertStaticSchemaResolved(schemaFacts, manifest.entry.propsSchemaPath);
3318
3364
  assertPreviewPropsMatchStaticSchema(manifest, preview, schemaFacts);
3319
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');
3320
3375
  }
3321
3376
  function assertPreviewDurationMatchesManifest(preview, manifest) {
3322
3377
  const durationFrames = Number(preview.durationFrames);
@@ -3731,8 +3786,8 @@ function classifyPreviewShell(source) {
3731
3786
  }
3732
3787
  return 'custom_or_unknown';
3733
3788
  }
3734
- function renderPromptFrameThinPreviewRoot() {
3735
- return `import { Player } from '@remotion/player';
3789
+ function renderPromptFrameThinPreviewRoot(localResources = false) {
3790
+ const source = `import { Player } from '@remotion/player';
3736
3791
  import {
3737
3792
  PromptFramePreviewApp,
3738
3793
  resolvePromptFramePreviewLocale,
@@ -3801,6 +3856,17 @@ createRoot(root).render(
3801
3856
  </StrictMode>,
3802
3857
  );
3803
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');
3804
3870
  }
3805
3871
  function assertPublicResourcesAccepted(report) {
3806
3872
  const firstError = report.diagnostics.find((item) => item.severity === 'error');
@@ -4361,6 +4427,8 @@ Commands:
4361
4427
  sync <dir> Refresh PromptFrame-generated authoring shell files (--dry-run by default; use --apply)
4362
4428
  preview <dir> Validate and print local Remotion preview envelope
4363
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
4364
4432
  dev <dir> Start a local preview or workspace checked-module connection
4365
4433
  --studio-origin <origin> Required for a multi-component workspace
4366
4434
  --idle-timeout <minutes> Workspace idle exit (default 30; 0 disables)
@@ -4377,6 +4445,7 @@ Commands:
4377
4445
  probe <buildId> --level <level> Rerun component layout/security probe
4378
4446
  login --endpoint <url> Start browser login code flow and store a CLI token
4379
4447
  --token <token> Verify and store an already issued CLI/CI token
4448
+ --with-assets Explicitly request asset.read in the normal browser authorization
4380
4449
  whoami Show the current PromptFrame CLI identity
4381
4450
  logout Revoke the current CLI token and clear local config
4382
4451
  discovery Fetch platform endpoint and self-service capabilities