@webskill/chatbot 0.15.0 → 0.17.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 +1 -1
- package/dist/chatbot.css +4 -0
- package/dist/config.d.ts +104 -0
- package/dist/config.js +144 -0
- package/dist/core-D3Tq37jA.js +3063 -0
- package/dist/core-DBetA176.js +2035 -0
- package/dist/index-B25ux241.d.ts +390 -0
- package/dist/index.d.ts +6 -389
- package/dist/index.js +300 -6152
- package/dist/markdown-4gn774sm.js +280 -0
- package/dist/parse-79VbXLl4.js +642 -0
- package/dist/parse-Cc8nFxir.d.ts +280 -0
- package/dist/tsx-DDiQWvTL.js +28 -0
- package/dist/types-C4dzGOPp.js +511 -0
- package/dist/vite.d.ts +33 -0
- package/dist/vite.js +132 -0
- package/package.json +20 -3
- package/virtual.d.ts +27 -0
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ implementation (OPFS storage, skills, demo/model providers, console embedding).
|
|
|
115
115
|
default `FsSessionStore` decides what you see:
|
|
116
116
|
|
|
117
117
|
- Sessions are sorted by `createdAt` **ascending**, and a page is taken from the
|
|
118
|
-
**tail** of that list. The first screen therefore holds the
|
|
118
|
+
**tail** of that list. The first screen therefore holds the _newest_ sessions,
|
|
119
119
|
and the oldest session is not on it.
|
|
120
120
|
- The page size is the store's (`FS_SESSION_PAGE_SIZE`, 50). `<Chatbot>` does
|
|
121
121
|
not override it — pass your own `sessionStore` if you need another size.
|
package/dist/chatbot.css
CHANGED
|
@@ -1391,6 +1391,10 @@
|
|
|
1391
1391
|
outline-style: var(--tw-outline-style);
|
|
1392
1392
|
outline-width: 1px;
|
|
1393
1393
|
}
|
|
1394
|
+
.sepia {
|
|
1395
|
+
--tw-sepia: sepia(100%);
|
|
1396
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1397
|
+
}
|
|
1394
1398
|
.filter {
|
|
1395
1399
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1396
1400
|
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { n as RuntimeConfig, r as RuntimeConfigStore } from "./index-B25ux241.js";
|
|
2
|
+
import { a as parseWebSkillConfig, c as ConfigFieldSpec, i as WebSkillConfigDefaults, l as ConfigLeafKind, n as ParseWebSkillConfigOptions, o as CONFIGURED_RUNTIME_PATHS, r as ParsedWebSkillConfig, s as CONFIG_FIELDS, t as CONFIG_TOP_LEVEL_SECTIONS, u as NON_CONFIGURABLE_RUNTIME_PATHS } from "./parse-Cc8nFxir.js";
|
|
3
|
+
//#region src/config/errors.d.ts
|
|
4
|
+
/** 稳定错误码,供宿主可编程处理;`message` 一律英文 @experimental */
|
|
5
|
+
type WebSkillConfigErrorCode = 'CONFIG_UNKNOWN_KEY' | 'CONFIG_TYPE' | 'CONFIG_RANGE' | 'CONFIG_ENUM' | 'CONFIG_SHAPE' | 'CONFIG_REFERENCE';
|
|
6
|
+
/**
|
|
7
|
+
* 配置文件校验失败。
|
|
8
|
+
*
|
|
9
|
+
* 校验一律**硬失败**:拼错一个字段名而解析器静默忽略,得到的是「看起来生效了其实没生效」
|
|
10
|
+
* 的构建产物,症状出现在运行时且完全无从反推。
|
|
11
|
+
* @experimental
|
|
12
|
+
*/
|
|
13
|
+
declare class WebSkillConfigError extends Error {
|
|
14
|
+
readonly code: WebSkillConfigErrorCode;
|
|
15
|
+
/** 出问题的位置在配置文件里的路径,如 `agentRuntime.loop.maxTurns` */
|
|
16
|
+
readonly path: string;
|
|
17
|
+
constructor(code: WebSkillConfigErrorCode, path: string, message: string);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/config/merge.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* 三方合并:宿主缺省值 · 用户存储 · 上次应用过的缺省值快照。
|
|
23
|
+
*
|
|
24
|
+
* 语义是**缺省值**而不是强制覆盖:用户在 Console 里改过的项永远优先。
|
|
25
|
+
* 合并按层递归而非顶层 spread——SDK 后续新增字段时,老的存储里没有那个字段,
|
|
26
|
+
* 逐层合并才能让缺省值填进去;顶层 spread 会因为 `sandbox` 整体存在而整段落空。
|
|
27
|
+
*
|
|
28
|
+
* 数组是**替换**语义:`entries: []` 是「用户把模型删光了」,
|
|
29
|
+
* 与「从没配过」(键缺席)必须分得开,否则删掉的条目会自己长回来。
|
|
30
|
+
*/
|
|
31
|
+
/** 三方合并的输入 @experimental */
|
|
32
|
+
interface ApplyConfigDefaultsInput {
|
|
33
|
+
/** 用户存储里的原始值(**未**合并默认值),可缺席 */
|
|
34
|
+
readonly stored?: unknown;
|
|
35
|
+
/** `parseWebSkillConfig(...).defaults` */
|
|
36
|
+
readonly defaults: WebSkillConfigDefaults;
|
|
37
|
+
/** 上次应用过的缺省值快照,可缺席 */
|
|
38
|
+
readonly lastApplied?: WebSkillConfigDefaults;
|
|
39
|
+
}
|
|
40
|
+
/** 合并结果:可直接使用的配置,以及要持久化的本次缺省值快照 @experimental */
|
|
41
|
+
interface AppliedConfigDefaults {
|
|
42
|
+
readonly config: RuntimeConfig;
|
|
43
|
+
/** 本次应用的缺省值;调用方负责持久化,下次作为 `lastApplied` 传回 */
|
|
44
|
+
readonly snapshot: WebSkillConfigDefaults;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `lastApplied` 缺席时每条分支都退化成「stored 存在就取 stored」,
|
|
48
|
+
* 即老实现的 `deepMerge(defaults, stored)`——修订跟随是纯增量,不改既有语义。
|
|
49
|
+
* @experimental
|
|
50
|
+
*/
|
|
51
|
+
declare function applyConfigDefaults(input: ApplyConfigDefaultsInput): AppliedConfigDefaults;
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/config/store.d.ts
|
|
54
|
+
/**
|
|
55
|
+
* 把「宿主缺省值」接进宿主已有的 `RuntimeConfigStore`。
|
|
56
|
+
*
|
|
57
|
+
* 不改 `RuntimeConfigStore` 接口——那是既有公开契约。包装器额外要一条读**原始**
|
|
58
|
+
* 存储的通道(`readRaw`):`load()` 返回的是补齐默认值之后的完整配置,
|
|
59
|
+
* 从它分不出「用户显式设成 deny-all」和「用户没配、SDK 默认就是 deny-all」。
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* 存储里代替明文 apiKey 的占位串。
|
|
63
|
+
*
|
|
64
|
+
* 它让烘焙的密钥不落盘(F12 看 localStorage 看不到),但**到此为止**:
|
|
65
|
+
* 密钥运行时必然在内存里以明文存在,也必然明文发给模型提供方。
|
|
66
|
+
* 这不是安全边界,只是把可被顺手读到的面缩小一点。
|
|
67
|
+
* @experimental
|
|
68
|
+
*/
|
|
69
|
+
declare const BAKED_KEY_SENTINEL = "\0webskill:baked-api-key";
|
|
70
|
+
/**
|
|
71
|
+
* entryId → 明文 apiKey。
|
|
72
|
+
*
|
|
73
|
+
* 解密代码由 `@webskill/chatbot/vite` 在 `secrets: 'bake'` 时**生成**,
|
|
74
|
+
* 因此本模块里一行 WebCrypto 都没有——`'omit'` 构建的产物里也就扫不到 `crypto.subtle`。
|
|
75
|
+
* 宿主直接把虚拟模块的 `loadWebSkillSecrets` 传进来即可。
|
|
76
|
+
* @experimental
|
|
77
|
+
*/
|
|
78
|
+
type BakedSecretLoader = () => Promise<Readonly<Record<string, string>>>;
|
|
79
|
+
/** 上次生效的缺省值快照仓;存哪儿由宿主决定 @experimental */
|
|
80
|
+
interface ConfigDefaultsSnapshotStore {
|
|
81
|
+
read(): Promise<WebSkillConfigDefaults | undefined> | WebSkillConfigDefaults | undefined;
|
|
82
|
+
write(snapshot: WebSkillConfigDefaults): Promise<void> | void;
|
|
83
|
+
clear(): Promise<void> | void;
|
|
84
|
+
}
|
|
85
|
+
/** 包装选项 @experimental */
|
|
86
|
+
interface ConfigDefaultsStoreOptions {
|
|
87
|
+
/** `parseWebSkillConfig(...).defaults` */
|
|
88
|
+
readonly defaults: WebSkillConfigDefaults;
|
|
89
|
+
/** 读原始存储;返回 `undefined` 表示这台机器上从没存过配置 */
|
|
90
|
+
readRaw(): Promise<unknown> | unknown;
|
|
91
|
+
/** 上次应用过的缺省值快照的读写。不给就退化成「每次都按首次应用处理」 */
|
|
92
|
+
readonly snapshot?: ConfigDefaultsSnapshotStore;
|
|
93
|
+
/** 烘焙凭据的读取器,通常是 `virtual:webskill-config` 的 `loadWebSkillSecrets` */
|
|
94
|
+
readonly secrets?: BakedSecretLoader;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 把一个 `RuntimeConfigStore` 包成「读时叠缺省值、写时摘凭据」的 store。
|
|
98
|
+
*
|
|
99
|
+
* 用户改过的字段永远优先;传了 `snapshot` 时,没改过的字段会跟随重新打包后的新缺省值。
|
|
100
|
+
* @experimental
|
|
101
|
+
*/
|
|
102
|
+
declare function createConfigDefaultsStore(store: RuntimeConfigStore, options: ConfigDefaultsStoreOptions): RuntimeConfigStore;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { type AppliedConfigDefaults, type ApplyConfigDefaultsInput, BAKED_KEY_SENTINEL, type BakedSecretLoader, CONFIGURED_RUNTIME_PATHS, CONFIG_FIELDS, CONFIG_TOP_LEVEL_SECTIONS, type ConfigDefaultsSnapshotStore, type ConfigDefaultsStoreOptions, type ConfigFieldSpec, type ConfigLeafKind, NON_CONFIGURABLE_RUNTIME_PATHS, type ParseWebSkillConfigOptions, type ParsedWebSkillConfig, type WebSkillConfigDefaults, WebSkillConfigError, type WebSkillConfigErrorCode, applyConfigDefaults, createConfigDefaultsStore, parseWebSkillConfig };
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { r as mergeRuntimeConfigDefaults } from "./types-C4dzGOPp.js";
|
|
2
|
+
import { a as NON_CONFIGURABLE_RUNTIME_PATHS, i as CONFIG_FIELDS, n as parseWebSkillConfig, o as WebSkillConfigError, r as CONFIGURED_RUNTIME_PATHS, t as CONFIG_TOP_LEVEL_SECTIONS } from "./parse-79VbXLl4.js";
|
|
3
|
+
|
|
4
|
+
//#region src/config/merge.ts
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
/** 叶子相等判定。数组与对象都按结构比,因为叶子可以是数组(模型列表、提示词) */
|
|
9
|
+
function sameValue(a, b) {
|
|
10
|
+
if (Object.is(a, b)) return true;
|
|
11
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, index) => sameValue(item, b[index]));
|
|
12
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
13
|
+
const keys = Object.keys(a);
|
|
14
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
15
|
+
return keys.every((key) => key in b && sameValue(a[key], b[key]));
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 逐叶子决定归属。
|
|
21
|
+
*
|
|
22
|
+
* 中间那个分支是本版的**全部新增语义**,也是最容易被后人当成冗余删掉的一条:
|
|
23
|
+
* 存储里的值与上次应用过的缺省值逐字相等,说明用户从没碰过这一项,
|
|
24
|
+
* 于是它应当跟随宿主新一版的缺省值。没有这一条,宿主改了默认值,
|
|
25
|
+
* 所有装过旧版的用户永远停在旧默认值上,而他们自己完全不知道有这么一项。
|
|
26
|
+
*/
|
|
27
|
+
function mergeLeaf(stored, defaultValue, lastApplied) {
|
|
28
|
+
if (defaultValue === void 0) return stored;
|
|
29
|
+
if (stored === void 0) return defaultValue;
|
|
30
|
+
if (isPlainObject(stored) && isPlainObject(defaultValue)) {
|
|
31
|
+
const last = isPlainObject(lastApplied) ? lastApplied : void 0;
|
|
32
|
+
const out = { ...stored };
|
|
33
|
+
for (const key of Object.keys(defaultValue)) out[key] = mergeLeaf(stored[key], defaultValue[key], last?.[key]);
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
if (lastApplied !== void 0 && sameValue(stored, lastApplied)) return defaultValue;
|
|
37
|
+
return stored;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `lastApplied` 缺席时每条分支都退化成「stored 存在就取 stored」,
|
|
41
|
+
* 即老实现的 `deepMerge(defaults, stored)`——修订跟随是纯增量,不改既有语义。
|
|
42
|
+
* @experimental
|
|
43
|
+
*/
|
|
44
|
+
function applyConfigDefaults(input) {
|
|
45
|
+
const { stored, defaults, lastApplied } = input;
|
|
46
|
+
return {
|
|
47
|
+
config: mergeRuntimeConfigDefaults(stored === void 0 ? defaults : mergeLeaf(stored, defaults, lastApplied)),
|
|
48
|
+
snapshot: defaults
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/config/store.ts
|
|
54
|
+
/**
|
|
55
|
+
* 把「宿主缺省值」接进宿主已有的 `RuntimeConfigStore`。
|
|
56
|
+
*
|
|
57
|
+
* 不改 `RuntimeConfigStore` 接口——那是既有公开契约。包装器额外要一条读**原始**
|
|
58
|
+
* 存储的通道(`readRaw`):`load()` 返回的是补齐默认值之后的完整配置,
|
|
59
|
+
* 从它分不出「用户显式设成 deny-all」和「用户没配、SDK 默认就是 deny-all」。
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* 存储里代替明文 apiKey 的占位串。
|
|
63
|
+
*
|
|
64
|
+
* 它让烘焙的密钥不落盘(F12 看 localStorage 看不到),但**到此为止**:
|
|
65
|
+
* 密钥运行时必然在内存里以明文存在,也必然明文发给模型提供方。
|
|
66
|
+
* 这不是安全边界,只是把可被顺手读到的面缩小一点。
|
|
67
|
+
* @experimental
|
|
68
|
+
*/
|
|
69
|
+
const BAKED_KEY_SENTINEL = "\0webskill:baked-api-key";
|
|
70
|
+
function mapEntries(config, map) {
|
|
71
|
+
return {
|
|
72
|
+
...config,
|
|
73
|
+
llm: {
|
|
74
|
+
...config.llm,
|
|
75
|
+
entries: config.llm.entries.map(map)
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createSecretVault(loader) {
|
|
80
|
+
let cache;
|
|
81
|
+
async function read() {
|
|
82
|
+
if (cache === void 0) cache = loader === void 0 ? {} : await loader();
|
|
83
|
+
return cache;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
/**
|
|
87
|
+
* 只在 `apiKey` 缺席或等于占位串时注入:空串是用户**显式清空**,
|
|
88
|
+
* 再注入回去等于用户删不掉这个密钥。
|
|
89
|
+
*/
|
|
90
|
+
async restore(config) {
|
|
91
|
+
const secrets = await read();
|
|
92
|
+
if (Object.keys(secrets).length === 0) return config;
|
|
93
|
+
return mapEntries(config, (entry) => {
|
|
94
|
+
const baked = secrets[entry.id];
|
|
95
|
+
if (baked === void 0) return entry;
|
|
96
|
+
if (entry.apiKey !== void 0 && entry.apiKey !== "\0webskill:baked-api-key") return entry;
|
|
97
|
+
return {
|
|
98
|
+
...entry,
|
|
99
|
+
apiKey: baked
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
/** 落盘前把烘焙密钥换回占位串,明文因此不进存储 */
|
|
104
|
+
async strip(config) {
|
|
105
|
+
const secrets = await read();
|
|
106
|
+
if (Object.keys(secrets).length === 0) return config;
|
|
107
|
+
return mapEntries(config, (entry) => entry.apiKey !== void 0 && entry.apiKey === secrets[entry.id] ? {
|
|
108
|
+
...entry,
|
|
109
|
+
apiKey: BAKED_KEY_SENTINEL
|
|
110
|
+
} : entry);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 把一个 `RuntimeConfigStore` 包成「读时叠缺省值、写时摘凭据」的 store。
|
|
116
|
+
*
|
|
117
|
+
* 用户改过的字段永远优先;传了 `snapshot` 时,没改过的字段会跟随重新打包后的新缺省值。
|
|
118
|
+
* @experimental
|
|
119
|
+
*/
|
|
120
|
+
function createConfigDefaultsStore(store, options) {
|
|
121
|
+
const vault = createSecretVault(options.secrets);
|
|
122
|
+
return {
|
|
123
|
+
load: async () => {
|
|
124
|
+
const stored = await options.readRaw();
|
|
125
|
+
const lastApplied = await options.snapshot?.read();
|
|
126
|
+
const { config, snapshot } = applyConfigDefaults({
|
|
127
|
+
stored: stored ?? void 0,
|
|
128
|
+
defaults: options.defaults,
|
|
129
|
+
lastApplied
|
|
130
|
+
});
|
|
131
|
+
await options.snapshot?.write(snapshot);
|
|
132
|
+
return vault.restore(config);
|
|
133
|
+
},
|
|
134
|
+
save: async (config) => store.save(await vault.strip(config)),
|
|
135
|
+
reset: async () => {
|
|
136
|
+
await store.reset();
|
|
137
|
+
await options.snapshot?.clear();
|
|
138
|
+
},
|
|
139
|
+
...store.subscribe ? { subscribe: (listener) => store.subscribe(listener) } : {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { BAKED_KEY_SENTINEL, CONFIGURED_RUNTIME_PATHS, CONFIG_FIELDS, CONFIG_TOP_LEVEL_SECTIONS, NON_CONFIGURABLE_RUNTIME_PATHS, WebSkillConfigError, applyConfigDefaults, createConfigDefaultsStore, parseWebSkillConfig };
|