@givolt/i3-composer-agent 2.3.0 → 2.3.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.
Files changed (122) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude/commands/i3-prototype.md +5 -7
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/.claude-plugin/plugin.json +1 -1
  5. package/.codex-plugin/plugin.json +1 -1
  6. package/.gemini/commands/i3-prototype.toml +5 -7
  7. package/README.md +12 -7
  8. package/bin/i3-composer-agent.cjs +33 -2
  9. package/bin/install.cjs +14 -0
  10. package/bin/native-migration.cjs +27 -3
  11. package/commands/i3-prototype.toml +5 -7
  12. package/docs/codebuddy-setup.md +65 -2
  13. package/docs/codex-setup.md +4 -0
  14. package/docs/other-hosts.md +1 -1
  15. package/docs/ui-prototype-native/MAIN-SESSION-REPORT.md +66 -0
  16. package/docs/ui-prototype-native/USAGE.md +91 -0
  17. package/docs/workbuddy-setup.md +4 -0
  18. package/evals/cases/i3-frontend-ui-engineering.json +16 -0
  19. package/evals/cases/i3-ui-prototype.json +109 -0
  20. package/evals/cases/i3-using-agent-skills.json +28 -0
  21. package/evals/fixtures/i3-ui-prototype/admin-list/assets/reference.css +3 -0
  22. package/evals/fixtures/i3-ui-prototype/admin-list/assets/reference.js +8 -0
  23. package/evals/fixtures/i3-ui-prototype/admin-list/capability-definition.json +67 -0
  24. package/evals/fixtures/i3-ui-prototype/admin-list/index.html +89 -0
  25. package/evals/fixtures/i3-ui-prototype/admin-list/readme.md +52 -0
  26. package/evals/fixtures/i3-ui-prototype/admin-list/template.json +8 -0
  27. package/evals/zh-CN/cases/i3-frontend-ui-engineering.json +17 -0
  28. package/evals/zh-CN/cases/i3-ui-prototype.json +115 -0
  29. package/evals/zh-CN/cases/i3-using-agent-skills.json +30 -0
  30. package/package.json +35 -1
  31. package/plugin.json +1 -1
  32. package/scripts/design-provider-discovery-test.js +190 -0
  33. package/scripts/design-provider-test.js +75 -0
  34. package/scripts/enterprise-template-test.js +67 -1
  35. package/scripts/lib/prototype-fixture.cjs +17 -0
  36. package/scripts/prototype-locations-test.js +58 -0
  37. package/scripts/ui-prototype-content-test.js +123 -0
  38. package/scripts/ui-prototype-dependencies-test.js +213 -0
  39. package/scripts/ui-prototype-devtools-test.js +53 -0
  40. package/scripts/ui-prototype-native-test.js +504 -0
  41. package/scripts/ui-prototype-template-test.js +261 -0
  42. package/scripts/ui-prototype-test.js +10 -0
  43. package/skills/i3-ui-prototype/SKILL.md +142 -23
  44. package/skills/i3-ui-prototype/assets/capabilities/management.json +294 -0
  45. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/acceptance.json +51 -0
  46. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/component-map.json +13 -0
  47. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/index.html +11 -0
  48. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/package-lock.json +2298 -0
  49. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/package.json +22 -0
  50. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/public/prototype-config.json +6 -0
  51. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/App.vue +136 -0
  52. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/components/DeviceEditor.vue +111 -0
  53. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/config.ts +19 -0
  54. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/content.ts +28 -0
  55. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/domain/devices.ts +50 -0
  56. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/domain/theme.ts +25 -0
  57. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/main.ts +18 -0
  58. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/style.css +50 -0
  59. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/src/useDevices.ts +66 -0
  60. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/tests/devices.test.ts +44 -0
  61. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/tests/theme.test.ts +26 -0
  62. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/tsconfig.json +15 -0
  63. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/legacy-starter/vite.config.ts +4 -0
  64. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/profile.json +174 -0
  65. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/readme.md +61 -0
  66. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/index.html +11 -0
  67. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/package-lock.json +2298 -0
  68. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/package.json +22 -0
  69. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/public/prototype-config.json +21 -0
  70. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/App.vue +51 -0
  71. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/config.ts +18 -0
  72. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/domain/theme.ts +25 -0
  73. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/main.ts +18 -0
  74. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/src/style.css +13 -0
  75. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/tests/theme.test.ts +26 -0
  76. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/tsconfig.json +15 -0
  77. package/skills/i3-ui-prototype/assets/frameworks/vue-antdv-next/starter/vite.config.ts +4 -0
  78. package/skills/i3-ui-prototype/assets/optional-skills/host-targets.json +737 -0
  79. package/skills/i3-ui-prototype/assets/optional-skills/sources.json +27 -0
  80. package/skills/i3-ui-prototype/assets/templates/catalog.js +149 -0
  81. package/skills/i3-ui-prototype/assets/templates/catalog.json +3 -2
  82. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/capability-definition.json +294 -0
  83. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/design-system.md +3 -3
  84. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/index.html +507 -2
  85. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/manifest.json +1 -1
  86. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/offline-build.json +265 -0
  87. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/offline.js +8888 -0
  88. package/skills/i3-ui-prototype/assets/templates/enterprise-admin/template.json +9 -0
  89. package/skills/i3-ui-prototype/assets/templates/gallery/index.html +6 -6
  90. package/skills/i3-ui-prototype/assets/templates/index.html +3 -0
  91. package/skills/i3-ui-prototype/assets/templates/serve-gallery.cjs +7 -0
  92. package/skills/i3-ui-prototype/references/artifact-contract.md +25 -0
  93. package/skills/i3-ui-prototype/references/content-template.md +76 -0
  94. package/skills/i3-ui-prototype/references/enterprise-admin.md +9 -3
  95. package/skills/i3-ui-prototype/references/entry-contract.md +71 -0
  96. package/skills/i3-ui-prototype/references/expert-adapter.md +39 -5
  97. package/skills/i3-ui-prototype/references/framework-contract.md +76 -0
  98. package/skills/i3-ui-prototype/references/interaction-acceptance.md +165 -0
  99. package/skills/i3-ui-prototype/references/legacy-native.md +58 -0
  100. package/skills/i3-ui-prototype/references/optional-skills.md +59 -0
  101. package/skills/i3-ui-prototype/references/template-adapters.md +1 -1
  102. package/skills/i3-ui-prototype/references/workflow-contract.md +58 -4
  103. package/skills/i3-ui-prototype/scripts/adapters/easel-html.cjs +3 -3
  104. package/skills/i3-ui-prototype/scripts/content-template.cjs +98 -0
  105. package/skills/i3-ui-prototype/scripts/design-provider-discovery.cjs +128 -0
  106. package/skills/i3-ui-prototype/scripts/enterprise-template.cjs +30 -14
  107. package/skills/i3-ui-prototype/scripts/manage-baseline.cjs +1 -1
  108. package/skills/i3-ui-prototype/scripts/native-browser.cjs +785 -0
  109. package/skills/i3-ui-prototype/scripts/native-build.cjs +91 -0
  110. package/skills/i3-ui-prototype/scripts/native-delivery.cjs +91 -0
  111. package/skills/i3-ui-prototype/scripts/native-devtools.cjs +91 -0
  112. package/skills/i3-ui-prototype/scripts/native-files.cjs +35 -0
  113. package/skills/i3-ui-prototype/scripts/native-pilot-lib.cjs +244 -0
  114. package/skills/i3-ui-prototype/scripts/native-pilot.cjs +34 -0
  115. package/skills/i3-ui-prototype/scripts/optional-skill-install.cjs +203 -0
  116. package/skills/i3-ui-prototype/scripts/prototype-dependencies.cjs +81 -0
  117. package/skills/i3-ui-prototype/scripts/prototype-lib.cjs +50 -26
  118. package/skills/i3-ui-prototype/scripts/prototype-locations.cjs +65 -0
  119. package/skills/i3-ui-prototype/scripts/sync-template.cjs +19 -0
  120. package/skills/i3-ui-prototype/scripts/template-contract.cjs +398 -0
  121. package/skills/i3-ui-prototype/scripts/validate-result.cjs +117 -0
  122. package/skills/i3-using-agent-skills/SKILL.md +2 -5
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const os = require('node:os');
5
+ const { spawnSync } = require('node:child_process');
6
+
7
+ const PROVIDER = 'ui-ux-pro-max';
8
+ const MARKETPLACE = 'ui-ux-pro-max-skill';
9
+ const json = file => JSON.parse(fs.readFileSync(file, 'utf8'));
10
+ const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
11
+ const safeSegment = value => typeof value === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._+-]*$/.test(value) && !value.includes('..');
12
+ function ensure(condition, message) { if (!condition) throw new Error(message); }
13
+ function canonical(file) { return fs.existsSync(file) ? fs.realpathSync(file) : path.resolve(file); }
14
+
15
+ function pluginRoot(installation, manifests, fallback) {
16
+ ensure(typeof installation.installPath === 'string' && path.isAbsolute(installation.installPath), 'Plugin installPath must be absolute');
17
+ ensure(safeSegment(installation.version), 'Installed plugin version is invalid');
18
+ const root = installation.installPath;
19
+ const manifestFile = manifests.map(name => path.join(root, name)).find(file => fs.existsSync(file));
20
+ ensure(manifestFile, 'Installed plugin manifest is missing');
21
+ const manifest = json(manifestFile);
22
+ ensure(manifest.name === PROVIDER && manifest.version === installation.version, 'Installed plugin manifest name or version does not match its registration');
23
+ const declared = manifest.skills === undefined ? fallback : manifest.skills;
24
+ ensure(typeof declared === 'string' && declared && !path.isAbsolute(declared) && !declared.includes('\\'), 'Plugin skills declaration is missing or invalid');
25
+ const folder = path.resolve(root, declared), relative = path.relative(path.resolve(root), folder);
26
+ ensure(relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative), 'Plugin skills declaration escapes its root');
27
+ if (fs.existsSync(folder)) {
28
+ const realRelative = path.relative(canonical(root), canonical(folder));
29
+ ensure(realRelative !== '..' && !realRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(realRelative), 'Plugin skills path escapes its root');
30
+ }
31
+ const skill = path.join(folder, PROVIDER);
32
+ ensure(fs.existsSync(path.join(skill, 'SKILL.md')), 'Installed plugin skill entrypoint is missing');
33
+ const skillRelative = path.relative(canonical(root), canonical(skill));
34
+ ensure(skillRelative !== '..' && !skillRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(skillRelative), 'Plugin skill path escapes its root');
35
+ return skill;
36
+ }
37
+
38
+ function codexCandidates(config, project, env, add, report) {
39
+ const source = 'codex-plugin';
40
+ try {
41
+ const run = spawnSync('codex', ['plugin', 'list', '--marketplace', MARKETPLACE, '--json'], { cwd: project, env, encoding: 'utf8', timeout: 10000, maxBuffer: 1024 * 1024, shell: false });
42
+ ensure(!run.error && run.status === 0, `Codex plugin discovery failed${run.error?.code ? ` (${run.error.code})` : ''}`);
43
+ const data = JSON.parse(run.stdout);
44
+ ensure(object(data) && Array.isArray(data.installed), 'Codex plugin discovery returned invalid metadata');
45
+ const records = data.installed.filter(record => record?.pluginId === `${PROVIDER}@${MARKETPLACE}`);
46
+ if (!records.length) { report(source, 'missing', 'Current Codex host has no registered UI/UX Pro Max plugin'); return; }
47
+ ensure(records.every(record => record.name === PROVIDER && record.marketplaceName === MARKETPLACE && typeof record.enabled === 'boolean' && typeof record.installed === 'boolean'), 'Codex plugin registration is invalid');
48
+ const active = records.filter(record => record.installed && record.enabled);
49
+ if (!active.length) { report(source, records.some(record => record.installed) ? 'disabled' : 'missing', 'UI/UX Pro Max is not enabled in the current Codex host'); return; }
50
+ if (active.length !== 1) { report(source, 'ambiguous', 'Multiple active Codex UI/UX Pro Max registrations require an explicit provider path'); return; }
51
+ const record = active[0];
52
+ ensure(safeSegment(record.version), 'Codex plugin version is invalid');
53
+ const installPath = path.join(config, 'plugins/cache', MARKETPLACE, PROVIDER, record.version);
54
+ add(pluginRoot({ installPath, version: record.version }, ['.codex-plugin/plugin.json', '.claude-plugin/plugin.json']), source);
55
+ } catch (error) { report(source, 'discovery-error', error.message); }
56
+ }
57
+
58
+ function registeredCandidates(host, config, project, add, report) {
59
+ const source = `${host}-plugin`, registryFile = path.join(config, 'plugins/installed_plugins.json'), settingsFile = path.join(config, 'settings.json');
60
+ try {
61
+ if (!fs.existsSync(registryFile)) { report(source, 'missing', 'Optional host plugin installation registry is absent'); return; }
62
+ const registry = json(registryFile);
63
+ ensure(registry.version === 2 && object(registry.plugins), 'Host plugin installation registry has an unsupported or invalid schema');
64
+ const entries = Object.entries(registry.plugins).filter(([id]) => id.startsWith(`${PROVIDER}@`));
65
+ if (!entries.length) { report(source, 'missing', 'Current host has no registered UI/UX Pro Max plugin'); return; }
66
+ ensure(fs.existsSync(settingsFile), 'Registered plugin enablement metadata is missing');
67
+ const settings = json(settingsFile);
68
+ ensure(object(settings) && object(settings.enabledPlugins), 'Host plugin enablement metadata is invalid');
69
+ const active = [];
70
+ let disabled = false;
71
+ for (const [id, records] of entries) {
72
+ ensure(Array.isArray(records), 'Host plugin registration must contain an installation array');
73
+ if (settings.enabledPlugins[id] !== true) { disabled = true; continue; }
74
+ for (const record of records) {
75
+ ensure(object(record), 'Host plugin installation entry is invalid');
76
+ if (record.scope === 'user') active.push(record);
77
+ else if (record.scope === 'project') {
78
+ ensure(typeof record.projectPath === 'string' && path.isAbsolute(record.projectPath), 'Project-scoped plugin requires an absolute projectPath');
79
+ if (canonical(record.projectPath) === canonical(project)) active.push(record);
80
+ } else throw new Error('Host plugin installation scope is unsupported');
81
+ }
82
+ }
83
+ if (!active.length) { report(source, disabled ? 'disabled' : 'missing', 'No enabled UI/UX Pro Max installation applies to the current project'); return; }
84
+ if (active.length !== 1) { report(source, 'ambiguous', 'Multiple applicable plugin installations require an explicit provider path'); return; }
85
+ const fallback = host === 'workbuddy' || host === 'codebuddy' ? './skills/' : undefined;
86
+ const manifests = host === 'claude' ? ['.claude-plugin/plugin.json'] : ['.codebuddy-plugin/plugin.json', '.claude-plugin/plugin.json'];
87
+ add(pluginRoot(active[0], manifests, fallback), source);
88
+ } catch (error) { report(source, 'discovery-error', error.message); }
89
+ }
90
+
91
+ function discoverProviderCandidates(options = {}) {
92
+ const result = { candidates: [], diagnostics: [] }, seen = new Set();
93
+ const report = (source, availability, reason) => result.diagnostics.push({ source, availability, reason });
94
+ const add = (root, source) => {
95
+ ensure(typeof root === 'string' && root, 'Provider root must be a nonempty path');
96
+ root = path.resolve(root);
97
+ if (!seen.has(root)) { seen.add(root); result.candidates.push({ root, source }); }
98
+ };
99
+ if (options.provider !== undefined) {
100
+ try { add(options.provider, 'explicit'); } catch (error) { report('explicit', 'discovery-error', error.message); }
101
+ return result;
102
+ }
103
+ if (options.providerRoots !== undefined) {
104
+ try {
105
+ ensure(Array.isArray(options.providerRoots), 'Host-exposed providerRoots must be an array');
106
+ for (const root of options.providerRoots) add(root, 'host-exposed');
107
+ } catch (error) { report('host-exposed', 'discovery-error', error.message); }
108
+ return result;
109
+ }
110
+ if (options.discover === false) return { ...result, contextRequired: true };
111
+ const host = options.host === 'claude-code' ? 'claude' : options.host;
112
+ if (!['codex', 'claude', 'workbuddy', 'codebuddy', 'gemini', 'cursor'].includes(host)) return { ...result, contextRequired: true };
113
+ const home = options.home || os.homedir(), env = options.env === undefined ? process.env : options.env, project = path.resolve(options.project || '.');
114
+ const configName = host === 'claude' ? 'CLAUDE_CONFIG_DIR' : `${host.toUpperCase()}_${host === 'codex' ? 'HOME' : 'CONFIG_DIR'}`;
115
+ const config = path.resolve(env[configName] || path.join(home, `.${host}`));
116
+ const projectFolder = host === 'codex' ? '.agents' : host === 'workbuddy' ? '.codebuddy' : `.${host}`;
117
+ const locations = [
118
+ [path.join(project, projectFolder, 'skills', PROVIDER), 'project-skill'],
119
+ ...(host === 'codex' ? [[path.join(home, '.agents/skills', PROVIDER), 'global-skill']] : []),
120
+ [path.join(config, 'skills', PROVIDER), 'global-skill'],
121
+ ];
122
+ for (const [root, source] of locations) if (fs.existsSync(path.join(root, 'SKILL.md'))) add(root, source);
123
+ if (host === 'codex') codexCandidates(config, project, env, add, report);
124
+ else if (['claude', 'workbuddy', 'codebuddy'].includes(host)) registeredCandidates(host, config, project, add, report);
125
+ return result;
126
+ }
127
+
128
+ module.exports = { discoverProviderCandidates };
@@ -29,9 +29,15 @@ function verifyReference(root = assetRoot) {
29
29
  assert.equal(hash(fs.readFileSync(path.join(reference, 'assets-manifest.json'))), origin.sourceManifestDigest, 'Invalid source manifest digest');
30
30
  return origin;
31
31
  }
32
+ function brandRuntimeResource(file, content) {
33
+ if (file !== 'index.html' && !/^src\/(?:[^/.][^/]*\/)*[^/]+\.(?:js|mjs|html)$/.test(file)) return content;
34
+ const text = Buffer.isBuffer(content) ? content.toString('utf8') : content;
35
+ const branded = text.replaceAll('EASEL', 'i3-Composer');
36
+ return branded === text ? content : Buffer.isBuffer(content) ? Buffer.from(branded, 'utf8') : branded;
37
+ }
32
38
  const mapping = {
33
39
  schemaVersion: 1,
34
- scope: 'EASEL 企业后台完整示例;模板审查范围,不代表其他业务已适配',
40
+ scope: 'i3-Composer 企业后台完整示例;模板审查范围,不代表其他业务已适配',
35
41
  requirements: [
36
42
  { id: 'ENT-01', pages: ['#/workbench'], roles: ['演示身份'], actions: ['查看工作台和导航'], scenarios: ['enterprise-navigation'] },
37
43
  { id: 'ENT-02', pages: ['#/models'], roles: ['有模型编辑权限的演示身份'], actions: ['新增模型', '校验和保存草稿'], scenarios: ['enterprise-form'] },
@@ -42,7 +48,7 @@ const mapping = {
42
48
  { id: 'ENT-07', pages: ['#/models'], roles: ['演示身份'], actions: ['查看空、失败等演示反馈'], scenarios: ['enterprise-feedback'] },
43
49
  { id: 'ENT-08', pages: ['app/routes.json'], roles: ['演示身份'], actions: ['打开原版已提供的全部页面入口'], scenarios: ['enterprise-routes'] },
44
50
  ],
45
- openQuestions: ['新项目的模块、字段、状态、权限及接口需按实际需求替换。原有 EASEL 示例不自动成为新业务规格。'],
51
+ openQuestions: ['新项目的模块、字段、状态、权限及接口需按实际需求替换。原有 i3-Composer 示例不自动成为新业务规格。'],
46
52
  };
47
53
  function initialize(directory, { id, title, purpose, templateRoot = assetRoot }) {
48
54
  const origin = verifyReference(templateRoot);
@@ -57,20 +63,30 @@ function initialize(directory, { id, title, purpose, templateRoot = assetRoot })
57
63
  fs.renameSync(path.join(app, stored), path.join(app, original));
58
64
  }
59
65
  const adaptations = [];
60
- for (const file of inventory(app).filter(name => /\.(?:js|mjs)$/.test(name))) {
66
+ for (const file of inventory(app).filter(name => name !== 'index.html' && /\.(?:js|mjs|html)$/.test(name))) {
61
67
  const location = path.join(app, file), old = fs.readFileSync(location, 'utf8');
62
- const next = old.replaceAll('i3-prototype-antdv-', storagePrefix);
68
+ const reasons = [], branded = brandRuntimeResource(file, old);
69
+ if (branded !== old) reasons.push('运行时可见品牌与示例数据统一为 i3-Composer');
70
+ const next = /\.(?:js|mjs)$/.test(file) ? branded.replaceAll('i3-prototype-antdv-', storagePrefix) : branded;
71
+ if (next !== branded) reasons.push('替换本地演示存储前缀,隔离不同原型实例');
63
72
  if (next !== old) {
64
73
  fs.writeFileSync(location, next);
65
- adaptations.push({ file, reason: '仅替换本地演示存储前缀,隔离不同原型实例', source: origin.files[file], generated: hash(next) });
74
+ adaptations.push({ file, reason: reasons.join(';'), source: origin.files[file], generated: hash(next) });
66
75
  }
67
76
  }
68
- const index = path.join(app, 'index.html'), original = fs.readFileSync(index, 'utf8');
69
- assert.ok(original.includes('src="app.js"'), 'Review changed EASEL boot entry before adapting');
70
- const adapted = original.replace('</head>', ' <link rel="stylesheet" href="tokens.css">\n <link rel="stylesheet" href="usability.css">\n</head>')
71
- .replace('src="app.js"', 'src="enterprise-bootstrap.mjs"');
77
+ const index = path.join(app, 'index.html');
78
+ const portable = fs.readFileSync(path.join(templateRoot, 'index.html'), 'utf8');
79
+ assert.ok(portable.includes('src="offline.js"') && !portable.includes('type="module"'), 'Offline enterprise entry required');
80
+ const adapted = portable.replaceAll('href="reference/', 'href="').replaceAll('data-source="reference/', 'data-source="')
81
+ .replace('</head>', ' <link rel="stylesheet" href="tokens.css">\n</head>')
82
+ .replace(/(<script type="application\/json" id="prototype-offline-config">)[\s\S]*?(<\/script>)/,
83
+ (_, start, end) => start + JSON.stringify({ title }).replaceAll('<', '\\u003c') + end);
72
84
  fs.writeFileSync(index, adapted);
73
- adaptations.push({ file: 'index.html', reason: '接入原有设计 token、实例配置和独立滚动适配,完整保留页面内容', source: origin.files['index.html'], generated: hash(adapted) });
85
+ const offline = fs.readFileSync(path.join(templateRoot, 'offline.js'), 'utf8')
86
+ .replaceAll('new URL("reference/', 'new URL("').replaceAll('i3-prototype-antdv-', storagePrefix);
87
+ fs.writeFileSync(path.join(app, 'offline.js'), offline);
88
+ adaptations.push({ file: 'index.html', reason: 'i3-Composer 完整可编辑 HTML 片段、路由和实例配置内嵌;经典脚本支持 file://;保留全部内容与本地样式', source: origin.files['index.html'], generated: hash(adapted) });
89
+ adaptations.push({ file: 'offline.js', reason: '来源模块的离线运行适配,实例存储前缀隔离;原始模块仍保留', generated: hash(offline) });
74
90
  fs.copyFileSync(path.join(templateRoot, 'usability.css'), path.join(app, 'usability.css'));
75
91
  fs.writeFileSync(path.join(app, 'enterprise-bootstrap.mjs'), "const response = await fetch(new URL('./config.json', import.meta.url));\nif (!response.ok) throw new Error('无法读取企业后台实例配置');\nconst config = await response.json();\ndocument.title = config.title;\nawait import('./app.js');\n");
76
92
  fs.copyFileSync(path.join(templateRoot, 'serve.cjs'), path.join(app, 'serve.cjs'));
@@ -78,11 +94,11 @@ function initialize(directory, { id, title, purpose, templateRoot = assetRoot })
78
94
  fs.writeFileSync(path.join(directory, 'design-system/MASTER.md'), fs.readFileSync(path.join(templateRoot, 'design-system.md')));
79
95
  fs.writeFileSync(path.join(directory, 'design-system/source.json'), JSON.stringify({
80
96
  schemaVersion: 1, source: origin.source, sourceVersion: origin.sourceVersion, sourceManifestDigest: origin.sourceManifestDigest,
81
- sourceFileCount: origin.fileCount, adaptations, purpose, description: '所有来源文件已复制;局部适配仅用于初始化接入。app/qa 是历史来源证据,当前验收使用原型根 qa/。',
97
+ sourceFileCount: origin.fileCount, adaptations, purpose, description: '所有来源文件已复制;运行时统一使用 i3-Composer 品牌,并保留初始化接入和存储隔离记录。app/qa 是历史来源证据,当前验收使用原型根 qa/。',
82
98
  }, null, 2) + '\n');
83
99
  fs.mkdirSync(path.join(directory, 'mocks'), { recursive: true });
84
- fs.writeFileSync(path.join(directory, 'mocks/README.md'), '# 演示数据\n\n完整 EASEL 示例的数据与规则保留在 app/src/ 各模块中,并使用实例独立的本地存储。它们用于模板审查;新业务需要按需求替换,不能当作真实服务或已完成业务适配。\n');
100
+ fs.writeFileSync(path.join(directory, 'mocks/README.md'), '# 演示数据\n\n完整 i3-Composer 示例的数据与规则保留在 app/src/ 各模块中,并使用实例独立的本地存储。它们用于模板审查;新业务需要按需求替换,不能当作真实服务或已完成业务适配。\n');
85
101
  fs.writeFileSync(path.join(directory, 'requirements-map.json'), JSON.stringify(mapping, null, 2) + '\n');
86
- return { kind: 'easel-html', entry: 'app/index.html', sourceVersion: origin.sourceVersion, instanceId, storagePrefix };
102
+ return { kind: 'easel-html', entry: 'app/index.html', sourceVersion: origin.sourceVersion, instanceId, storagePrefix, offlineVersion: 1 };
87
103
  }
88
- module.exports = { verifyReference, initialize };
104
+ module.exports = { verifyReference, initialize, brandRuntimeResource };
@@ -5,7 +5,7 @@ try {
5
5
  const { options, positional } = lib.parseArgs(process.argv.slice(2));
6
6
  const [operation, directory] = positional;
7
7
  if (!directory || !['inspect', 'manifest', 'revise', 'confirm-baseline'].includes(operation)) throw new Error('Usage: manage-baseline.cjs <inspect|manifest|revise|confirm-baseline> <directory> [--revision r002] [--confirmation file.json]');
8
- const result = operation === 'manifest' ? lib.contentManifest(directory) : operation === 'inspect' ? lib.compareBaseline(directory) : operation === 'revise' ? lib.revisePrototype(directory, options.revision) : lib.freezeBaseline(directory, JSON.parse(fs.readFileSync(options.confirmation, 'utf8')));
8
+ const result = operation === 'manifest' ? lib.contentManifest(directory) : operation === 'inspect' ? lib.compareBaseline(directory) : operation === 'revise' ? lib.revisePrototype(directory, options.revision, options) : lib.freezeBaseline(directory, JSON.parse(fs.readFileSync(options.confirmation, 'utf8')));
9
9
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
10
10
  if (result.status === 'corrupt-baseline') process.exitCode = 1;
11
11
  } catch (error) { process.stderr.write(error.message + '\n'); process.exitCode = 1; }