@aalis/runtime 0.15.0 → 0.16.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/README.md +11 -2
- package/dist/config-store.d.ts +49 -12
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +419 -58
- package/dist/config-store.js.map +1 -1
- package/dist/config-sync.d.ts +19 -8
- package/dist/config-sync.d.ts.map +1 -1
- package/dist/config-sync.js +81 -58
- package/dist/config-sync.js.map +1 -1
- package/dist/console-sink.d.ts +1 -1
- package/dist/file-logger.d.ts +4 -4
- package/dist/file-logger.d.ts.map +1 -1
- package/dist/file-logger.js +6 -5
- package/dist/file-logger.js.map +1 -1
- package/dist/home.d.ts +64 -0
- package/dist/home.d.ts.map +1 -0
- package/dist/home.js +229 -0
- package/dist/home.js.map +1 -0
- package/dist/host-logs.d.ts +9 -0
- package/dist/host-logs.d.ts.map +1 -0
- package/dist/host-logs.js +104 -0
- package/dist/host-logs.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/instance-lock.d.ts +56 -0
- package/dist/instance-lock.d.ts.map +1 -0
- package/dist/instance-lock.js +281 -0
- package/dist/instance-lock.js.map +1 -0
- package/dist/node-modules-loader.d.ts +2 -2
- package/dist/node-modules-loader.d.ts.map +1 -1
- package/dist/node-modules-loader.js +8 -4
- package/dist/node-modules-loader.js.map +1 -1
- package/dist/plugin-discovery.d.ts +9 -4
- package/dist/plugin-discovery.d.ts.map +1 -1
- package/dist/plugin-discovery.js +28 -16
- package/dist/plugin-discovery.js.map +1 -1
- package/dist/providers.d.ts +38 -13
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +92 -216
- package/dist/providers.js.map +1 -1
- package/dist/secrets.d.ts +44 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +104 -0
- package/dist/secrets.js.map +1 -0
- package/dist/start.d.ts +19 -12
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +166 -43
- package/dist/start.js.map +1 -1
- package/dist/yaml-file.d.ts +35 -0
- package/dist/yaml-file.d.ts.map +1 -0
- package/dist/yaml-file.js +213 -0
- package/dist/yaml-file.js.map +1 -0
- package/package.json +13 -7
package/dist/start.d.ts
CHANGED
|
@@ -2,11 +2,17 @@ import { App } from '@aalis/core';
|
|
|
2
2
|
import { type ConfigSyncOptions } from './config-sync.js';
|
|
3
3
|
import { type PluginLoader } from './plugin-discovery.js';
|
|
4
4
|
export interface StartAalisOptions {
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 安装集目录(package.json 与 node_modules 所在,源码形态另有 packages/),必须是绝对路径。
|
|
7
|
+
* 源码形态的入口传仓库根,脚手架生成的入口传项目目录。
|
|
8
|
+
*/
|
|
9
|
+
appDir: string;
|
|
10
|
+
/**
|
|
11
|
+
* 宿主代码给的兜底家目录,必须是绝对路径。优先级最低:`--home`、`AALIS_HOME`、`--profile` / `AALIS_PROFILE`
|
|
12
|
+
* 都没给时才用它,再没有就是 `~/.aalis`。
|
|
13
|
+
*/
|
|
14
|
+
defaultHome?: string;
|
|
15
|
+
/** 插件加载器,默认 createNodeModulesPluginLoader(安装集);monorepo 传 createFsPluginLoader(<仓库>/packages) */
|
|
10
16
|
pluginLoader?: PluginLoader;
|
|
11
17
|
/**
|
|
12
18
|
* 彩色控制台日志,默认 true(独立部署即有全量级别)。webui-only/嵌入式/测试夹具可传 false:
|
|
@@ -15,18 +21,19 @@ export interface StartAalisOptions {
|
|
|
15
21
|
*/
|
|
16
22
|
consoleSink?: boolean;
|
|
17
23
|
/**
|
|
18
|
-
*
|
|
24
|
+
* 文件日志写到家目录的 `logs/latest.log`,默认开(WebUI 与 CLI 的日志页尾读这个文件)。
|
|
19
25
|
* 启动时先把上一轮的日志改名保留(编号插在扩展名前:latest.1.log),更早的依次后移,保留 5 份。
|
|
20
|
-
*
|
|
26
|
+
* 子命令模式一律不写文件日志。
|
|
21
27
|
*/
|
|
22
|
-
fileLog?: boolean
|
|
28
|
+
fileLog?: boolean;
|
|
23
29
|
/** 退出时复原终端 raw-mode/alt-screen,默认 true */
|
|
24
30
|
terminalRestore?: boolean;
|
|
25
31
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
32
|
+
* 命令行参数,默认 `process.argv.slice(2)`。开头的宿主参数先取走:`--home <目录>`、`--profile <名>`、
|
|
33
|
+
* `--init`(初始化家目录后退出)、`--service`(由服务管理器拉起:实例锁被占时以 0 退出,重启交给服务管理器)。余下的非空即子命令模式:命中执行后退出,未命中报错退出(exit 2),不进守护进程。
|
|
34
|
+
* 宿主自管命令行时传显式数组(`[]` 即守护进程)。
|
|
28
35
|
*/
|
|
29
|
-
|
|
36
|
+
argv?: string[];
|
|
30
37
|
/** 覆盖 dev/prod 判定,默认按 NODE_ENV !== 'production' */
|
|
31
38
|
devMode?: boolean;
|
|
32
39
|
/**
|
|
@@ -42,5 +49,5 @@ export interface StartAalisOptions {
|
|
|
42
49
|
* 生命周期不变量:① `consoleHandle.bindEvents` 必须在 `new App` 之后(之前没有事件总线);
|
|
43
50
|
* ② 子命令短路必须在 `app.start` 之前。
|
|
44
51
|
*/
|
|
45
|
-
export declare function startAalis(opts
|
|
52
|
+
export declare function startAalis(opts: StartAalisOptions): Promise<App>;
|
|
46
53
|
//# sourceMappingURL=start.d.ts.map
|
package/dist/start.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,GAAG,EAAoD,MAAM,aAAa,CAAC;AAGpF,OAAO,EAAE,KAAK,iBAAiB,EAAgD,MAAM,kBAAkB,CAAC;AAgBxG,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAmCjF,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAkBD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,CAuQtE"}
|
package/dist/start.js
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
1
2
|
import { createRequire } from 'node:module';
|
|
3
|
+
import { join, normalize } from 'node:path';
|
|
4
|
+
import { describeUnmet, unmetDependencies } from '@aalis/api-doctor';
|
|
5
|
+
import { hostLogs } from '@aalis/api-host-logs';
|
|
6
|
+
import { hostPaths } from '@aalis/api-host-paths';
|
|
2
7
|
import { pluginSource } from '@aalis/api-plugin-source';
|
|
3
|
-
import { App, events, provide, services } from '@aalis/core';
|
|
8
|
+
import { App, events, LogHub, provide, services } from '@aalis/core';
|
|
4
9
|
import { installBootstrapBuffer } from './bootstrap-buffer.js';
|
|
5
10
|
import { createConfigStore, installHostConfig } from './config-store.js';
|
|
6
11
|
import { installConfigHotReload, withPluginConfigSync } from './config-sync.js';
|
|
7
12
|
import { installConsoleSink } from './console-sink.js';
|
|
8
|
-
import { appendCrashLog,
|
|
13
|
+
import { appendCrashLog, setupFileLogger } from './file-logger.js';
|
|
14
|
+
import { derivePaths, HomeArgsError, initHome, isFullyAbsolute, isHomeInitialized, parseHostArgs, profileOfRealHome, resolveHome, } from './home.js';
|
|
15
|
+
import { createHostLogs } from './host-logs.js';
|
|
16
|
+
import { acquireInstanceLock } from './instance-lock.js';
|
|
9
17
|
import { createNodeModulesPluginLoader } from './node-modules-loader.js';
|
|
10
18
|
import { createPluginDiscovery } from './plugin-discovery.js';
|
|
11
|
-
import { createFsYamlConfigProvider, createProcessRespawnStrategy, READY_MESSAGE } from './providers.js';
|
|
19
|
+
import { createFsSecretsProvider, createFsYamlConfigProvider, createProcessRespawnStrategy, createServiceRestartStrategy, READY_MESSAGE, } from './providers.js';
|
|
12
20
|
import { tryDispatchSubcommand } from './subcommand.js';
|
|
13
21
|
import { installTerminalStateRestorer } from './terminal.js';
|
|
22
|
+
// ============================================================
|
|
23
|
+
// startAalis —— Node 宿主一行启动(独立部署与 monorepo 自托管共用)
|
|
24
|
+
// ============================================================
|
|
25
|
+
//
|
|
26
|
+
// 独立部署(scaffold 生成的 index.mjs):
|
|
27
|
+
// import { startAalis } from '@aalis/runtime';
|
|
28
|
+
// startAalis({ appDir: here, defaultHome: join(here, 'home') }); // 从 node_modules 加载插件 + 默认开控制台/文件日志
|
|
29
|
+
//
|
|
30
|
+
// monorepo 自托管(src/index.ts)传仓库根与 monorepo 风味的加载器:
|
|
31
|
+
// startAalis({ appDir, pluginLoader: createFsPluginLoader(join(appDir, 'packages')) });
|
|
32
|
+
//
|
|
33
|
+
// 命令行开头的宿主参数(`--home`、`--profile`、`--init`、`--service`)先取走,用来定家目录与运行形态;余下的 argv 非空即子命令
|
|
34
|
+
// 模式——`node index.mjs <name> [args]` ≡ 聊天里的 `/<name> args`,命中即执行并退出,未命中报错退出
|
|
35
|
+
// (exit 2),**两种情况都不进守护进程**。宿主自己解析命令行时传 `argv: []` 或显式数组。
|
|
36
|
+
//
|
|
37
|
+
// 家目录没初始化就拒绝启动,只提示怎样初始化:绝不在找不到家目录时静默起一个空的家。
|
|
38
|
+
//
|
|
39
|
+
// 子命令进程是与守护进程零通信的一次性实例,因此不装文件日志(会把守护进程正在写的 latest.log 轮转走)、
|
|
40
|
+
// 不注入重启策略(`restart` 会 spawn 出 argv 仍带 restart 的 detached 子进程,无限连环——实测)。
|
|
41
|
+
//
|
|
42
|
+
// 宿主的「I/O 那一层」(日志 sink / 终端复原 / 子命令分发)全在本包,core 只产生 LogEntry。
|
|
43
|
+
const APP_DIR_EXAMPLE = 'startAalis({ appDir: dirname(fileURLToPath(import.meta.url)) })';
|
|
44
|
+
/** respawn 时把实例锁的交接令牌交给新进程的环境变量 */
|
|
45
|
+
const HANDOFF_ENV = 'AALIS_LOCK_HANDOFF';
|
|
14
46
|
/**
|
|
15
47
|
* 读取 @aalis/core 的实际安装版本,供 App 启动 banner 展示。
|
|
16
48
|
* core 的 `exports` 开放了 `./package.json` 子路径,可 require;解析失败则返回 undefined,
|
|
@@ -31,9 +63,97 @@ function readCoreVersion() {
|
|
|
31
63
|
* 生命周期不变量:① `consoleHandle.bindEvents` 必须在 `new App` 之后(之前没有事件总线);
|
|
32
64
|
* ② 子命令短路必须在 `app.start` 之前。
|
|
33
65
|
*/
|
|
34
|
-
export async function startAalis(opts
|
|
66
|
+
export async function startAalis(opts) {
|
|
67
|
+
// JS 入口可能一个参数都不传:在动家目录之前停下,不落到缺省的家目录
|
|
68
|
+
if (typeof opts?.appDir !== 'string') {
|
|
69
|
+
console.error(`[aalis] startAalis 缺少 appDir:把入口文件所在的目录作为 appDir 传入,例如 ${APP_DIR_EXAMPLE}。`);
|
|
70
|
+
process.exit(2);
|
|
71
|
+
}
|
|
35
72
|
const { consoleSink = true, fileLog = true, terminalRestore = true } = opts;
|
|
36
|
-
|
|
73
|
+
// ── 家目录:在任何日志与文件写入之前定下来 ──
|
|
74
|
+
// 命令行与环境变量里写的相对路径按启动时的 cwd 解析一次:那是用户在 shell 里的意图。
|
|
75
|
+
// 启动目录可能已被删除,这时 process.cwd() 会抛错;只有遇到相对路径才需要它。
|
|
76
|
+
let startCwd;
|
|
77
|
+
try {
|
|
78
|
+
startCwd = process.cwd();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
startCwd = undefined;
|
|
82
|
+
}
|
|
83
|
+
let hostArgs;
|
|
84
|
+
let home;
|
|
85
|
+
try {
|
|
86
|
+
hostArgs = parseHostArgs(opts.argv ?? process.argv.slice(2));
|
|
87
|
+
home = resolveHome({ args: hostArgs, env: process.env, defaultHome: opts.defaultHome, cwd: startCwd });
|
|
88
|
+
if (!isFullyAbsolute(opts.appDir)) {
|
|
89
|
+
throw new HomeArgsError(`appDir 必须是绝对路径:${opts.appDir}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (!(err instanceof HomeArgsError))
|
|
94
|
+
throw err;
|
|
95
|
+
console.error(`[aalis] ${err.message}`);
|
|
96
|
+
process.exit(2);
|
|
97
|
+
}
|
|
98
|
+
const appDir = normalize(opts.appDir);
|
|
99
|
+
const declared = derivePaths(home.home, appDir, home.profile);
|
|
100
|
+
if (hostArgs.init) {
|
|
101
|
+
try {
|
|
102
|
+
initHome(declared);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
console.error(`[aalis] ${err instanceof Error ? err.message : String(err)}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
console.log(`[aalis] 已初始化家目录:${declared.home}`);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
if (!isHomeInitialized(declared)) {
|
|
112
|
+
console.error(`[aalis] 家目录还没有初始化:${declared.home}\n[aalis] 在启动命令里、子命令之前加上 --init 运行一次即可初始化。`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
// 交给插件的家目录是真实路径:符号链接、大小写与末尾分隔符不同的写法指向同一个目录时,插件拿到的是同一个路径
|
|
116
|
+
// (记忆库按它记归属)。profile 按写法认不出时按真实路径再认一次,派生的库名才跟着同一个目录走
|
|
117
|
+
const realHome = realpathSync.native(declared.home);
|
|
118
|
+
const paths = derivePaths(realHome, appDir, home.profile ?? profileOfRealHome(realHome));
|
|
119
|
+
// 默认加载器从安装集的 package.json 找插件:缺了它会一个插件都不加载、照样起来,只留一行「发现 0 个插件」
|
|
120
|
+
if (!opts.pluginLoader && !existsSync(join(paths.app, 'package.json'))) {
|
|
121
|
+
console.error(`[aalis] 安装集 ${paths.app} 里没有 package.json,找不到要加载的插件。\n` +
|
|
122
|
+
`[aalis] appDir 要指向入口文件所在、带 package.json 的目录,例如 ${APP_DIR_EXAMPLE}。`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
const subcommands = hostArgs.rest;
|
|
126
|
+
// ── 实例锁:同一家目录只许一个守护进程。要在写日志之前取到,否则第二个实例一起来就把正在写的 latest.log 轮转走 ──
|
|
127
|
+
// 交接令牌读到就从环境里删掉,不让插件起的子进程与侧车继承它。子命令进程不取锁(它们以后经 socket 连实例)。
|
|
128
|
+
const handoffToken = process.env[HANDOFF_ENV];
|
|
129
|
+
delete process.env[HANDOFF_ENV];
|
|
130
|
+
let instanceLock;
|
|
131
|
+
if (subcommands.length === 0) {
|
|
132
|
+
let acquired;
|
|
133
|
+
try {
|
|
134
|
+
acquired = acquireInstanceLock(paths.run, { handoffToken });
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(`[aalis] 无法在 ${paths.run} 取实例锁:${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
if (!acquired.ok) {
|
|
141
|
+
const { holder, file } = acquired;
|
|
142
|
+
const occupied = `[aalis] 家目录 ${paths.home} 已有实例在运行:pid ${holder.pid}(锁文件 ${file})。\n` +
|
|
143
|
+
'[aalis] 确认没有实例在运行时(例如进程被强制结束、而本机查不了进程启动时间,或容器被强杀后在同一次开机内重启),' +
|
|
144
|
+
'删掉这个锁文件再启动。';
|
|
145
|
+
if (hostArgs.service) {
|
|
146
|
+
// 服务形态按正常退出:配合 KeepAlive={SuccessfulExit=false},不会被服务管理器反复拉起再抢锁
|
|
147
|
+
console.error(`${occupied}\n[aalis] 以服务形态启动,按正常退出处理。`);
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
console.error(occupied);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
instanceLock = acquired.lock;
|
|
154
|
+
const lock = acquired.lock;
|
|
155
|
+
process.on('exit', () => lock.release());
|
|
156
|
+
}
|
|
37
157
|
// ── 最早期:任何日志之前先装 bootstrap buffer,再装 terminal / console sink ──
|
|
38
158
|
const bootstrap = installBootstrapBuffer();
|
|
39
159
|
if (terminalRestore)
|
|
@@ -47,7 +167,7 @@ export async function startAalis(opts = {}) {
|
|
|
47
167
|
: installConsoleSink({ target: 'stderr', minLevel: 'warn' });
|
|
48
168
|
// 子命令进程不写文件日志:setupFileLogger 会把 latest.log 轮转走,而守护进程可能正在写它(webui 历史日志
|
|
49
169
|
// 以该文件为单一数据源)。
|
|
50
|
-
const fileLogTarget = fileLog
|
|
170
|
+
const fileLogTarget = fileLog && !subcommandMode ? join(paths.logs, 'latest.log') : undefined;
|
|
51
171
|
// ── fatal handler(覆盖整个 async 启动过程)──
|
|
52
172
|
let fileLogger;
|
|
53
173
|
let handlingFatal = false;
|
|
@@ -73,9 +193,20 @@ export async function startAalis(opts = {}) {
|
|
|
73
193
|
fileLogger = await setupFileLogger(fileLogTarget);
|
|
74
194
|
// sink 全部装好;bootstrap buffer 完成使命,解除订阅并释放内存。
|
|
75
195
|
bootstrap.dispose();
|
|
76
|
-
// ── 组装 App:YAML
|
|
77
|
-
|
|
78
|
-
const
|
|
196
|
+
// ── 组装 App:YAML 配置文档与密钥文件、按 loader 发现插件、用 spawn 重启 ──
|
|
197
|
+
// 配置或密钥文件读不进来(写坏了、读不了)是环境问题:一行报错退出,不带调用栈(消息不含文件内容)
|
|
198
|
+
const loadOrExit = (load) => {
|
|
199
|
+
try {
|
|
200
|
+
return load();
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
console.error(`[aalis] ${err instanceof Error ? err.message : String(err)}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const { config, provider } = loadOrExit(() => createFsYamlConfigProvider(paths.config));
|
|
208
|
+
const secrets = loadOrExit(() => createFsSecretsProvider(paths.secrets));
|
|
209
|
+
const store = createConfigStore(config, provider, secrets);
|
|
79
210
|
// core 的慢操作阈值:缺省(含 YAML 空键解出的 null)交给 core 默认;不是非负有限数的写法告警后同样交给默认
|
|
80
211
|
// (启动时读取,改了要重启)
|
|
81
212
|
const slowThresholdMs = store.get('slowThresholdMs') ?? undefined;
|
|
@@ -88,11 +219,17 @@ export async function startAalis(opts = {}) {
|
|
|
88
219
|
// 子命令进程没有重启能力,不注入策略:`app.restart()` 按 core 语义抛「不可用」,指令层折成失败文案。
|
|
89
220
|
// 注入的话,`restart` 子命令在 app.stop 超过策略 500ms 等待时会 spawn 一个 argv 仍带 restart 的
|
|
90
221
|
// detached 子进程,新进程再命中 restart……无限连环(实测 5 代,Ctrl+C 打不到)。
|
|
91
|
-
restartStrategy: subcommandMode
|
|
222
|
+
restartStrategy: subcommandMode
|
|
223
|
+
? undefined
|
|
224
|
+
: hostArgs.service
|
|
225
|
+
? createServiceRestartStrategy()
|
|
226
|
+
: createProcessRespawnStrategy({
|
|
227
|
+
appDir: paths.app,
|
|
228
|
+
cwd: startCwd ?? paths.home,
|
|
229
|
+
...(instanceLock ? { env: { [HANDOFF_ENV]: instanceLock.handoffToken } } : {}),
|
|
230
|
+
}),
|
|
92
231
|
// 宿主层决定 dev/prod;core 不读 process.env
|
|
93
232
|
devMode: opts.devMode ?? process.env.NODE_ENV !== 'production',
|
|
94
|
-
// 宿主注入时钟:日志时间戳的权威时间来源在此层,core 逻辑不主动取墙上时间。
|
|
95
|
-
now: () => new Date(),
|
|
96
233
|
// 宿主读取 @aalis/core 的实际版本注入启动 banner——core 环境无关、不自读 package.json。
|
|
97
234
|
version: readCoreVersion(),
|
|
98
235
|
});
|
|
@@ -101,16 +238,19 @@ export async function startAalis(opts = {}) {
|
|
|
101
238
|
const received = typeof slowThresholdMs === 'number' ? String(slowThresholdMs) : JSON.stringify(slowThresholdMs);
|
|
102
239
|
app.logger.warn(`配置项 slowThresholdMs 应为不小于 0 的有限数字(毫秒),收到 ${received},按默认值处理`);
|
|
103
240
|
}
|
|
104
|
-
// 配置文档先于任何插件接上:文档里的服务偏好要在全部提供者上线前生效
|
|
105
|
-
installHostConfig(app, store, opts.configSync);
|
|
106
|
-
// 默认值从 configSchema 派生(唯一声明来源),导入定义后、登记前深合并进文档;登记时原样交给 core
|
|
107
|
-
const configLoader = withPluginConfigSync(opts.pluginLoader ?? createNodeModulesPluginLoader(opts.projectDir), app, store, opts.configSync);
|
|
108
241
|
// 宿主的根绑定:事件订阅、服务查询与宿主服务登记都经它,随 App 停止撤回
|
|
109
242
|
const host = app.bind({ events, provide, services });
|
|
243
|
+
// 宿主路径、日志与配置文档先于任何插件接上:插件激活时就要用到,文档里的服务偏好要在全部提供者上线前生效。
|
|
244
|
+
// 日志接 App 写的那个中枢(runtime 不注入,即默认中枢),历史读本轮的文件日志
|
|
245
|
+
host.provide.use(hostPaths, paths, { exclusive: true });
|
|
246
|
+
host.provide.use(hostLogs, createHostLogs(LogHub.default, fileLogger, app.logger), { exclusive: true });
|
|
247
|
+
installHostConfig(app, store, opts.configSync);
|
|
248
|
+
// 默认值从 configSchema 派生(唯一声明来源),导入定义后、登记前深合并进文档;登记时原样交给 core
|
|
249
|
+
const configLoader = withPluginConfigSync(opts.pluginLoader ?? createNodeModulesPluginLoader(paths.app), app, store, opts.configSync);
|
|
110
250
|
const discovery = createPluginDiscovery(app, configLoader.loader, store);
|
|
111
|
-
host.provide(pluginSource, { rescan: () => discovery.rescan() }, { exclusive: true });
|
|
251
|
+
host.provide.use(pluginSource, { rescan: () => discovery.rescan() }, { exclusive: true });
|
|
112
252
|
// 不变量①:App 构造完成后再让 sink 监听终端归属事件——此前没有事件总线可订阅。
|
|
113
|
-
consoleHandle.bindEvents(host.events);
|
|
253
|
+
consoleHandle.bindEvents(host.events.use);
|
|
114
254
|
try {
|
|
115
255
|
// 每个定义交给 Core 前已经规范化;首次 apply 与配置快照用同一份字段。
|
|
116
256
|
await discovery.loadAll();
|
|
@@ -124,7 +264,7 @@ export async function startAalis(opts = {}) {
|
|
|
124
264
|
// 两种情况都干净退出、**绝不进守护进程**——打错的命令名若照常起守护,就是与运行中实例并存的
|
|
125
265
|
// 第二个实例(同时连 onebot、每条消息回两遍)。与具体命令解耦——各插件自行注册命令。
|
|
126
266
|
if (subcommandMode) {
|
|
127
|
-
const exitCode = await tryDispatchSubcommand(host.services, subcommands);
|
|
267
|
+
const exitCode = await tryDispatchSubcommand(host.services.use, subcommands);
|
|
128
268
|
await app.stop();
|
|
129
269
|
await new Promise(r => setImmediate(r));
|
|
130
270
|
process.exit(exitCode);
|
|
@@ -135,30 +275,13 @@ export async function startAalis(opts = {}) {
|
|
|
135
275
|
// 典型链条:A 缺 apiKey 激活失败(有 ERROR)→ 依赖 A 所提供服务的 B 静默挂起,
|
|
136
276
|
// 用户只看到 A 的报错,会以为「只是 A 不可用」,不知道 B 也一并废了
|
|
137
277
|
// (实例:embedding-openai 缺 key → memory-vector 永远不激活,向量记忆整个不工作)。
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
const missing = [];
|
|
146
|
-
const waiting = new Set();
|
|
147
|
-
for (const svc of p.requiredServices ?? []) {
|
|
148
|
-
if (host.services.inspect(svc).length > 0)
|
|
149
|
-
continue;
|
|
150
|
-
const provider = activating.find(s => s.provides?.includes(svc));
|
|
151
|
-
if (provider)
|
|
152
|
-
waiting.add(provider.instanceId);
|
|
153
|
-
else
|
|
154
|
-
missing.push(svc);
|
|
155
|
-
}
|
|
156
|
-
const reasons = [
|
|
157
|
-
...(missing.length > 0 ? [`缺少服务: ${missing.join('、')}`] : []),
|
|
158
|
-
...[...waiting].map(id => `等待 ${id} 激活完成`),
|
|
159
|
-
];
|
|
160
|
-
if (reasons.length > 0)
|
|
161
|
-
app.logger.warn(`插件 "${p.instanceId}" 依赖未满足,未激活(${reasons.join(';')})`);
|
|
278
|
+
// 缺的沿依赖链追到根(失败、停用、没有提供者、成环)报 warn;只是在等提供者启动(含转入后台的慢激活)的报 info。
|
|
279
|
+
// 判据与写法同 plugin-doctor、WebUI 与终端界面(api-doctor 的 unmetDependencies)
|
|
280
|
+
for (const [instance, unmet] of unmetDependencies(app.plugins.getStatus(), host.services.use.names())) {
|
|
281
|
+
if (unmet.lacking.length > 0)
|
|
282
|
+
app.logger.warn(`插件 "${instance}" 依赖未满足,未激活:${describeUnmet(unmet)}`);
|
|
283
|
+
else if (unmet.waiting.length > 0)
|
|
284
|
+
app.logger.info(`插件 "${instance}" 在等依赖启动:${describeUnmet(unmet)}`);
|
|
162
285
|
}
|
|
163
286
|
// 向重启我们的父进程回报「起来了」。父进程据此决定放手退出,还是判定本次
|
|
164
287
|
// 更新失败并回滚(见 providers.ts 的 createProcessRespawnStrategy)。
|
package/dist/start.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Logger } from '@aalis/core';
|
|
2
|
+
interface YamlFileOptions<T> {
|
|
3
|
+
/** 报错与日志里对文件的称呼,如「配置文件」「密钥文件」 */
|
|
4
|
+
label: string;
|
|
5
|
+
logger: Logger;
|
|
6
|
+
serialize(value: T): string;
|
|
7
|
+
/** 文件不存在时的初始值 */
|
|
8
|
+
missing(): T;
|
|
9
|
+
/**
|
|
10
|
+
* 原子写的权限位。给定时临时文件一律以它创建,不沿用原文件的权限(密钥文件用 0o600);
|
|
11
|
+
* 不给时沿用原文件的权限位,没有原文件时用 0o600
|
|
12
|
+
*/
|
|
13
|
+
mode?: number;
|
|
14
|
+
/**
|
|
15
|
+
* 运行中改成空文件(或只剩注释)时按 {@link missing} 的值投递。缺省拒收:主配置变空多是写坏了,
|
|
16
|
+
* 放行会让全部插件按默认值重载;密钥文件清空是合法状态
|
|
17
|
+
*/
|
|
18
|
+
emptyAsMissing?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface YamlFile<T> {
|
|
21
|
+
/** 装载时读到的内容 */
|
|
22
|
+
initial: T;
|
|
23
|
+
/**
|
|
24
|
+
* 原子写回。写前比对盘上内容:有尚未生效的外部修改时拒写,抛 {@link ConfigSaveRefusedError}(只带路径)
|
|
25
|
+
*/
|
|
26
|
+
save(value: T): void;
|
|
27
|
+
/**
|
|
28
|
+
* 监听文件所在目录(按文件名过滤)+ 300ms 去抖,按与上次读入或写出的内容比对去重(自写回不触发);
|
|
29
|
+
* 拒写之后的下一次变更不去重;武装后立即对账一次。返回退订
|
|
30
|
+
*/
|
|
31
|
+
watch(onChange: (next: T) => void): () => void;
|
|
32
|
+
}
|
|
33
|
+
export declare function openYamlFile<T extends object>(absPath: string, opts: YamlFileOptions<T>): YamlFile<T>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=yaml-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-file.d.ts","sourceRoot":"","sources":["../src/yaml-file.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAS1C,UAAU,eAAe,CAAC,CAAC;IACzB,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC;IAC5B,iBAAiB;IACjB,OAAO,IAAI,CAAC,CAAC;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,QAAQ,CAAC,CAAC;IAClB,eAAe;IACf,OAAO,EAAE,CAAC,CAAC;IACX;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAChD;AAkCD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAmKrG"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { chmodSync, existsSync, watch as fsWatch, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { basename, dirname } from 'node:path';
|
|
4
|
+
import { ConfigSaveRefusedError } from '@aalis/api-host-config';
|
|
5
|
+
import { parseDocument } from 'yaml';
|
|
6
|
+
/**
|
|
7
|
+
* yaml 解析错误的可公开描述:只给错误码与行列。yaml 的报错 message 可能带原文(例如
|
|
8
|
+
* `Unexpected ${token.source} in …`、block scalar 头部的多余字符),配置与密钥文件的原文可能就是密钥,
|
|
9
|
+
* 随日志进 latest.log 与 WebUI 日志页就是泄漏。
|
|
10
|
+
*/
|
|
11
|
+
function describeYamlError(err) {
|
|
12
|
+
const e = err;
|
|
13
|
+
const code = typeof e?.code === 'string' ? e.code : '语法错误';
|
|
14
|
+
const at = e?.linePos?.[0];
|
|
15
|
+
return at ? `${code}(第 ${at.line} 行第 ${at.col} 列)` : code;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 解析 YAML 文本:错误抛出,警告(例如把 `!xxx` 当成标签、值变成空)记一行错误码与行列。不用 yaml 的 parse():
|
|
19
|
+
* 它把警告连同原文经 process.emitWarning 打到 stderr。toJS() 遇到以集合作键时同样会这样打(不进 doc.warnings),
|
|
20
|
+
* logLevel 设为 error 关掉
|
|
21
|
+
*/
|
|
22
|
+
function parseYamlText(text, label, logger) {
|
|
23
|
+
const doc = parseDocument(text, { logLevel: 'error' });
|
|
24
|
+
if (doc.errors.length > 0)
|
|
25
|
+
throw doc.errors[0];
|
|
26
|
+
for (const warning of doc.warnings)
|
|
27
|
+
logger.warn(`${label}有可疑写法:${describeYamlError(warning)}`);
|
|
28
|
+
return doc.toJS();
|
|
29
|
+
}
|
|
30
|
+
/** 值必须是纯映射:裸标量、数组会被下游按 Object.entries 摊开写回垃圾 */
|
|
31
|
+
function describeNonMapping(v) {
|
|
32
|
+
if (v === null)
|
|
33
|
+
return 'empty';
|
|
34
|
+
if (Array.isArray(v))
|
|
35
|
+
return 'array';
|
|
36
|
+
if (typeof v !== 'object')
|
|
37
|
+
return typeof v;
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
export function openYamlFile(absPath, opts) {
|
|
41
|
+
const { label, logger } = opts;
|
|
42
|
+
const dataDir = dirname(absPath);
|
|
43
|
+
let raw = null;
|
|
44
|
+
// 拒写之后调用方手里的文档(常连同运行态)已领先于文件:下一次文件变更不论内容是否等于 raw 都要投递,
|
|
45
|
+
// 让宿主按文件重新对账。否则把文件原样改回的「修好」会被当成空变更跳过,文档与运行态从此偏离文件,
|
|
46
|
+
// 下一次成功保存还会把被拒的改动写进去。
|
|
47
|
+
let reconcilePending = false;
|
|
48
|
+
let watcher = null;
|
|
49
|
+
let debounceTimer = null;
|
|
50
|
+
function load() {
|
|
51
|
+
if (!existsSync(absPath)) {
|
|
52
|
+
raw = null;
|
|
53
|
+
return opts.missing();
|
|
54
|
+
}
|
|
55
|
+
raw = readFileSync(absPath, 'utf-8');
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = parseYamlText(raw, label, logger) ?? {};
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
// 启动时语法错误照样响亮拒启,消息里只有错误码与行列(崩溃日志同为暴露面)
|
|
62
|
+
throw new Error(`${label}解析失败:${describeYamlError(err)}(${absPath})`);
|
|
63
|
+
}
|
|
64
|
+
// 「解析成功但不是映射」同样上抛,不能放进下游摊开再写回磁盘。空文件视同空映射(?? {} 已兜)
|
|
65
|
+
const kind = describeNonMapping(parsed);
|
|
66
|
+
if (kind)
|
|
67
|
+
throw new Error(`${label}不是一个映射(得到 ${kind}):${absPath}`);
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
const initial = load();
|
|
71
|
+
return {
|
|
72
|
+
initial,
|
|
73
|
+
save(value) {
|
|
74
|
+
// 写前比对磁盘实况:盘上内容必须仍是本进程最后读入或写出的那份。不是,就有尚未生效的外部修改
|
|
75
|
+
// (去抖窗口内、监听未武装、解析失败被搁置、另一个进程写过),整份覆写会把它静默吃掉,所以拒写。
|
|
76
|
+
// 文件不存在时没有可保护的内容,照写。
|
|
77
|
+
let onDisk = null;
|
|
78
|
+
try {
|
|
79
|
+
onDisk = readFileSync(absPath, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err.code !== 'ENOENT')
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
if (onDisk !== null && onDisk !== raw) {
|
|
86
|
+
reconcilePending = true;
|
|
87
|
+
throw new ConfigSaveRefusedError(`${label}有尚未生效的外部修改,为免覆盖已拒绝本次保存(${absPath})`);
|
|
88
|
+
}
|
|
89
|
+
const text = opts.serialize(value);
|
|
90
|
+
// 原子写:先写临时文件再 rename(同目录同分区,rename 原子覆盖)。直接 writeFileSync 是 O_CREAT|O_TRUNC,
|
|
91
|
+
// 写中断(磁盘写满、进程被杀)时盘上留下 0 字节或半截 YAML,空文件还会被当成空文档放行。
|
|
92
|
+
// watch() 守的是目录,rename 换 inode 不会打断监听。
|
|
93
|
+
const tmp = `${absPath}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`;
|
|
94
|
+
// 临时文件建出来就是收紧的权限:按默认 umask 建出再 chmod,其间或进程在 rename 前被杀时,目录里就有一份
|
|
95
|
+
// 权限更宽的副本;rename 让目标继承 tmp 的权限
|
|
96
|
+
let mode = opts.mode ?? 0o600;
|
|
97
|
+
if (opts.mode === undefined) {
|
|
98
|
+
try {
|
|
99
|
+
mode = statSync(absPath).mode & 0o777;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* 原文件不存在或取不到权限:按 0o600 建 */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
writeFileSync(tmp, text, { encoding: 'utf-8', mode });
|
|
107
|
+
// 建文件时的 mode 还会被 umask 收窄;对齐回目标权限位(不会宽于 mode 本身)
|
|
108
|
+
try {
|
|
109
|
+
chmodSync(tmp, mode);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* 对不齐就保持建出时更窄的权限,不拖累写入本身 */
|
|
113
|
+
}
|
|
114
|
+
renameSync(tmp, absPath);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
// 临时文件是整份内容(可能含密钥),不能留在目录里
|
|
118
|
+
try {
|
|
119
|
+
rmSync(tmp, { force: true });
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
/* 删不掉也要把原始错误传出去 */
|
|
123
|
+
}
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
raw = text;
|
|
127
|
+
reconcilePending = false;
|
|
128
|
+
},
|
|
129
|
+
watch(onChange) {
|
|
130
|
+
if (watcher)
|
|
131
|
+
return () => { };
|
|
132
|
+
// 守目录而非文件:文件可能尚未落盘,建出来时也要能接上
|
|
133
|
+
if (!existsSync(dataDir))
|
|
134
|
+
return () => { };
|
|
135
|
+
const fileName = basename(absPath);
|
|
136
|
+
try {
|
|
137
|
+
const schedule = () => {
|
|
138
|
+
if (debounceTimer)
|
|
139
|
+
clearTimeout(debounceTimer);
|
|
140
|
+
debounceTimer = setTimeout(() => {
|
|
141
|
+
debounceTimer = null;
|
|
142
|
+
// 读盘失败与解析失败分开:编辑器先删后建式保存的瞬时 ENOENT 不能报成「YAML 写错了」。
|
|
143
|
+
// 读不到安静返回(下一个事件会来),解析失败才出声
|
|
144
|
+
let current;
|
|
145
|
+
try {
|
|
146
|
+
current = readFileSync(absPath, 'utf-8');
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// raw 在装载、save、本回调三处维护:一并挡掉自写回、目录里无关文件的杂音、touch 与改完又改回来的空变更。
|
|
152
|
+
// 拒写之后的这一次除外(见 reconcilePending)
|
|
153
|
+
if (current === raw && !reconcilePending)
|
|
154
|
+
return;
|
|
155
|
+
let parsed;
|
|
156
|
+
try {
|
|
157
|
+
parsed = parseYamlText(current, label, logger);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// raw 只在解析成功后推进:同一份坏内容每次保存都重新告警,直到改对为止
|
|
161
|
+
logger.warn(`${label}解析失败,本次变更已忽略,仍在使用上一份内容:${describeYamlError(err)}`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// 空文件、裸标量、数组:一道闸拒收,放行会让下游按空内容重载或写回垃圾
|
|
165
|
+
if (parsed === null && opts.emptyAsMissing)
|
|
166
|
+
parsed = opts.missing();
|
|
167
|
+
const kind = describeNonMapping(parsed);
|
|
168
|
+
if (kind) {
|
|
169
|
+
logger.warn(`${label}不是一个映射(得到 ${kind}),本次变更已忽略`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
raw = current;
|
|
173
|
+
reconcilePending = false;
|
|
174
|
+
try {
|
|
175
|
+
onChange(parsed);
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
logger.warn(`${label}变更处理器抛错:${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
+
}
|
|
180
|
+
}, 300);
|
|
181
|
+
};
|
|
182
|
+
// 必须监听目录,不能监听文件本身:fs.watch 绑的是那一刻的 inode,编辑器保存与本模块的 save
|
|
183
|
+
// 都是「写临时文件 → rename」,换了 inode 之后绑在旧 inode 上的 watcher 永不触发,也没有任何报错
|
|
184
|
+
watcher = fsWatch(dataDir, (_event, filename) => {
|
|
185
|
+
// filename 平台不保证非空;为空时不过滤,交给内容比对兜底
|
|
186
|
+
if (filename && basename(filename.toString()) !== fileName)
|
|
187
|
+
return;
|
|
188
|
+
schedule();
|
|
189
|
+
});
|
|
190
|
+
// 不接 error 会成为未捕获异常、带倒进程(Windows 上被监听的目录被删或不可访问时会报 EPERM)
|
|
191
|
+
watcher.on('error', err => {
|
|
192
|
+
logger.warn(`${label}监听出错,已停止监听(${err instanceof Error ? err.message : String(err)}):手动修改后需重启才生效,重启前的进程保存会被拒绝`);
|
|
193
|
+
watcher?.close();
|
|
194
|
+
watcher = null;
|
|
195
|
+
});
|
|
196
|
+
// 武装即对账一次:武装前(启动期)的外部修改不会再有事件,不补就既不生效、又让 save 一直拒写
|
|
197
|
+
schedule();
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
logger.warn(`无法监听${label}变更(${err instanceof Error ? err.message : String(err)}):手动修改后需重启才生效,重启前的进程保存会被拒绝`);
|
|
201
|
+
}
|
|
202
|
+
return () => {
|
|
203
|
+
if (debounceTimer) {
|
|
204
|
+
clearTimeout(debounceTimer);
|
|
205
|
+
debounceTimer = null;
|
|
206
|
+
}
|
|
207
|
+
watcher?.close();
|
|
208
|
+
watcher = null;
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=yaml-file.js.map
|