@leeoohoo/ui-apps-devkit 0.1.1 → 0.1.3

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 (62) hide show
  1. package/README.md +84 -76
  2. package/bin/chatos-uiapp.js +3 -4
  3. package/package.json +28 -25
  4. package/src/cli.js +53 -53
  5. package/src/commands/dev.js +14 -14
  6. package/src/commands/init.js +143 -142
  7. package/src/commands/install.js +56 -55
  8. package/src/commands/pack.js +72 -72
  9. package/src/commands/validate.js +113 -113
  10. package/src/lib/args.js +49 -49
  11. package/src/lib/config.js +29 -29
  12. package/src/lib/fs.js +78 -78
  13. package/src/lib/path-boundary.js +16 -16
  14. package/src/lib/plugin.js +45 -45
  15. package/src/lib/state-constants.js +2 -0
  16. package/src/lib/template.js +172 -172
  17. package/src/sandbox/server.js +2302 -1200
  18. package/templates/basic/README.md +80 -77
  19. package/templates/basic/chatos.config.json +5 -5
  20. package/templates/basic/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +178 -178
  21. package/templates/basic/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
  22. package/templates/basic/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
  23. package/templates/basic/docs/CHATOS_UI_APPS_OVERVIEW.md +115 -113
  24. package/templates/basic/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +225 -224
  25. package/templates/basic/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
  26. package/templates/basic/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -45
  27. package/templates/basic/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
  28. package/templates/basic/plugin/apps/app/compact.mjs +41 -41
  29. package/templates/basic/plugin/apps/app/index.mjs +287 -287
  30. package/templates/basic/plugin/apps/app/mcp-prompt.en.md +7 -7
  31. package/templates/basic/plugin/apps/app/mcp-prompt.zh.md +7 -7
  32. package/templates/basic/plugin/apps/app/mcp-server.mjs +15 -15
  33. package/templates/basic/plugin/backend/index.mjs +37 -37
  34. package/templates/basic/template.json +7 -7
  35. package/templates/notepad/README.md +61 -58
  36. package/templates/notepad/chatos.config.json +4 -4
  37. package/templates/notepad/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +178 -178
  38. package/templates/notepad/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
  39. package/templates/notepad/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
  40. package/templates/notepad/docs/CHATOS_UI_APPS_OVERVIEW.md +115 -113
  41. package/templates/notepad/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +225 -224
  42. package/templates/notepad/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
  43. package/templates/notepad/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -45
  44. package/templates/notepad/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
  45. package/templates/notepad/plugin/apps/app/api.mjs +30 -30
  46. package/templates/notepad/plugin/apps/app/compact.mjs +41 -41
  47. package/templates/notepad/plugin/apps/app/dom.mjs +14 -14
  48. package/templates/notepad/plugin/apps/app/ds-tree.mjs +35 -35
  49. package/templates/notepad/plugin/apps/app/index.mjs +1056 -1056
  50. package/templates/notepad/plugin/apps/app/layers.mjs +338 -338
  51. package/templates/notepad/plugin/apps/app/markdown.mjs +120 -120
  52. package/templates/notepad/plugin/apps/app/mcp-prompt.en.md +22 -22
  53. package/templates/notepad/plugin/apps/app/mcp-prompt.zh.md +22 -22
  54. package/templates/notepad/plugin/apps/app/mcp-server.mjs +207 -200
  55. package/templates/notepad/plugin/apps/app/styles.mjs +355 -355
  56. package/templates/notepad/plugin/apps/app/tags.mjs +21 -21
  57. package/templates/notepad/plugin/apps/app/ui.mjs +280 -280
  58. package/templates/notepad/plugin/backend/index.mjs +99 -99
  59. package/templates/notepad/plugin/plugin.json +23 -23
  60. package/templates/notepad/plugin/shared/notepad-paths.mjs +80 -62
  61. package/templates/notepad/plugin/shared/notepad-store.mjs +765 -765
  62. package/templates/notepad/template.json +8 -8
@@ -1,142 +1,143 @@
1
- import path from 'path';
2
- import readline from 'readline';
3
-
4
- import { ensureDir, isDirectory, isFile, rmForce, writeText } from '../lib/fs.js';
5
- import {
6
- copyTemplate,
7
- listTemplates,
8
- readTemplateMeta,
9
- maybeReplaceTokensInFile,
10
- writeScaffoldConfig,
11
- writeScaffoldManifest,
12
- writeScaffoldPackageJson,
13
- } from '../lib/template.js';
14
-
15
- function canPrompt() {
16
- return Boolean(process.stdin.isTTY && process.stdout.isTTY);
17
- }
18
-
19
- async function promptLine(question, { defaultValue = '' } = {}) {
20
- if (!canPrompt()) throw new Error(`Missing required value: ${question}`);
21
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
22
- try {
23
- const answer = await new Promise((resolve) => rl.question(question, resolve));
24
- const v = String(answer || '').trim();
25
- return v || defaultValue;
26
- } finally {
27
- rl.close();
28
- }
29
- }
30
-
31
- export async function cmdInit({ positionals, flags }) {
32
- const list = Boolean(flags['list-templates'] || flags.listTemplates);
33
- if (list) {
34
- const templates = listTemplates();
35
- if (templates.length === 0) {
36
- // eslint-disable-next-line no-console
37
- console.log('No templates found.');
38
- return;
39
- }
40
- const lines = templates.map((t) => `- ${t.name}${t.description ? `: ${t.description}` : ''}`).join('\n');
41
- // eslint-disable-next-line no-console
42
- console.log(`Templates:\n${lines}\n\nUse:\n chatos-uiapp init my-app --template <name>\n`);
43
- return;
44
- }
45
-
46
- const dirArg = String(positionals[0] || '').trim();
47
- if (!dirArg) throw new Error('init requires <dir>');
48
-
49
- const templateName = String(flags.template || flags.t || '').trim() || 'basic';
50
- const templateMeta = readTemplateMeta(templateName);
51
-
52
- const destDir = path.resolve(process.cwd(), dirArg);
53
- const force = Boolean(flags.force);
54
-
55
- if (isDirectory(destDir)) {
56
- const entries = await (async () => {
57
- try {
58
- return (await import('fs')).default.readdirSync(destDir);
59
- } catch {
60
- return [];
61
- }
62
- })();
63
- if (entries.length > 0 && !force) {
64
- throw new Error(`Target directory is not empty: ${destDir} (use --force to overwrite)`);
65
- }
66
- if (force) rmForce(destDir);
67
- }
68
-
69
- ensureDir(destDir);
70
- copyTemplate({ templateName, destDir });
71
-
72
- const pluginId =
73
- String(flags['plugin-id'] || flags.pluginId || '').trim() || (await promptLine('pluginId (e.g. com.example.myapp): '));
74
- const pluginName =
75
- String(flags.name || '').trim() ||
76
- (await promptLine('plugin name (display): ', { defaultValue: String(templateMeta?.defaults?.pluginName || '').trim() || pluginId }));
77
-
78
- const defaultAppId = String(templateMeta?.defaults?.appId || '').trim() || 'app';
79
- const appId =
80
- String(flags['app-id'] || flags.appId || '').trim() ||
81
- (await promptLine('appId (e.g. manager): ', { defaultValue: defaultAppId }));
82
-
83
- const version = String(flags.version || '').trim() || String(templateMeta?.defaults?.version || '').trim() || '0.1.0';
84
-
85
- const pluginDir = path.join(destDir, 'plugin');
86
- ensureDir(pluginDir);
87
-
88
- if (!isFile(path.join(pluginDir, 'plugin.json'))) {
89
- const withBackend = templateMeta?.defaults?.withBackend !== false;
90
- writeScaffoldManifest({ destPluginDir: pluginDir, pluginId, pluginName, version, appId, withBackend });
91
- }
92
- writeScaffoldPackageJson({ destDir, projectName: path.basename(destDir) });
93
- writeScaffoldConfig({ destDir, pluginDir: 'plugin', appId });
94
-
95
- // rename template app folder "app" -> actual appId
96
- const srcAppDir = path.join(pluginDir, 'apps', 'app');
97
- const dstAppDir = path.join(pluginDir, 'apps', appId);
98
- try {
99
- const fs = (await import('fs')).default;
100
- if (fs.existsSync(srcAppDir) && fs.statSync(srcAppDir).isDirectory()) {
101
- ensureDir(path.dirname(dstAppDir));
102
- fs.renameSync(srcAppDir, dstAppDir);
103
- }
104
- } catch {
105
- // ignore
106
- }
107
-
108
- // Token replacements inside README/template code.
109
- const replacements = {
110
- __PLUGIN_ID__: pluginId,
111
- __PLUGIN_NAME__: pluginName,
112
- __APP_ID__: appId,
113
- __VERSION__: version,
114
- };
115
-
116
- maybeReplaceTokensInFile(path.join(destDir, 'README.md'), replacements);
117
- maybeReplaceTokensInFile(path.join(destDir, 'chatos.config.json'), replacements);
118
- if (isFile(path.join(pluginDir, 'plugin.json'))) {
119
- maybeReplaceTokensInFile(path.join(pluginDir, 'plugin.json'), replacements);
120
- }
121
- maybeReplaceTokensInFile(path.join(pluginDir, 'backend', 'index.mjs'), replacements);
122
- maybeReplaceTokensInFile(path.join(dstAppDir, 'index.mjs'), replacements);
123
- maybeReplaceTokensInFile(path.join(dstAppDir, 'compact.mjs'), replacements);
124
- maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-server.mjs'), replacements);
125
- maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-prompt.zh.md'), replacements);
126
- maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-prompt.en.md'), replacements);
127
-
128
- // Ensure a helpful note exists even if template is edited later.
129
- writeText(
130
- path.join(destDir, '.gitignore'),
131
- `node_modules/\n.DS_Store\n.chatos/\n*.log\n\n# build outputs (if you add bundling later)\ndist/\n`
132
- );
133
-
134
- // eslint-disable-next-line no-console
135
- console.log(`Created: ${destDir}
136
-
137
- Next:
138
- cd ${dirArg}
139
- npm install
140
- npm run dev
141
- `);
142
- }
1
+ import path from 'path';
2
+ import readline from 'readline';
3
+
4
+ import { ensureDir, isDirectory, isFile, rmForce, writeText } from '../lib/fs.js';
5
+ import {
6
+ copyTemplate,
7
+ listTemplates,
8
+ readTemplateMeta,
9
+ maybeReplaceTokensInFile,
10
+ writeScaffoldConfig,
11
+ writeScaffoldManifest,
12
+ writeScaffoldPackageJson,
13
+ } from '../lib/template.js';
14
+ import { COMPAT_STATE_ROOT_DIRNAME, STATE_ROOT_DIRNAME } from '../lib/state-constants.js';
15
+
16
+ function canPrompt() {
17
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
18
+ }
19
+
20
+ async function promptLine(question, { defaultValue = '' } = {}) {
21
+ if (!canPrompt()) throw new Error(`Missing required value: ${question}`);
22
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
23
+ try {
24
+ const answer = await new Promise((resolve) => rl.question(question, resolve));
25
+ const v = String(answer || '').trim();
26
+ return v || defaultValue;
27
+ } finally {
28
+ rl.close();
29
+ }
30
+ }
31
+
32
+ export async function cmdInit({ positionals, flags }) {
33
+ const list = Boolean(flags['list-templates'] || flags.listTemplates);
34
+ if (list) {
35
+ const templates = listTemplates();
36
+ if (templates.length === 0) {
37
+ // eslint-disable-next-line no-console
38
+ console.log('No templates found.');
39
+ return;
40
+ }
41
+ const lines = templates.map((t) => `- ${t.name}${t.description ? `: ${t.description}` : ''}`).join('\n');
42
+ // eslint-disable-next-line no-console
43
+ console.log(`Templates:\n${lines}\n\nUse:\n chatos-uiapp init my-app --template <name>\n`);
44
+ return;
45
+ }
46
+
47
+ const dirArg = String(positionals[0] || '').trim();
48
+ if (!dirArg) throw new Error('init requires <dir>');
49
+
50
+ const templateName = String(flags.template || flags.t || '').trim() || 'basic';
51
+ const templateMeta = readTemplateMeta(templateName);
52
+
53
+ const destDir = path.resolve(process.cwd(), dirArg);
54
+ const force = Boolean(flags.force);
55
+
56
+ if (isDirectory(destDir)) {
57
+ const entries = await (async () => {
58
+ try {
59
+ return (await import('fs')).default.readdirSync(destDir);
60
+ } catch {
61
+ return [];
62
+ }
63
+ })();
64
+ if (entries.length > 0 && !force) {
65
+ throw new Error(`Target directory is not empty: ${destDir} (use --force to overwrite)`);
66
+ }
67
+ if (force) rmForce(destDir);
68
+ }
69
+
70
+ ensureDir(destDir);
71
+ copyTemplate({ templateName, destDir });
72
+
73
+ const pluginId =
74
+ String(flags['plugin-id'] || flags.pluginId || '').trim() || (await promptLine('pluginId (e.g. com.example.myapp): '));
75
+ const pluginName =
76
+ String(flags.name || '').trim() ||
77
+ (await promptLine('plugin name (display): ', { defaultValue: String(templateMeta?.defaults?.pluginName || '').trim() || pluginId }));
78
+
79
+ const defaultAppId = String(templateMeta?.defaults?.appId || '').trim() || 'app';
80
+ const appId =
81
+ String(flags['app-id'] || flags.appId || '').trim() ||
82
+ (await promptLine('appId (e.g. manager): ', { defaultValue: defaultAppId }));
83
+
84
+ const version = String(flags.version || '').trim() || String(templateMeta?.defaults?.version || '').trim() || '0.1.0';
85
+
86
+ const pluginDir = path.join(destDir, 'plugin');
87
+ ensureDir(pluginDir);
88
+
89
+ if (!isFile(path.join(pluginDir, 'plugin.json'))) {
90
+ const withBackend = templateMeta?.defaults?.withBackend !== false;
91
+ writeScaffoldManifest({ destPluginDir: pluginDir, pluginId, pluginName, version, appId, withBackend });
92
+ }
93
+ writeScaffoldPackageJson({ destDir, projectName: path.basename(destDir) });
94
+ writeScaffoldConfig({ destDir, pluginDir: 'plugin', appId });
95
+
96
+ // rename template app folder "app" -> actual appId
97
+ const srcAppDir = path.join(pluginDir, 'apps', 'app');
98
+ const dstAppDir = path.join(pluginDir, 'apps', appId);
99
+ try {
100
+ const fs = (await import('fs')).default;
101
+ if (fs.existsSync(srcAppDir) && fs.statSync(srcAppDir).isDirectory()) {
102
+ ensureDir(path.dirname(dstAppDir));
103
+ fs.renameSync(srcAppDir, dstAppDir);
104
+ }
105
+ } catch {
106
+ // ignore
107
+ }
108
+
109
+ // Token replacements inside README/template code.
110
+ const replacements = {
111
+ __PLUGIN_ID__: pluginId,
112
+ __PLUGIN_NAME__: pluginName,
113
+ __APP_ID__: appId,
114
+ __VERSION__: version,
115
+ };
116
+
117
+ maybeReplaceTokensInFile(path.join(destDir, 'README.md'), replacements);
118
+ maybeReplaceTokensInFile(path.join(destDir, 'chatos.config.json'), replacements);
119
+ if (isFile(path.join(pluginDir, 'plugin.json'))) {
120
+ maybeReplaceTokensInFile(path.join(pluginDir, 'plugin.json'), replacements);
121
+ }
122
+ maybeReplaceTokensInFile(path.join(pluginDir, 'backend', 'index.mjs'), replacements);
123
+ maybeReplaceTokensInFile(path.join(dstAppDir, 'index.mjs'), replacements);
124
+ maybeReplaceTokensInFile(path.join(dstAppDir, 'compact.mjs'), replacements);
125
+ maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-server.mjs'), replacements);
126
+ maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-prompt.zh.md'), replacements);
127
+ maybeReplaceTokensInFile(path.join(dstAppDir, 'mcp-prompt.en.md'), replacements);
128
+
129
+ // Ensure a helpful note exists even if template is edited later.
130
+ writeText(
131
+ path.join(destDir, '.gitignore'),
132
+ `node_modules/\n.DS_Store\n${STATE_ROOT_DIRNAME}/\n${COMPAT_STATE_ROOT_DIRNAME}/\n*.log\n\n# build outputs (if you add bundling later)\ndist/\n`
133
+ );
134
+
135
+ // eslint-disable-next-line no-console
136
+ console.log(`Created: ${destDir}
137
+
138
+ Next:
139
+ cd ${dirArg}
140
+ npm install
141
+ npm run dev
142
+ `);
143
+ }
@@ -1,55 +1,56 @@
1
- import os from 'os';
2
- import path from 'path';
3
-
4
- import { copyDir, ensureDir, isDirectory, rmForce, sanitizeDirComponent } from '../lib/fs.js';
5
- import { loadDevkitConfig } from '../lib/config.js';
6
- import { findPluginDir, loadPluginManifest } from '../lib/plugin.js';
7
-
8
- function defaultStateDir(hostApp) {
9
- const app = typeof hostApp === 'string' && hostApp.trim() ? hostApp.trim() : 'chatos';
10
- return path.join(os.homedir(), '.deepseek_cli', app);
11
- }
12
-
13
- function copyPluginDir(srcDir, destDir) {
14
- ensureDir(path.dirname(destDir));
15
- rmForce(destDir);
16
- copyDir(srcDir, destDir, {
17
- filter: (src) => {
18
- const base = path.basename(src);
19
- if (base === 'node_modules') return false;
20
- if (base === '.git') return false;
21
- if (base === '.DS_Store') return false;
22
- if (base.endsWith('.map')) return false;
23
- return true;
24
- },
25
- });
26
- }
27
-
28
- export async function cmdInstall({ flags }) {
29
- const { config } = loadDevkitConfig(process.cwd());
30
- const pluginDir = findPluginDir(process.cwd(), flags['plugin-dir'] || flags.pluginDir || config?.pluginDir);
31
- const { pluginId, name, version } = loadPluginManifest(pluginDir);
32
-
33
- const hostApp = String(flags['host-app'] || flags.hostApp || 'chatos').trim() || 'chatos';
34
- const stateDir = String(flags['state-dir'] || flags.stateDir || defaultStateDir(hostApp)).trim();
35
- if (!stateDir) throw new Error('stateDir is required');
36
-
37
- const pluginsRoot = path.join(stateDir, 'ui_apps', 'plugins');
38
- ensureDir(pluginsRoot);
39
-
40
- const dirName = sanitizeDirComponent(pluginId);
41
- if (!dirName) throw new Error(`Invalid plugin id: ${pluginId}`);
42
-
43
- const destDir = path.join(pluginsRoot, dirName);
44
- const replaced = isDirectory(destDir);
45
- copyPluginDir(pluginDir, destDir);
46
-
47
- // eslint-disable-next-line no-console
48
- console.log(
49
- `Installed: ${pluginId} (${name}@${version})\n` +
50
- ` -> ${destDir}\n` +
51
- ` replaced: ${replaced}\n\n` +
52
- `Open ChatOS -> 应用 -> 刷新(同 id 覆盖生效)。`
53
- );
54
- }
55
-
1
+ import os from 'os';
2
+ import path from 'path';
3
+
4
+ import { copyDir, ensureDir, isDirectory, rmForce, sanitizeDirComponent } from '../lib/fs.js';
5
+ import { loadDevkitConfig } from '../lib/config.js';
6
+ import { findPluginDir, loadPluginManifest } from '../lib/plugin.js';
7
+ import { STATE_ROOT_DIRNAME } from '../lib/state-constants.js';
8
+
9
+ function defaultStateDir(hostApp) {
10
+ const app = typeof hostApp === 'string' && hostApp.trim() ? hostApp.trim() : 'chatos';
11
+ return path.join(os.homedir(), STATE_ROOT_DIRNAME, app);
12
+ }
13
+
14
+ function copyPluginDir(srcDir, destDir) {
15
+ ensureDir(path.dirname(destDir));
16
+ rmForce(destDir);
17
+ copyDir(srcDir, destDir, {
18
+ filter: (src) => {
19
+ const base = path.basename(src);
20
+ if (base === 'node_modules') return false;
21
+ if (base === '.git') return false;
22
+ if (base === '.DS_Store') return false;
23
+ if (base.endsWith('.map')) return false;
24
+ return true;
25
+ },
26
+ });
27
+ }
28
+
29
+ export async function cmdInstall({ flags }) {
30
+ const { config } = loadDevkitConfig(process.cwd());
31
+ const pluginDir = findPluginDir(process.cwd(), flags['plugin-dir'] || flags.pluginDir || config?.pluginDir);
32
+ const { pluginId, name, version } = loadPluginManifest(pluginDir);
33
+
34
+ const hostApp = String(flags['host-app'] || flags.hostApp || 'chatos').trim() || 'chatos';
35
+ const stateDir = String(flags['state-dir'] || flags.stateDir || defaultStateDir(hostApp)).trim();
36
+ if (!stateDir) throw new Error('stateDir is required');
37
+
38
+ const pluginsRoot = path.join(stateDir, 'ui_apps', 'plugins');
39
+ ensureDir(pluginsRoot);
40
+
41
+ const dirName = sanitizeDirComponent(pluginId);
42
+ if (!dirName) throw new Error(`Invalid plugin id: ${pluginId}`);
43
+
44
+ const destDir = path.join(pluginsRoot, dirName);
45
+ const replaced = isDirectory(destDir);
46
+ copyPluginDir(pluginDir, destDir);
47
+
48
+ // eslint-disable-next-line no-console
49
+ console.log(
50
+ `Installed: ${pluginId} (${name}@${version})\n` +
51
+ ` -> ${destDir}\n` +
52
+ ` replaced: ${replaced}\n\n` +
53
+ `Open ChatOS -> 应用 -> 刷新(同 id 覆盖生效)。`
54
+ );
55
+ }
56
+
@@ -1,72 +1,72 @@
1
- import os from 'os';
2
- import path from 'path';
3
- import { spawnSync } from 'child_process';
4
-
5
- import { copyDir, ensureDir, rmForce } from '../lib/fs.js';
6
- import { loadDevkitConfig } from '../lib/config.js';
7
- import { findPluginDir, loadPluginManifest } from '../lib/plugin.js';
8
-
9
- function hasCmd(cmd) {
10
- const res = spawnSync('sh', ['-lc', `command -v ${cmd} >/dev/null 2>&1`], { stdio: 'ignore' });
11
- return res.status === 0;
12
- }
13
-
14
- function packWithZip({ cwd, srcDir, outFile }) {
15
- // zip -r out.zip . (from within srcDir)
16
- const res = spawnSync('zip', ['-r', outFile, '.'], { cwd: srcDir, stdio: 'inherit' });
17
- if (res.status !== 0) throw new Error('zip failed');
18
- }
19
-
20
- function packWithPowershell({ srcDir, outFile }) {
21
- const script = `Compress-Archive -Path "${srcDir}\\*" -DestinationPath "${outFile}" -Force`;
22
- const res = spawnSync('powershell', ['-NoProfile', '-Command', script], { stdio: 'inherit' });
23
- if (res.status !== 0) throw new Error('powershell Compress-Archive failed');
24
- }
25
-
26
- export async function cmdPack({ flags }) {
27
- const { config } = loadDevkitConfig(process.cwd());
28
- const pluginDir = findPluginDir(process.cwd(), flags['plugin-dir'] || flags.pluginDir || config?.pluginDir);
29
- const { pluginId, version } = loadPluginManifest(pluginDir);
30
-
31
- const outArg = String(flags.out || '').trim();
32
- const outDir = outArg ? path.dirname(path.resolve(process.cwd(), outArg)) : path.join(process.cwd(), 'dist');
33
- ensureDir(outDir);
34
-
35
- const outFile = outArg
36
- ? path.resolve(process.cwd(), outArg)
37
- : path.join(outDir, `${pluginId.replace(/[^a-zA-Z0-9._-]+/g, '-')}-${version || '0.0.0'}.zip`);
38
-
39
- rmForce(outFile);
40
-
41
- const stagingBase = path.join(os.tmpdir(), `chatos-uiapp-pack-${Date.now()}-${Math.random().toString(16).slice(2)}`);
42
- ensureDir(stagingBase);
43
- try {
44
- // mimic ChatOS importer: exclude node_modules/.git/*.map/.DS_Store
45
- copyDir(pluginDir, stagingBase, {
46
- filter: (src) => {
47
- const base = path.basename(src);
48
- if (base === 'node_modules') return false;
49
- if (base === '.git') return false;
50
- if (base === '.DS_Store') return false;
51
- if (base.endsWith('.map')) return false;
52
- return true;
53
- },
54
- });
55
-
56
- const platform = process.platform;
57
- if (hasCmd('zip')) {
58
- packWithZip({ cwd: process.cwd(), srcDir: stagingBase, outFile });
59
- } else if (platform === 'win32') {
60
- packWithPowershell({ srcDir: stagingBase, outFile });
61
- } else {
62
- throw new Error('zip command not found (install "zip" or run on Windows with powershell)');
63
- }
64
-
65
- // eslint-disable-next-line no-console
66
- console.log(`Packed: ${outFile}
67
-
68
- ChatOS -> 应用 -> 导入应用包 -> 选择该 zip`);
69
- } finally {
70
- rmForce(stagingBase);
71
- }
72
- }
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import { spawnSync } from 'child_process';
4
+
5
+ import { copyDir, ensureDir, rmForce } from '../lib/fs.js';
6
+ import { loadDevkitConfig } from '../lib/config.js';
7
+ import { findPluginDir, loadPluginManifest } from '../lib/plugin.js';
8
+
9
+ function hasCmd(cmd) {
10
+ const res = spawnSync('sh', ['-lc', `command -v ${cmd} >/dev/null 2>&1`], { stdio: 'ignore' });
11
+ return res.status === 0;
12
+ }
13
+
14
+ function packWithZip({ cwd, srcDir, outFile }) {
15
+ // zip -r out.zip . (from within srcDir)
16
+ const res = spawnSync('zip', ['-r', outFile, '.'], { cwd: srcDir, stdio: 'inherit' });
17
+ if (res.status !== 0) throw new Error('zip failed');
18
+ }
19
+
20
+ function packWithPowershell({ srcDir, outFile }) {
21
+ const script = `Compress-Archive -Path "${srcDir}\\*" -DestinationPath "${outFile}" -Force`;
22
+ const res = spawnSync('powershell', ['-NoProfile', '-Command', script], { stdio: 'inherit' });
23
+ if (res.status !== 0) throw new Error('powershell Compress-Archive failed');
24
+ }
25
+
26
+ export async function cmdPack({ flags }) {
27
+ const { config } = loadDevkitConfig(process.cwd());
28
+ const pluginDir = findPluginDir(process.cwd(), flags['plugin-dir'] || flags.pluginDir || config?.pluginDir);
29
+ const { pluginId, version } = loadPluginManifest(pluginDir);
30
+
31
+ const outArg = String(flags.out || '').trim();
32
+ const outDir = outArg ? path.dirname(path.resolve(process.cwd(), outArg)) : path.join(process.cwd(), 'dist');
33
+ ensureDir(outDir);
34
+
35
+ const outFile = outArg
36
+ ? path.resolve(process.cwd(), outArg)
37
+ : path.join(outDir, `${pluginId.replace(/[^a-zA-Z0-9._-]+/g, '-')}-${version || '0.0.0'}.zip`);
38
+
39
+ rmForce(outFile);
40
+
41
+ const stagingBase = path.join(os.tmpdir(), `chatos-uiapp-pack-${Date.now()}-${Math.random().toString(16).slice(2)}`);
42
+ ensureDir(stagingBase);
43
+ try {
44
+ // mimic ChatOS importer: exclude node_modules/.git/*.map/.DS_Store
45
+ copyDir(pluginDir, stagingBase, {
46
+ filter: (src) => {
47
+ const base = path.basename(src);
48
+ if (base === 'node_modules') return false;
49
+ if (base === '.git') return false;
50
+ if (base === '.DS_Store') return false;
51
+ if (base.endsWith('.map')) return false;
52
+ return true;
53
+ },
54
+ });
55
+
56
+ const platform = process.platform;
57
+ if (hasCmd('zip')) {
58
+ packWithZip({ cwd: process.cwd(), srcDir: stagingBase, outFile });
59
+ } else if (platform === 'win32') {
60
+ packWithPowershell({ srcDir: stagingBase, outFile });
61
+ } else {
62
+ throw new Error('zip command not found (install "zip" or run on Windows with powershell)');
63
+ }
64
+
65
+ // eslint-disable-next-line no-console
66
+ console.log(`Packed: ${outFile}
67
+
68
+ ChatOS -> 应用 -> 导入应用包 -> 选择该 zip`);
69
+ } finally {
70
+ rmForce(stagingBase);
71
+ }
72
+ }