@xcanwin/manyoyo 8.6.12 → 8.7.0
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/bin/manyoyo.js +12 -12
- package/lib/container-network.js +1 -0
- package/lib/name-template.js +52 -0
- package/lib/runtime-resolver.js +13 -1
- package/lib/web/index.html +84 -84
- package/lib/web/server.js +177 -7
- package/lib/web/storage.js +444 -0
- package/package.json +3 -1
package/bin/manyoyo.js
CHANGED
|
@@ -61,18 +61,14 @@ const {
|
|
|
61
61
|
formatServeLogValue,
|
|
62
62
|
getServeProcessSnapshot
|
|
63
63
|
} = require('../lib/serve-log');
|
|
64
|
+
const { formatNow, resolveQuickChatPath, expandTemplate } = require('../lib/name-template');
|
|
64
65
|
const { version: BIN_VERSION, imageVersion: IMAGE_VERSION_DEFAULT } = require('../package.json');
|
|
65
66
|
const IMAGE_VERSION_BASE = String(IMAGE_VERSION_DEFAULT || '1.0.0').split('-')[0];
|
|
66
67
|
const IMAGE_VERSION_HELP_EXAMPLE = IMAGE_VERSION_DEFAULT || `${IMAGE_VERSION_BASE}-common`;
|
|
67
68
|
|
|
68
|
-
//
|
|
69
|
+
// {now} 与默认容器名用的时间:年月日-时分秒(命令行与网页共用 lib/name-template.js)
|
|
69
70
|
function formatDate() {
|
|
70
|
-
|
|
71
|
-
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
72
|
-
const day = String(now.getDate()).padStart(2, '0');
|
|
73
|
-
const hour = String(now.getHours()).padStart(2, '0');
|
|
74
|
-
const minute = String(now.getMinutes()).padStart(2, '0');
|
|
75
|
-
return `${month}${day}-${hour}${minute}`;
|
|
71
|
+
return formatNow();
|
|
76
72
|
}
|
|
77
73
|
|
|
78
74
|
function detectCommandName() {
|
|
@@ -95,6 +91,7 @@ const CONFIG = {
|
|
|
95
91
|
// Default configuration
|
|
96
92
|
let CONTAINER_NAME = `my-${formatDate()}`;
|
|
97
93
|
let HOST_PATH = process.cwd();
|
|
94
|
+
let HOST_PATH_TEMPLATED = false;
|
|
98
95
|
let CONTAINER_PATH = HOST_PATH;
|
|
99
96
|
let IMAGE_NAME = "ghcr.io/xcanwin/manyoyo";
|
|
100
97
|
let IMAGE_VERSION = IMAGE_VERSION_DEFAULT || `${IMAGE_VERSION_BASE}-common`;
|
|
@@ -159,11 +156,7 @@ function normalizeCommandSuffix(suffix) {
|
|
|
159
156
|
}
|
|
160
157
|
|
|
161
158
|
function resolveContainerNameTemplate(name) {
|
|
162
|
-
|
|
163
|
-
return name;
|
|
164
|
-
}
|
|
165
|
-
const nowValue = formatDate();
|
|
166
|
-
return name.replace(/\{now\}|\$\{now\}/g, nowValue);
|
|
159
|
+
return expandTemplate(name, { now: formatDate() }).value;
|
|
167
160
|
}
|
|
168
161
|
|
|
169
162
|
function pickConfigValue(...values) {
|
|
@@ -1598,6 +1591,7 @@ https://github.com/xcanwin/manyoyo
|
|
|
1598
1591
|
isServerStopMode,
|
|
1599
1592
|
pickConfigValue,
|
|
1600
1593
|
resolveContainerNameTemplate,
|
|
1594
|
+
templateVars: { now: formatDate(), quickChatPath: resolveQuickChatPath(config) },
|
|
1601
1595
|
normalizeCommandSuffix,
|
|
1602
1596
|
normalizeJsonEnvMap,
|
|
1603
1597
|
normalizeCliEnvMap,
|
|
@@ -1608,6 +1602,7 @@ https://github.com/xcanwin/manyoyo
|
|
|
1608
1602
|
});
|
|
1609
1603
|
|
|
1610
1604
|
HOST_PATH = resolvedRuntime.hostPath;
|
|
1605
|
+
HOST_PATH_TEMPLATED = resolvedRuntime.hostPathTemplated === true;
|
|
1611
1606
|
CONTAINER_NAME = resolvedRuntime.containerName;
|
|
1612
1607
|
CONTAINER_PATH = resolvedRuntime.containerPath;
|
|
1613
1608
|
IMAGE_NAME = resolvedRuntime.imageName;
|
|
@@ -1827,6 +1822,7 @@ function createRuntimeContext(modeState = {}) {
|
|
|
1827
1822
|
return {
|
|
1828
1823
|
containerName: CONTAINER_NAME,
|
|
1829
1824
|
hostPath: HOST_PATH,
|
|
1825
|
+
hostPathTemplated: HOST_PATH_TEMPLATED,
|
|
1830
1826
|
containerPath: CONTAINER_PATH,
|
|
1831
1827
|
imageName: IMAGE_NAME,
|
|
1832
1828
|
imageVersion: IMAGE_VERSION,
|
|
@@ -1877,6 +1873,10 @@ function handleRemoveContainer(runtime) {
|
|
|
1877
1873
|
}
|
|
1878
1874
|
|
|
1879
1875
|
function validateHostPath(runtime) {
|
|
1876
|
+
// 目录是按模板({now} / {quickChatPath})生成的:此刻才建,只在真的要用它的时候建
|
|
1877
|
+
if (runtime.hostPathTemplated && !fs.existsSync(runtime.hostPath)) {
|
|
1878
|
+
fs.mkdirSync(runtime.hostPath, { recursive: true });
|
|
1879
|
+
}
|
|
1880
1880
|
if (!fs.existsSync(runtime.hostPath)) {
|
|
1881
1881
|
console.log(`${RED}⚠️ 错误: 宿主机路径不存在: ${runtime.hostPath}${NC}`);
|
|
1882
1882
|
process.exit(1);
|
package/lib/container-network.js
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// 配置里 containerName / hostPath / containerPath 的占位符,命令行与网页共用这一个展开函数:
|
|
7
|
+
// {now}(也认 ${now})→ 年月日-时分秒,如 20261024-230915
|
|
8
|
+
// {quickChatPath} → 快捷对话目录(serve.quickChat.path),没配置就是默认工作目录 ~/.manyoyo/work
|
|
9
|
+
const NOW_TOKEN = /\{now\}|\$\{now\}/g;
|
|
10
|
+
const QUICK_CHAT_PATH_TOKEN = /\{quickChatPath\}/g;
|
|
11
|
+
|
|
12
|
+
function formatNow(date = new Date()) {
|
|
13
|
+
const pad = value => String(value).padStart(2, '0');
|
|
14
|
+
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function expandHome(value, homeDir = os.homedir()) {
|
|
18
|
+
const text = String(value || '').trim();
|
|
19
|
+
if (text === '~') return homeDir;
|
|
20
|
+
if (text.startsWith('~/')) return path.join(homeDir, text.slice(2));
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 快捷对话目录的绝对路径(去掉末尾斜杠);没配置返回 defaultDir */
|
|
25
|
+
function resolveQuickChatPath(config, { homeDir = os.homedir(), defaultDir = path.join(os.homedir(), '.manyoyo', 'work') } = {}) {
|
|
26
|
+
const quickChat = config && config.serve && typeof config.serve === 'object' ? config.serve.quickChat : null;
|
|
27
|
+
const raw = quickChat && typeof quickChat.path === 'string' ? quickChat.path.trim() : '';
|
|
28
|
+
if (!raw) return defaultDir;
|
|
29
|
+
const resolved = path.resolve(expandHome(raw, homeDir));
|
|
30
|
+
return resolved;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 展开占位符。返回 { value, templated }:templated 表示原文里确实有占位符
|
|
35
|
+
* (调用方据此决定要不要替用户建目录:只有按模板生成的目录才自动建)。
|
|
36
|
+
*/
|
|
37
|
+
function expandTemplate(value, vars = {}) {
|
|
38
|
+
if (typeof value !== 'string') return { value, templated: false };
|
|
39
|
+
const now = vars.now === undefined ? formatNow() : vars.now;
|
|
40
|
+
let templated = false;
|
|
41
|
+
let result = value.replace(NOW_TOKEN, () => {
|
|
42
|
+
templated = true;
|
|
43
|
+
return now;
|
|
44
|
+
});
|
|
45
|
+
result = result.replace(QUICK_CHAT_PATH_TOKEN, () => {
|
|
46
|
+
templated = true;
|
|
47
|
+
return String(vars.quickChatPath || '').replace(/\/+$/, '');
|
|
48
|
+
});
|
|
49
|
+
return { value: result, templated };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { formatNow, expandHome, resolveQuickChatPath, expandTemplate };
|
package/lib/runtime-resolver.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const { expandTemplate } = require('./name-template');
|
|
4
5
|
|
|
5
6
|
function toArray(value) {
|
|
6
7
|
return Array.isArray(value) ? value : (value ? [value] : []);
|
|
@@ -17,6 +18,8 @@ function resolveRuntimeConfig(options = {}) {
|
|
|
17
18
|
const envVars = params.envVars || process.env;
|
|
18
19
|
const pickConfigValue = params.pickConfigValue;
|
|
19
20
|
const resolveContainerNameTemplate = params.resolveContainerNameTemplate;
|
|
21
|
+
// 占位符变量({now} / {quickChatPath}):一次解析里只取一个时间,容器名和目录用同一个时间戳
|
|
22
|
+
const templateVars = params.templateVars || null;
|
|
20
23
|
const normalizeCommandSuffix = params.normalizeCommandSuffix;
|
|
21
24
|
const normalizeJsonEnvMap = params.normalizeJsonEnvMap;
|
|
22
25
|
const normalizeCliEnvMap = params.normalizeCliEnvMap;
|
|
@@ -29,18 +32,25 @@ function resolveRuntimeConfig(options = {}) {
|
|
|
29
32
|
const isServerStopMode = params.isServerStopMode === true;
|
|
30
33
|
|
|
31
34
|
let hostPath = pickConfigValue(cliOptions.hostPath, runConfig.hostPath, globalConfig.hostPath, defaults.hostPath) || defaults.hostPath;
|
|
35
|
+
let hostPathTemplated = false;
|
|
36
|
+
if (templateVars) {
|
|
37
|
+
const expanded = expandTemplate(hostPath, templateVars);
|
|
38
|
+
hostPath = expanded.value;
|
|
39
|
+
hostPathTemplated = expanded.templated;
|
|
40
|
+
}
|
|
32
41
|
let containerName = defaults.containerName;
|
|
33
42
|
const mergedContainerName = pickConfigValue(cliOptions.contName, runConfig.containerName, globalConfig.containerName);
|
|
34
43
|
if (mergedContainerName) {
|
|
35
44
|
containerName = mergedContainerName;
|
|
36
45
|
}
|
|
37
|
-
containerName = resolveContainerNameTemplate(containerName);
|
|
46
|
+
containerName = templateVars ? expandTemplate(containerName, templateVars).value : resolveContainerNameTemplate(containerName);
|
|
38
47
|
|
|
39
48
|
let containerPath = defaults.containerPath;
|
|
40
49
|
const mergedContainerPath = pickConfigValue(cliOptions.contPath, runConfig.containerPath, globalConfig.containerPath);
|
|
41
50
|
if (mergedContainerPath) {
|
|
42
51
|
containerPath = mergedContainerPath;
|
|
43
52
|
}
|
|
53
|
+
if (templateVars) containerPath = expandTemplate(containerPath, templateVars).value;
|
|
44
54
|
|
|
45
55
|
const imageName = pickConfigValue(cliOptions.imageName, runConfig.imageName, globalConfig.imageName, defaults.imageName) || defaults.imageName;
|
|
46
56
|
const imageVersion = pickConfigValue(cliOptions.imageVer, runConfig.imageVersion, globalConfig.imageVersion, defaults.imageVersion) || defaults.imageVersion;
|
|
@@ -182,6 +192,8 @@ function resolveRuntimeConfig(options = {}) {
|
|
|
182
192
|
|
|
183
193
|
return {
|
|
184
194
|
hostPath,
|
|
195
|
+
// 宿主机目录是按模板({now} / {quickChatPath})生成的:真正创建容器时才替用户建目录
|
|
196
|
+
hostPathTemplated,
|
|
185
197
|
containerName,
|
|
186
198
|
containerPath,
|
|
187
199
|
imageName,
|