@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.
- package/README.md +84 -76
- package/bin/chatos-uiapp.js +3 -4
- package/package.json +28 -25
- package/src/cli.js +53 -53
- package/src/commands/dev.js +14 -14
- package/src/commands/init.js +143 -142
- package/src/commands/install.js +56 -55
- package/src/commands/pack.js +72 -72
- package/src/commands/validate.js +113 -113
- package/src/lib/args.js +49 -49
- package/src/lib/config.js +29 -29
- package/src/lib/fs.js +78 -78
- package/src/lib/path-boundary.js +16 -16
- package/src/lib/plugin.js +45 -45
- package/src/lib/state-constants.js +2 -0
- package/src/lib/template.js +172 -172
- package/src/sandbox/server.js +2302 -1200
- package/templates/basic/README.md +80 -77
- package/templates/basic/chatos.config.json +5 -5
- package/templates/basic/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +178 -178
- package/templates/basic/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
- package/templates/basic/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
- package/templates/basic/docs/CHATOS_UI_APPS_OVERVIEW.md +115 -113
- package/templates/basic/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +225 -224
- package/templates/basic/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
- package/templates/basic/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -45
- package/templates/basic/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
- package/templates/basic/plugin/apps/app/compact.mjs +41 -41
- package/templates/basic/plugin/apps/app/index.mjs +287 -287
- package/templates/basic/plugin/apps/app/mcp-prompt.en.md +7 -7
- package/templates/basic/plugin/apps/app/mcp-prompt.zh.md +7 -7
- package/templates/basic/plugin/apps/app/mcp-server.mjs +15 -15
- package/templates/basic/plugin/backend/index.mjs +37 -37
- package/templates/basic/template.json +7 -7
- package/templates/notepad/README.md +61 -58
- package/templates/notepad/chatos.config.json +4 -4
- package/templates/notepad/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +178 -178
- package/templates/notepad/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +75 -74
- package/templates/notepad/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
- package/templates/notepad/docs/CHATOS_UI_APPS_OVERVIEW.md +115 -113
- package/templates/notepad/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +225 -224
- package/templates/notepad/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
- package/templates/notepad/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +45 -45
- package/templates/notepad/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
- package/templates/notepad/plugin/apps/app/api.mjs +30 -30
- package/templates/notepad/plugin/apps/app/compact.mjs +41 -41
- package/templates/notepad/plugin/apps/app/dom.mjs +14 -14
- package/templates/notepad/plugin/apps/app/ds-tree.mjs +35 -35
- package/templates/notepad/plugin/apps/app/index.mjs +1056 -1056
- package/templates/notepad/plugin/apps/app/layers.mjs +338 -338
- package/templates/notepad/plugin/apps/app/markdown.mjs +120 -120
- package/templates/notepad/plugin/apps/app/mcp-prompt.en.md +22 -22
- package/templates/notepad/plugin/apps/app/mcp-prompt.zh.md +22 -22
- package/templates/notepad/plugin/apps/app/mcp-server.mjs +207 -200
- package/templates/notepad/plugin/apps/app/styles.mjs +355 -355
- package/templates/notepad/plugin/apps/app/tags.mjs +21 -21
- package/templates/notepad/plugin/apps/app/ui.mjs +280 -280
- package/templates/notepad/plugin/backend/index.mjs +99 -99
- package/templates/notepad/plugin/plugin.json +23 -23
- package/templates/notepad/plugin/shared/notepad-paths.mjs +80 -62
- package/templates/notepad/plugin/shared/notepad-store.mjs +765 -765
- package/templates/notepad/template.json +8 -8
package/src/lib/template.js
CHANGED
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
import { copyDir, ensureDir, isDirectory, isFile, readJson, writeJson, writeText } from './fs.js';
|
|
6
|
-
|
|
7
|
-
function packageRoot() {
|
|
8
|
-
// src/lib/template.js -> src -> package root
|
|
9
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
return path.resolve(here, '..', '..');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function getTemplateDir(name) {
|
|
14
|
-
const root = packageRoot();
|
|
15
|
-
const dir = path.join(root, 'templates', name);
|
|
16
|
-
if (!isDirectory(dir)) throw new Error(`template not found: ${name}`);
|
|
17
|
-
return dir;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function readSelfPackage() {
|
|
21
|
-
const root = packageRoot();
|
|
22
|
-
const pkgPath = path.join(root, 'package.json');
|
|
23
|
-
if (!isFile(pkgPath)) return { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
24
|
-
try {
|
|
25
|
-
const pkg = readJson(pkgPath);
|
|
26
|
-
return pkg && typeof pkg === 'object' ? pkg : { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
27
|
-
} catch {
|
|
28
|
-
return { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function readTemplateMeta(name) {
|
|
33
|
-
const dir = getTemplateDir(name);
|
|
34
|
-
const metaPath = path.join(dir, 'template.json');
|
|
35
|
-
if (!isFile(metaPath)) return { name, description: '', defaults: null };
|
|
36
|
-
try {
|
|
37
|
-
const meta = readJson(metaPath);
|
|
38
|
-
return {
|
|
39
|
-
name,
|
|
40
|
-
description: typeof meta?.description === 'string' ? meta.description.trim() : '',
|
|
41
|
-
defaults: meta?.defaults && typeof meta.defaults === 'object' ? meta.defaults : null,
|
|
42
|
-
};
|
|
43
|
-
} catch {
|
|
44
|
-
return { name, description: '', defaults: null };
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function listTemplates() {
|
|
49
|
-
const root = packageRoot();
|
|
50
|
-
const templatesDir = path.join(root, 'templates');
|
|
51
|
-
let entries = [];
|
|
52
|
-
try {
|
|
53
|
-
entries = fs.readdirSync(templatesDir, { withFileTypes: true });
|
|
54
|
-
} catch {
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const out = [];
|
|
59
|
-
for (const ent of entries) {
|
|
60
|
-
if (!ent?.isDirectory?.()) continue;
|
|
61
|
-
const name = String(ent.name || '').trim();
|
|
62
|
-
if (!name || name.startsWith('.')) continue;
|
|
63
|
-
if (!isDirectory(path.join(templatesDir, name))) continue;
|
|
64
|
-
out.push(readTemplateMeta(name));
|
|
65
|
-
}
|
|
66
|
-
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
67
|
-
return out;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function copyTemplate({ templateName, destDir }) {
|
|
71
|
-
const srcDir = getTemplateDir(templateName);
|
|
72
|
-
ensureDir(destDir);
|
|
73
|
-
|
|
74
|
-
copyDir(srcDir, destDir, {
|
|
75
|
-
filter: (src) => {
|
|
76
|
-
const base = path.basename(src);
|
|
77
|
-
if (base === 'node_modules') return false;
|
|
78
|
-
if (base === '.DS_Store') return false;
|
|
79
|
-
return true;
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function writeScaffoldManifest({ destPluginDir, pluginId, pluginName, version, appId, withBackend = true }) {
|
|
85
|
-
const manifest = {
|
|
86
|
-
manifestVersion: 1,
|
|
87
|
-
id: pluginId,
|
|
88
|
-
name: pluginName,
|
|
89
|
-
version: version || '0.1.0',
|
|
90
|
-
description: 'A ChatOS UI Apps plugin.',
|
|
91
|
-
...(withBackend ? { backend: { entry: 'backend/index.mjs' } } : {}),
|
|
92
|
-
apps: [
|
|
93
|
-
{
|
|
94
|
-
id: appId,
|
|
95
|
-
name: 'My App',
|
|
96
|
-
description: 'A ChatOS module app.',
|
|
97
|
-
entry: {
|
|
98
|
-
type: 'module',
|
|
99
|
-
path: `apps/${appId}/index.mjs`,
|
|
100
|
-
compact: { type: 'module', path: `apps/${appId}/compact.mjs` },
|
|
101
|
-
},
|
|
102
|
-
ai: {
|
|
103
|
-
// Keep the default scaffold dependency-free: prompt is safe, MCP server is opt-in.
|
|
104
|
-
mcpPrompt: {
|
|
105
|
-
title: 'My App · MCP Prompt',
|
|
106
|
-
zh: `apps/${appId}/mcp-prompt.zh.md`,
|
|
107
|
-
en: `apps/${appId}/mcp-prompt.en.md`,
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
writeJson(path.join(destPluginDir, 'plugin.json'), manifest);
|
|
115
|
-
return manifest;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function writeScaffoldPackageJson({ destDir, projectName }) {
|
|
119
|
-
const selfPkg = readSelfPackage();
|
|
120
|
-
const devkitName = typeof selfPkg?.name === 'string' && selfPkg.name.trim() ? selfPkg.name.trim() : '@leeoohoo/ui-apps-devkit';
|
|
121
|
-
const devkitVersion = typeof selfPkg?.version === 'string' && selfPkg.version.trim() ? selfPkg.version.trim() : '0.1.0';
|
|
122
|
-
const devkitRange = `^${devkitVersion}`;
|
|
123
|
-
|
|
124
|
-
const baseScripts = {
|
|
125
|
-
dev: 'chatos-uiapp dev',
|
|
126
|
-
validate: 'chatos-uiapp validate',
|
|
127
|
-
pack: 'chatos-uiapp pack',
|
|
128
|
-
'install:chatos': 'chatos-uiapp install --host-app chatos',
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const pkgPath = path.join(destDir, 'package.json');
|
|
132
|
-
const existing = isFile(pkgPath) ? readJson(pkgPath) : {};
|
|
133
|
-
const pkg = existing && typeof existing === 'object' ? existing : {};
|
|
134
|
-
|
|
135
|
-
pkg.name = projectName;
|
|
136
|
-
pkg.private = true;
|
|
137
|
-
pkg.type = 'module';
|
|
138
|
-
|
|
139
|
-
pkg.scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {};
|
|
140
|
-
for (const [key, value] of Object.entries(baseScripts)) {
|
|
141
|
-
if (!pkg.scripts[key]) pkg.scripts[key] = value;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
pkg.devDependencies = pkg.devDependencies && typeof pkg.devDependencies === 'object' ? pkg.devDependencies : {};
|
|
145
|
-
if (!pkg.devDependencies[devkitName]) {
|
|
146
|
-
pkg.devDependencies[devkitName] = devkitRange;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
writeJson(pkgPath, pkg);
|
|
150
|
-
return pkg;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function writeScaffoldConfig({ destDir, pluginDir = 'plugin', appId = '' }) {
|
|
154
|
-
const cfgPath = path.join(destDir, 'chatos.config.json');
|
|
155
|
-
const existing = isFile(cfgPath) ? readJson(cfgPath) : {};
|
|
156
|
-
const cfg = existing && typeof existing === 'object' ? existing : {};
|
|
157
|
-
cfg.pluginDir = pluginDir;
|
|
158
|
-
cfg.appId = appId;
|
|
159
|
-
writeJson(cfgPath, cfg);
|
|
160
|
-
return cfg;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function maybeReplaceTokensInFile(filePath, replacements) {
|
|
164
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
165
|
-
let next = raw;
|
|
166
|
-
for (const [key, value] of Object.entries(replacements || {})) {
|
|
167
|
-
next = next.split(key).join(String(value));
|
|
168
|
-
}
|
|
169
|
-
if (next !== raw) {
|
|
170
|
-
writeText(filePath, next);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import { copyDir, ensureDir, isDirectory, isFile, readJson, writeJson, writeText } from './fs.js';
|
|
6
|
+
|
|
7
|
+
function packageRoot() {
|
|
8
|
+
// src/lib/template.js -> src -> package root
|
|
9
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
return path.resolve(here, '..', '..');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getTemplateDir(name) {
|
|
14
|
+
const root = packageRoot();
|
|
15
|
+
const dir = path.join(root, 'templates', name);
|
|
16
|
+
if (!isDirectory(dir)) throw new Error(`template not found: ${name}`);
|
|
17
|
+
return dir;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readSelfPackage() {
|
|
21
|
+
const root = packageRoot();
|
|
22
|
+
const pkgPath = path.join(root, 'package.json');
|
|
23
|
+
if (!isFile(pkgPath)) return { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
24
|
+
try {
|
|
25
|
+
const pkg = readJson(pkgPath);
|
|
26
|
+
return pkg && typeof pkg === 'object' ? pkg : { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
27
|
+
} catch {
|
|
28
|
+
return { name: '@leeoohoo/ui-apps-devkit', version: '0.1.0' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function readTemplateMeta(name) {
|
|
33
|
+
const dir = getTemplateDir(name);
|
|
34
|
+
const metaPath = path.join(dir, 'template.json');
|
|
35
|
+
if (!isFile(metaPath)) return { name, description: '', defaults: null };
|
|
36
|
+
try {
|
|
37
|
+
const meta = readJson(metaPath);
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
description: typeof meta?.description === 'string' ? meta.description.trim() : '',
|
|
41
|
+
defaults: meta?.defaults && typeof meta.defaults === 'object' ? meta.defaults : null,
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return { name, description: '', defaults: null };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function listTemplates() {
|
|
49
|
+
const root = packageRoot();
|
|
50
|
+
const templatesDir = path.join(root, 'templates');
|
|
51
|
+
let entries = [];
|
|
52
|
+
try {
|
|
53
|
+
entries = fs.readdirSync(templatesDir, { withFileTypes: true });
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const ent of entries) {
|
|
60
|
+
if (!ent?.isDirectory?.()) continue;
|
|
61
|
+
const name = String(ent.name || '').trim();
|
|
62
|
+
if (!name || name.startsWith('.')) continue;
|
|
63
|
+
if (!isDirectory(path.join(templatesDir, name))) continue;
|
|
64
|
+
out.push(readTemplateMeta(name));
|
|
65
|
+
}
|
|
66
|
+
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function copyTemplate({ templateName, destDir }) {
|
|
71
|
+
const srcDir = getTemplateDir(templateName);
|
|
72
|
+
ensureDir(destDir);
|
|
73
|
+
|
|
74
|
+
copyDir(srcDir, destDir, {
|
|
75
|
+
filter: (src) => {
|
|
76
|
+
const base = path.basename(src);
|
|
77
|
+
if (base === 'node_modules') return false;
|
|
78
|
+
if (base === '.DS_Store') return false;
|
|
79
|
+
return true;
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function writeScaffoldManifest({ destPluginDir, pluginId, pluginName, version, appId, withBackend = true }) {
|
|
85
|
+
const manifest = {
|
|
86
|
+
manifestVersion: 1,
|
|
87
|
+
id: pluginId,
|
|
88
|
+
name: pluginName,
|
|
89
|
+
version: version || '0.1.0',
|
|
90
|
+
description: 'A ChatOS UI Apps plugin.',
|
|
91
|
+
...(withBackend ? { backend: { entry: 'backend/index.mjs' } } : {}),
|
|
92
|
+
apps: [
|
|
93
|
+
{
|
|
94
|
+
id: appId,
|
|
95
|
+
name: 'My App',
|
|
96
|
+
description: 'A ChatOS module app.',
|
|
97
|
+
entry: {
|
|
98
|
+
type: 'module',
|
|
99
|
+
path: `apps/${appId}/index.mjs`,
|
|
100
|
+
compact: { type: 'module', path: `apps/${appId}/compact.mjs` },
|
|
101
|
+
},
|
|
102
|
+
ai: {
|
|
103
|
+
// Keep the default scaffold dependency-free: prompt is safe, MCP server is opt-in.
|
|
104
|
+
mcpPrompt: {
|
|
105
|
+
title: 'My App · MCP Prompt',
|
|
106
|
+
zh: `apps/${appId}/mcp-prompt.zh.md`,
|
|
107
|
+
en: `apps/${appId}/mcp-prompt.en.md`,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
writeJson(path.join(destPluginDir, 'plugin.json'), manifest);
|
|
115
|
+
return manifest;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function writeScaffoldPackageJson({ destDir, projectName }) {
|
|
119
|
+
const selfPkg = readSelfPackage();
|
|
120
|
+
const devkitName = typeof selfPkg?.name === 'string' && selfPkg.name.trim() ? selfPkg.name.trim() : '@leeoohoo/ui-apps-devkit';
|
|
121
|
+
const devkitVersion = typeof selfPkg?.version === 'string' && selfPkg.version.trim() ? selfPkg.version.trim() : '0.1.0';
|
|
122
|
+
const devkitRange = `^${devkitVersion}`;
|
|
123
|
+
|
|
124
|
+
const baseScripts = {
|
|
125
|
+
dev: 'chatos-uiapp dev',
|
|
126
|
+
validate: 'chatos-uiapp validate',
|
|
127
|
+
pack: 'chatos-uiapp pack',
|
|
128
|
+
'install:chatos': 'chatos-uiapp install --host-app chatos',
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const pkgPath = path.join(destDir, 'package.json');
|
|
132
|
+
const existing = isFile(pkgPath) ? readJson(pkgPath) : {};
|
|
133
|
+
const pkg = existing && typeof existing === 'object' ? existing : {};
|
|
134
|
+
|
|
135
|
+
pkg.name = projectName;
|
|
136
|
+
pkg.private = true;
|
|
137
|
+
pkg.type = 'module';
|
|
138
|
+
|
|
139
|
+
pkg.scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {};
|
|
140
|
+
for (const [key, value] of Object.entries(baseScripts)) {
|
|
141
|
+
if (!pkg.scripts[key]) pkg.scripts[key] = value;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
pkg.devDependencies = pkg.devDependencies && typeof pkg.devDependencies === 'object' ? pkg.devDependencies : {};
|
|
145
|
+
if (!pkg.devDependencies[devkitName]) {
|
|
146
|
+
pkg.devDependencies[devkitName] = devkitRange;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
writeJson(pkgPath, pkg);
|
|
150
|
+
return pkg;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function writeScaffoldConfig({ destDir, pluginDir = 'plugin', appId = '' }) {
|
|
154
|
+
const cfgPath = path.join(destDir, 'chatos.config.json');
|
|
155
|
+
const existing = isFile(cfgPath) ? readJson(cfgPath) : {};
|
|
156
|
+
const cfg = existing && typeof existing === 'object' ? existing : {};
|
|
157
|
+
cfg.pluginDir = pluginDir;
|
|
158
|
+
cfg.appId = appId;
|
|
159
|
+
writeJson(cfgPath, cfg);
|
|
160
|
+
return cfg;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function maybeReplaceTokensInFile(filePath, replacements) {
|
|
164
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
165
|
+
let next = raw;
|
|
166
|
+
for (const [key, value] of Object.entries(replacements || {})) {
|
|
167
|
+
next = next.split(key).join(String(value));
|
|
168
|
+
}
|
|
169
|
+
if (next !== raw) {
|
|
170
|
+
writeText(filePath, next);
|
|
171
|
+
}
|
|
172
|
+
}
|