cortico 0.1.0 → 0.1.1

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 (63) hide show
  1. package/README.md +4 -11
  2. package/package.json +1 -3
  3. package/src/bot.ts +1 -1
  4. package/src/core/README.md +0 -4
  5. package/src/core/types.ts +1 -1
  6. package/src/core/util.ts +2 -1
  7. package/src/deploy.ts +8 -6
  8. package/src/providers/README.md +4 -5
  9. package/src/providers/base.ts +1 -1
  10. package/src/providers/configuration.ts +2 -1
  11. package/src/providers/console/settings.ts +8 -10
  12. package/src/providers/llamacpp/console/runtime-panel.ts +73 -32
  13. package/src/providers/llamacpp/console/server.ts +1 -20
  14. package/src/providers/llamacpp/index.ts +0 -2
  15. package/src/providers/llamacpp/options.ts +0 -2
  16. package/src/providers/llamacpp/strings.ts +6 -6
  17. package/src/providers/openai-responses-compat/index.ts +0 -3
  18. package/src/web/README.md +1 -5
  19. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +35 -41
  20. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +4 -4
  21. package/src/web/client/console-pages/host.ts +2 -2
  22. package/src/web/client/features/core/strings.ts +2 -2
  23. package/src/web/client/features/feature.ts +1 -1
  24. package/src/web/client/features/live/index.ts +8 -1
  25. package/src/web/client/features/live/onboarding.ts +4 -1
  26. package/src/web/client/features/live/strings.ts +11 -11
  27. package/src/web/client/features/live/timeline.ts +2 -1
  28. package/src/web/client/features/settings/general.ts +1 -0
  29. package/src/web/client/features/settings/index.ts +1 -0
  30. package/src/web/client/features/worlds/index.ts +1 -0
  31. package/src/web/client/shell/index.ts +27 -47
  32. package/src/web/client/shell/strings.ts +0 -22
  33. package/src/web/client/ui/icons.ts +5 -0
  34. package/src/web/console-pages.ts +1 -1
  35. package/src/web/public/styles.css +21 -8
  36. package/src/web/server.ts +11 -3
  37. package/src/web/shared/client-panel.ts +1 -1
  38. package/src/web/shared/console-protocol.ts +2 -1
  39. package/src/worlds/minecraft/README.md +61 -11
  40. package/src/worlds/minecraft/cell-facts.ts +226 -0
  41. package/src/worlds/minecraft/chests.ts +5 -0
  42. package/src/worlds/minecraft/containers.ts +325 -0
  43. package/src/worlds/minecraft/entity-facts.ts +31 -5
  44. package/src/worlds/minecraft/executor.ts +283 -10348
  45. package/src/worlds/minecraft/inventory.ts +268 -0
  46. package/src/worlds/minecraft/melee.ts +419 -0
  47. package/src/worlds/minecraft/placed-ledger.ts +152 -0
  48. package/src/worlds/minecraft/placement.ts +1038 -0
  49. package/src/worlds/minecraft/receipt.ts +340 -0
  50. package/src/worlds/minecraft/skill-context.ts +358 -0
  51. package/src/worlds/minecraft/skills-build.ts +1169 -0
  52. package/src/worlds/minecraft/skills-container.ts +1343 -0
  53. package/src/worlds/minecraft/skills-craft.ts +333 -0
  54. package/src/worlds/minecraft/skills-dig.ts +624 -0
  55. package/src/worlds/minecraft/skills-gather.ts +1230 -0
  56. package/src/worlds/minecraft/skills-interact.ts +1559 -0
  57. package/src/worlds/minecraft/tools.ts +331 -0
  58. package/src/worlds/minecraft/travel.ts +763 -0
  59. package/src/worlds/minecraft/until.ts +75 -0
  60. package/src/worlds/terminal/world.ts +8 -5
  61. package/src/providers/console/config.ts +0 -21
  62. package/src/providers/llamacpp/config.ts +0 -36
  63. package/src/providers/openai-responses-compat/config.ts +0 -20
package/README.md CHANGED
@@ -15,8 +15,7 @@ import type { EventEnvelope, WorldHost } from 'cortico/core/types.ts';
15
15
 
16
16
  Inside a running instance those specifiers are resolved by the host's module hook to the
17
17
  framework that instance runs. This package supplies the same files to the editor, the type
18
- checker and the test runner before the extension is installed anywhere, which is the whole of
19
- what it does: it has no entry point and starts nothing.
18
+ checker and the test runner. It has no entry point and starts nothing.
20
19
 
21
20
  ## Install
22
21
 
@@ -34,14 +33,8 @@ the packaging rules.
34
33
 
35
34
  ## Running a bot
36
35
 
37
- Running Cortico itself means cloning the repository; deployments, bot packages and the web
38
- console live in the checkout.
39
-
40
- ```bash
41
- git clone https://github.com/Pal-AI-Lab/Cortico.git
42
- cd Cortico
43
- corepack pnpm install
44
- pnpm start
45
- ```
36
+ Deployments, bot packages and the web console live in a checkout of the repository, which is
37
+ where a bot runs from:
38
+ [Quick Start](https://github.com/Pal-AI-Lab/Cortico#quick-start).
46
39
 
47
40
  MIT licensed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortico",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "A TypeScript framework for event-driven, long-running persona bots",
6
6
  "license": "MIT",
@@ -36,8 +36,6 @@
36
36
  "@types/express": "^4.17.21",
37
37
  "@types/ws": "^8.5.13",
38
38
  "express": "^4.21.2",
39
- "fflate": "0.8.3",
40
- "tar-stream": "3.2.1",
41
39
  "ws": "^8.18.0"
42
40
  }
43
41
  }
package/src/bot.ts CHANGED
@@ -867,7 +867,7 @@ export function deriveConsolePageSources(
867
867
  parts: { assembly: WorldAssembly; persona?: Persona },
868
868
  /**
869
869
  * bot 标识、展示名、Memory 名与配置组;配置组归入 Persona 页面。
870
- * 展示名由部署配置提供;Persona 页标题取 Persona 的类名。
870
+ * 展示名是这一台 bot 的名字(底栏头像旁边那个),Persona 页的标题另取 Persona 的类名。
871
871
  */
872
872
  bot?: { id: string; label: string; memoryName?: string; configGroups?: readonly ConfigGroup[] },
873
873
 
@@ -77,12 +77,8 @@ handler 异常转为失败回执。流式生成时 `EagerDispatch` 可提前执
77
77
  `Persona.onHandoff(snapshot, { hardTokens })` 返回 `{ tail, trim? }`。Core 重建 system 前缀,
78
78
  按 `hardTokens − estimate(prefix)` 限制保留上下文,其中 prefix 包括 system 前缀和 Persona 的合成
79
79
  开头(`sessionHead()`)。Core 校验工具调用配对,并重置 session。
80
- 合成开头每次请求现取,不写入持久 session;Core 不据此判定远端缓存是否命中。
81
80
  阶段预算与保留比例由 Persona 决定,模型配置来自当前 provider。
82
81
 
83
- 存储清单使用 `StorageOwner` 标识 Core、Persona、Memory 与 World 的归属。
84
- 页面组织和 Memory 展示名称由装配层处理。
85
-
86
82
  ## 错误隔离
87
83
 
88
84
  装配层将 World 构造失败限制在该 World。`start()` 抛错时不挂载;`stop()` 失败或超时会记录
package/src/core/types.ts CHANGED
@@ -930,7 +930,7 @@ export interface Persona {
930
930
  /**
931
931
  * session 的合成开头:每次请求置于 system 前缀之后、持久历史之前,不写入 session,交接时
932
932
  * 不进保留内容。Core 每次出请求前调用一次,丢弃 system 与 developer 项,补齐工具配对,
933
- * 计入 hardTokens。内容由 Persona 的当前输入决定。
933
+ * 计入 hardTokens。内容只应随 Persona 自己的输入变化;每次不同就每次打穿前缀缓存。
934
934
  */
935
935
  sessionHead?(): Item[];
936
936
  /** Memory 实例。Core 不读它的内容;bot 没给 memoryName 时控制台以它的类名作 Memory 页标题。 */
package/src/core/util.ts CHANGED
@@ -372,7 +372,8 @@ export function shortId(prefix = ''): string {
372
372
  }
373
373
 
374
374
  /**
375
- * BOM 读取 UTF-8 UTF-16 LE 文本,返回值不含 BOM;无 BOM 时使用 UTF-8。
375
+ * 读一个手写的文本文件:按 BOM 选编码,UTF-8 UTF-16 LE 都读得出来,BOM 不进返回值。
376
+ * 没有 BOM 就是 UTF-8。Windows 的 shell 重定向写出的是带 BOM 的 UTF-8 或 UTF-16 LE。
376
377
  */
377
378
  export function readTextFile(file: string): string {
378
379
  const bytes = readFileSync(file);
package/src/deploy.ts CHANGED
@@ -3,12 +3,11 @@
3
3
  * 部署文件中的 providers 字段不参与合并。Core、Persona 与 World 的默认值由各自所有者提供。
4
4
  * 运行时共享合并后的配置对象,控制台和调参工具原位更新。
5
5
  */
6
- import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs';
6
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
7
7
  import { isAbsolute, resolve } from 'node:path';
8
8
  import type { CoreConfig, LLMProviderEntry } from './core/types.ts';
9
9
  import { deepMerge, type LoadedConfig } from './core/config.ts';
10
10
  import { secretReader } from './core/secrets.ts';
11
- import { readTextFile } from './core/util.ts';
12
11
  import { deploymentRoot, repoRoot as codeRepoRoot } from './paths.ts';
13
12
 
14
13
  export type { LoadedConfig } from './core/config.ts';
@@ -27,7 +26,10 @@ export function listBots(root = deploymentRoot()): string[] {
27
26
  /** 部署根下一份部署都没有时自建的那份,以及它引用的 bot 代码包。 */
28
27
  export const DEFAULT_DEPLOYMENT = { name: 'mybot', bot: 'cormini' } as const;
29
28
 
30
- /** 部署目录中的开场引导标记。 */
29
+ /**
30
+ * 开场引导的一次性标记,放在部署目录下。自建部署时写入,控制台见到它才给引导,操作员
31
+ * 开口或按下那颗按钮后删除。手动建的部署没有这个文件,也就不会看到引导。
32
+ */
31
33
  export const ONBOARDING_FLAG_FILE = '.onboarding';
32
34
 
33
35
  /** 部署根下另有用途的目录名,不能拿来当部署名。 */
@@ -88,7 +90,7 @@ function globalProviders(providersDir: string): Record<string, LLMProviderEntry>
88
90
  const file = resolve(providersDir, name, 'config.json');
89
91
  if (!existsSync(file)) continue;
90
92
  try {
91
- table[name] = JSON.parse(readTextFile(file)) as LLMProviderEntry;
93
+ table[name] = JSON.parse(readFileSync(file, 'utf8')) as LLMProviderEntry;
92
94
  } catch (err) {
93
95
  throw new Error(`${file} 解析失败:${err instanceof Error ? err.message : String(err)}`);
94
96
  }
@@ -108,7 +110,7 @@ function packageWorldOverrides(pkgDir: string): Record<string, unknown> {
108
110
  const file = resolve(ioDir, id, 'config.json');
109
111
  if (!existsSync(file)) continue;
110
112
  try {
111
- worlds[id] = JSON.parse(readTextFile(file));
113
+ worlds[id] = JSON.parse(readFileSync(file, 'utf8'));
112
114
  } catch (err) {
113
115
  throw new Error(`${file} 解析失败:${err instanceof Error ? err.message : String(err)}`);
114
116
  }
@@ -135,7 +137,7 @@ export function loadDeployment<C extends CoreConfig>(
135
137
  const providers = resolve(providersDir);
136
138
  const cfgPath = resolve(dir, 'config.json');
137
139
  const raw: Partial<C> & Record<string, unknown> = existsSync(cfgPath)
138
- ? (JSON.parse(readTextFile(cfgPath)) as Partial<C> & Record<string, unknown>)
140
+ ? (JSON.parse(readFileSync(cfgPath, 'utf8')) as Partial<C> & Record<string, unknown>)
139
141
  : {};
140
142
  // 部署文件不能覆盖共享端点表。
141
143
  delete raw.providers;
@@ -1,4 +1,4 @@
1
- <!-- Owner: src/providers/base.ts, src/providers/registry.ts, src/providers/console/settings.ts, src/providers/console/config.ts, src/providers/openai-responses-compat/config.ts -->
1
+ <!-- Owner: src/providers/base.ts, src/providers/registry.ts -->
2
2
 
3
3
  # src/providers
4
4
 
@@ -27,10 +27,9 @@ effort 收任意非空串)、`serviceTiers`、`create(name, entry, host)`。可
27
27
  `validateEntry()`、`validateModel()`、`accepts()`(多模态判定)、`config()` 与 `console()`
28
28
  (附加配置组与面板)、`prices()`、`estimateTokens()`、`contextOverflow()`。
29
29
 
30
- 地址、密钥变量名与图像开关由 `console/config.ts` 声明为 ConfigGroup。模块自己的
31
- 配置通过 `config()` 声明。模块可在 `instance` 插槽中渲染这些 schema,
32
- 通过 `ctx.setConfig` 保存;`console().config` 显式为空时省略独立配置页签,
33
- 配置组仍参与服务端校验。运行时启停和模型操作由面板 invoke 提供。
30
+ 地址、密钥变量名与图像开关由控制台的端点面板编辑,模块不为它们声明配置组。模块自己的
31
+ `options.*` 要么走 `config()` 的配置组,要么由 `console()` 声明一块挂进 `instance` 插槽的面板——
32
+ 内建 llamacpp 的运行时与模型两段走的是后者。
34
33
 
35
34
  `create()` 返回 `ProviderInstance`:`client`(实现 `respond`)、`listModels?`、`control?`、
36
35
  `compatibilityKey?`、`start?` / `stop?`、`contextWindow?(model)`。
@@ -60,7 +60,7 @@ export interface ProviderInstance {
60
60
  contextWindow?(model: string): number | undefined;
61
61
  }
62
62
 
63
- /** 端点是否满足本地配置条件;不探测远端可达性或生成结果。 */
63
+ /** 一个端点此刻能不能发起生成。判断只看本地状态,不连上游。 */
64
64
  export interface ProviderAvailability {
65
65
  ready: boolean;
66
66
  /** 不可用的原因,控制台语言;可用时不带。 */
@@ -47,7 +47,8 @@ export function validateSpec(
47
47
  }
48
48
 
49
49
  /**
50
- * 返回第一项不满足的本地条件;通用条件满足后才询问模块。
50
+ * 端点能不能用:选了模型、声明的密钥读得到,模块自己的条件也满足。
51
+ * 通用条件不满足就不问模块,第一条不满足的就是回给操作员的那句话。
51
52
  */
52
53
  export function endpointAvailability(
53
54
  module: ProviderModule,
@@ -27,14 +27,13 @@ import { responseRequest } from '../../protocol/open-responses/context-helpers.t
27
27
  import { record } from '../../protocol/open-responses/context.ts';
28
28
  import { text } from './strings.ts';
29
29
  import type { ProviderConsoleHost } from './types.ts';
30
- import { connectionGroup } from './config.ts';
31
30
 
32
31
  export type SecretStatus = 'env' | 'file' | 'none';
33
32
 
34
33
  /** Output token limit used by the connectivity probe. */
35
34
  const PROBE_MAX_OUTPUT_TOKENS = 256;
36
35
 
37
- /** 从磁盘加载的密钥变量名按字面匹配。 */
36
+ /** 密钥变量名由操作员自由填写,拼进正则前按字面转义。 */
38
37
  function escapeRegExp(s: string): string {
39
38
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
40
39
  }
@@ -71,8 +70,7 @@ export class ProviderSettings {
71
70
  private declaredGroups(language: Language) {
72
71
  return this.modules.flatMap((module) =>
73
72
  this.entries(module).flatMap(({ name, entry }) =>
74
- [connectionGroup(name, entry, language), ...(module.config?.(name, entry, language) ?? [])]
75
- .map((group) => ({ name, group })),
73
+ (module.config?.(name, entry, language) ?? []).map((group) => ({ name, group })),
76
74
  ),
77
75
  );
78
76
  }
@@ -250,6 +248,7 @@ export class ProviderSettings {
250
248
  })) satisfies ConsolePageSource[];
251
249
  }
252
250
 
251
+ /** 端点能不能用:通用条件由框架查,模块自己的条件由模块答。 */
253
252
  availability(name: string, language: Language = 'zh'): ProviderAvailability {
254
253
  const entry = this.config.providers[name];
255
254
  if (!entry) return { ready: false, reason: text(language).unknownInstance };
@@ -263,7 +262,8 @@ export class ProviderSettings {
263
262
  }
264
263
 
265
264
  /**
266
- * 汇总端点的本地可用性,附第一个可用端点或最后一个不可用原因。
265
+ * 这批端点里有没有一个能用。灯亮=有;悬停说明给出第一个可用的端点名,
266
+ * 或者最后一个端点的不可用原因。
267
267
  */
268
268
  private availableLamp(
269
269
  entries: ReadonlyArray<{ name: string }>,
@@ -281,7 +281,7 @@ export class ProviderSettings {
281
281
  return { label: S.availableLamp, state: 'offline', hint: reason };
282
282
  }
283
283
 
284
- /** 汇总所有模块的端点可用性。 */
284
+ /** 所有模块合起来有没有一个可用端点。控制台左栏「语言模型」那一行点的就是它。 */
285
285
  providersLamp(language: Language = 'zh'): ConsoleLamp {
286
286
  return this.availableLamp(
287
287
  this.modules.flatMap((module) => this.entries(module)),
@@ -334,7 +334,7 @@ export class ProviderSettings {
334
334
  },
335
335
  ...(extra.panels ?? []),
336
336
  ],
337
- config: extra.config ?? entries.flatMap(
337
+ config: entries.flatMap(
338
338
  ({ name, entry }) => module.config?.(name, entry, language) ?? [],
339
339
  ),
340
340
  invoke: async (panel, method, args) => {
@@ -358,8 +358,6 @@ export class ProviderSettings {
358
358
  instances: this.entries(module).map(({ name, entry }) => ({
359
359
  name,
360
360
  entry,
361
- config: this.declaredGroups(language).filter((item) => item.name === name)
362
- .map(({ group }) => ({ group, values: this.values(group.id, language) })),
363
361
  secretConfigured: this.secretStatus(name, entry),
364
362
  quotes: (entry.spec ? [entry.spec] : []).map((spec) => ({
365
363
  model: spec.model,
@@ -385,7 +383,7 @@ export class ProviderSettings {
385
383
  const name = body.name;
386
384
  if (method === 'create') {
387
385
  this.assertNewName(name, language);
388
- // 未设置端点价目覆盖,报价仍可来自模块。
386
+ // 报价留空:用量页把这条端点的调用记成未计价,而不是零元。
389
387
  this.save(name, {
390
388
  kind: module.id,
391
389
  baseUrl: String(body.baseUrl || module.defaultBaseUrl || ''),
@@ -3,12 +3,11 @@
3
3
  * The endpoint comes from `ctx.scope.instance`; edits are written on change.
4
4
  */
5
5
  import type { ConsolePanel, ConsolePanelContext } from '../../../web/shared/client-panel.ts';
6
- import type { ConfigValues } from '../../../core/config-schema.ts';
7
- import { configField } from '../../../web/client/features/config/view.ts';
8
- import type { RuntimePanelState } from './server.ts';
6
+ import type { LaunchOptions } from '../options.ts';
7
+ import type { RuntimeState } from '../runtime.ts';
9
8
  import { panel } from '../strings.ts';
10
9
 
11
- type Row = RuntimePanelState;
10
+ type Row = RuntimeState & { name: string };
12
11
 
13
12
  const POLL_MS = 2_000;
14
13
 
@@ -43,31 +42,18 @@ export const runtimePanel: ConsolePanel = {
43
42
  await load(true);
44
43
  }
45
44
 
46
- async function saveConfig(groupId: string, values: ConfigValues): Promise<void> {
47
- if (busy) return;
48
- busy = true;
49
- message.textContent = '';
50
- try {
51
- await ctx.setConfig(groupId, values);
52
- } catch (error) {
53
- message.textContent = String(error);
54
- } finally {
55
- busy = false;
56
- }
57
- await load(true);
58
- }
59
-
60
- function renderConfig(row: Row, index: number, body: HTMLElement): void {
61
- const { group, values } = row.config[index];
62
- body.append(ui.section(group.schema.title));
63
- for (const [path, property] of Object.entries(group.schema.properties)) {
64
- const field = configField(ui, property, values[path], () => {
65
- if (field.read) void saveConfig(group.id, { [path]: field.read() });
66
- }, ctx.signal);
67
- field.node.setAttribute('aria-label', property.title);
68
- body.append(ui.field(property.title, field.node));
69
- if (property.description) body.append(ui.msgline(property.description));
70
- }
45
+ /** One launch field; empty input sends NaN so the endpoint reports the same error as a bad value. */
46
+ function launchNumber(row: Row, key: keyof LaunchOptions, label: string, min: number): HTMLElement {
47
+ const current = row.launch?.[key];
48
+ const input = ui.input({
49
+ type: 'number',
50
+ value: current === undefined ? '' : String(current),
51
+ onChange: (value) => void act('configure', { launch: { [key]: Number(value) } }),
52
+ });
53
+ input.min = String(min);
54
+ input.step = '1';
55
+ input.setAttribute('aria-label', label);
56
+ return ui.field(label, input);
71
57
  }
72
58
 
73
59
  function renderUnmanaged(row: Row, body: HTMLElement): void {
@@ -88,8 +74,42 @@ export const runtimePanel: ConsolePanel = {
88
74
  : install.phase === 'extracting' ? ui.pill(S.extracting, 'plain')
89
75
  : ui.pill(S.absent, 'off');
90
76
 
91
- renderConfig(row, 0, body);
92
- body.append(ui.kv([{ k: S.installStatus, v: installPill }]));
77
+ body.append(ui.section(S.runtimeSection, S.runtimeSectionDesc));
78
+ const release = ui.input({
79
+ value: row.release ?? '',
80
+ cls: 'mono',
81
+ onChange: (value) => void act('configure', { release: value }),
82
+ });
83
+ release.setAttribute('aria-label', S.release);
84
+ const backend = ui.select({
85
+ value: row.backend ?? '',
86
+ options: row.backendChoices,
87
+ onChange: (value) => void act('configure', { backend: value }),
88
+ });
89
+ backend.setAttribute('aria-label', S.backend);
90
+ backend.disabled = row.own;
91
+ const dir = ui.input({
92
+ value: row.own ? row.runtimeDir ?? '' : '',
93
+ cls: 'mono',
94
+ onChange: (value) => void act('configure', { runtimeDir: value }),
95
+ });
96
+ dir.setAttribute('aria-label', S.runtimeDir);
97
+ const dirRow = ui.rowbar();
98
+ dirRow.append(dir, ui.button(S.browse, {
99
+ size: 'sm',
100
+ onClick: async () => {
101
+ const picked = await ctx.pickPath({ kind: 'directory', title: S.runtimeDir, currentPath: dir.value });
102
+ if (picked) await act('configure', { runtimeDir: picked });
103
+ },
104
+ }));
105
+ body.append(
106
+ ui.field(S.release, release),
107
+ ui.msgline(S.releaseHint),
108
+ ui.field(S.backend, backend),
109
+ ui.field(S.runtimeDir, dirRow),
110
+ ui.msgline(S.runtimeDirHint),
111
+ ui.kv([{ k: S.installStatus, v: installPill }]),
112
+ );
93
113
  if (row.smartAppControl === 1) body.append(ui.msgline(S.sacWarning, true));
94
114
  if (install.phase === 'downloading' || install.phase === 'extracting') {
95
115
  const progress = ui.progress({
@@ -113,7 +133,28 @@ export const runtimePanel: ConsolePanel = {
113
133
  installBar.append(ui.h('span', 'grow'), ui.button(S.disable, { onClick: () => void act('disable') }));
114
134
  body.append(installBar);
115
135
 
116
- renderConfig(row, 1, body);
136
+ body.append(ui.section(S.launchSection, S.launchSectionDesc));
137
+ const launchRow = ui.rowbar();
138
+ launchRow.append(
139
+ launchNumber(row, 'contextSize', S.contextSize, 1),
140
+ launchNumber(row, 'nGpuLayers', S.nGpuLayers, 0),
141
+ launchNumber(row, 'parallel', S.parallel, 1),
142
+ );
143
+ const extraArgs = ui.input({
144
+ value: row.launch?.extraArgs ?? '',
145
+ cls: 'mono',
146
+ onChange: (value) => void act('configure', { launch: { extraArgs: value } }),
147
+ });
148
+ extraArgs.setAttribute('aria-label', S.extraArgs);
149
+ body.append(
150
+ launchRow,
151
+ ui.field(S.extraArgs, extraArgs),
152
+ ui.msgline(S.extraArgsHint),
153
+ ui.checkbox(S.autoStart, {
154
+ checked: row.autoStart,
155
+ onChange: (checked) => void act('configure', { autoStart: checked }),
156
+ }).el,
157
+ );
117
158
 
118
159
  body.append(ui.section(S.serverSection, S.serverSectionDesc));
119
160
  const server = row.server;
@@ -8,9 +8,6 @@ import type { ProviderConsoleHost } from '../../console/types.ts';
8
8
  import type { RouterCatalog, RouterModel } from '../catalog.ts';
9
9
  import { LAUNCH_DEFAULTS, PINNED_RELEASE, backendChoices, defaultBackend, llamacppOptions, type LaunchOptions } from '../options.ts';
10
10
  import type { LlamaRuntime } from '../runtime.ts';
11
- import type { RuntimeState } from '../runtime.ts';
12
- import { runtimeConfig } from '../config.ts';
13
- import { getByPath, type ConfigGroup, type ConfigValues } from '../../../core/config-schema.ts';
14
11
  import { text } from '../strings.ts';
15
12
 
16
13
  interface Control {
@@ -26,11 +23,6 @@ export interface ModelsState {
26
23
  models: RouterModel[];
27
24
  }
28
25
 
29
- export type RuntimePanelState = RuntimeState & {
30
- name: string;
31
- config: Array<{ group: ConfigGroup; values: ConfigValues }>;
32
- };
33
-
34
26
  /** The launch fields the panel edits, each optional and applied over the stored values. */
35
27
  type LaunchPatch = Partial<Record<keyof LaunchOptions, unknown>>;
36
28
 
@@ -50,7 +42,6 @@ export function llamacppConsole(host: ProviderConsoleHost): Partial<ConsolePageC
50
42
  return found.entry;
51
43
  };
52
44
  return {
53
- config: [],
54
45
  panels: [
55
46
  { id: 'runtime', title: S.runtimePanel, description: S.runtimePanelDescription, slot: 'instance' },
56
47
  { id: 'models', title: S.modelsPanel, description: S.modelsPanelDescription, slot: 'instance' },
@@ -59,17 +50,7 @@ export function llamacppConsole(host: ProviderConsoleHost): Partial<ConsolePageC
59
50
  if (panel === 'runtime') {
60
51
  const value = body(args);
61
52
  const name = value.name as string;
62
- if (method === 'state') {
63
- const entry = entryOf(name);
64
- const prefix = `providers.${name}.`;
65
- const config = runtimeConfig(name, entry, host.language).map((group) => ({
66
- group,
67
- values: Object.fromEntries(Object.keys(group.schema.properties).map((path) => [
68
- path, getByPath(entry as unknown as Record<string, unknown>, path.slice(prefix.length)) ?? '',
69
- ])) as ConfigValues,
70
- }));
71
- return { name, ...(await control(name).runtime.state(host.language)), config } satisfies RuntimePanelState;
72
- }
53
+ if (method === 'state') return { name, ...(await control(name).runtime.state(host.language)) };
73
54
  if (method === 'enable') {
74
55
  const entry = entryOf(name);
75
56
  const options = llamacppOptions(entry);
@@ -4,7 +4,6 @@ import type { Language } from '../../core/language.ts';
4
4
  import { isContextOverflow } from '../transport/errors.ts';
5
5
  import { modelsRoot, runtimesRoot } from '../../paths.ts';
6
6
  import { RouterCatalog } from './catalog.ts';
7
- import { runtimeConfig } from './config.ts';
8
7
  import { llamacppConsole } from './console/server.ts';
9
8
  import { LlamaCppProvider } from './native.ts';
10
9
  import { backendChoices, llamacppOptions, normalizeLlamaCpp } from './options.ts';
@@ -28,7 +27,6 @@ export default {
28
27
  baseUrlSuggestions: ['http://127.0.0.1:8090/v1', 'http://127.0.0.1:8080/v1'],
29
28
  normalize: normalizeLlamaCpp,
30
29
  console: llamacppConsole,
31
- config: runtimeConfig,
32
30
  reasoningTiers: reasoningTiers('zh'),
33
31
  localize: (language) => ({ reasoningTiers: reasoningTiers(language) }),
34
32
  serviceTiers: [],
@@ -42,8 +42,6 @@ export function normalizeLlamaCpp(entry: LLMProviderEntry): LLMProviderEntry {
42
42
  if (options.runtime && typeof options.runtime === 'object') {
43
43
  const runtime = options.runtime as Partial<RuntimeOptions>;
44
44
  options.runtime = { ...runtime, release: runtime.release ?? PINNED_RELEASE, backend: runtime.backend ?? defaultBackend() };
45
- if (typeof options.runtime.release === 'string') options.runtime.release = options.runtime.release.trim();
46
- if (typeof options.runtime.runtimeDir === 'string') options.runtime.runtimeDir = options.runtime.runtimeDir.trim();
47
45
  if (options.runtime.runtimeDir === '') delete options.runtime.runtimeDir;
48
46
  options.launch = { ...LAUNCH_DEFAULTS, ...(options.launch ?? {}) };
49
47
  }
@@ -101,9 +101,9 @@ const panelZh = {
101
101
  '启动失败时可在 Windows 安全中心核对拦截记录;若确认被拦截,请使用符合信任要求的运行时。',
102
102
  server: '服务进程',
103
103
  serverSection: '服务进程与端点',
104
- serverSectionDesc: '',
104
+ serverSectionDesc: 'llama-server 本地服务管理与监听状态。',
105
105
  runtimeSection: '二进制运行环境',
106
- runtimeSectionDesc: '',
106
+ runtimeSectionDesc: '官方 llama.cpp 二进制包与运行依赖管理。',
107
107
  releaseHint: '上游 release tag,如 b10930;换了要重新下载。',
108
108
  backendHint: '按本机平台给出的官方构建;CUDA 版自动搭配上游的 cudart 包。',
109
109
  runtimeDirHint: '填了就不下载:目录里要有 llama-server 及其动态库(自编译、Linux CUDA、签过名的构建)。',
@@ -141,7 +141,7 @@ const panelZh = {
141
141
  pullPlaceholder: 'HuggingFace 仓库,如 ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
142
142
  localDir: '本机 GGUF 目录',
143
143
  cacheDir: '缓存目录',
144
- noModels: '暂无 GGUF 模型。可下载模型,或将 .gguf 文件放入本机目录后重新扫描。',
144
+ noModels: '暂无可用的 GGUF 模型:可通过上方输入拉取,或将 .gguf 文件放入本机目录后点击重扫。',
145
145
  modelStatusLabel: {
146
146
  loaded: '已加载',
147
147
  loading: '加载中',
@@ -175,9 +175,9 @@ const panelEn: typeof panelZh = {
175
175
  'If startup fails, check Windows Security for a block record. If a block is confirmed, use a runtime that meets the trust requirements.',
176
176
  server: 'Server',
177
177
  serverSection: 'Server Process & Endpoint',
178
- serverSectionDesc: '',
178
+ serverSectionDesc: 'llama-server local process and listening status.',
179
179
  runtimeSection: 'Binary Runtime Environment',
180
- runtimeSectionDesc: '',
180
+ runtimeSectionDesc: 'Official llama.cpp binary and dependencies.',
181
181
  releaseHint: 'Upstream release tag, such as b10930; changing it downloads another build.',
182
182
  backendHint: 'Official builds for this platform; CUDA builds are paired with the upstream cudart package.',
183
183
  runtimeDirHint: 'When set nothing is downloaded: the directory holds llama-server and its shared libraries (self-built, Linux CUDA, signed builds).',
@@ -215,7 +215,7 @@ const panelEn: typeof panelZh = {
215
215
  pullPlaceholder: 'HuggingFace repo, e.g. ggml-org/Qwen2.5-Coder-7B-Instruct-GGUF',
216
216
  localDir: 'Local GGUF directory',
217
217
  cacheDir: 'Cache directory',
218
- noModels: 'No GGUF models. Download a model, or place GGUF files in the local directory and rescan.',
218
+ noModels: 'No models yet: pull one above, or drop GGUF files into the local directory and rescan.',
219
219
  modelStatusLabel: {
220
220
  loaded: 'loaded',
221
221
  loading: 'loading',
@@ -3,7 +3,6 @@ import type { LLMProviderEntry } from '../../core/types.ts';
3
3
  import { isContextOverflow } from '../transport/errors.ts';
4
4
  import { ModelCatalog, ResponsesProvider } from './native.ts';
5
5
  import { text } from './strings.ts';
6
- import { protocolConfig } from './config.ts';
7
6
 
8
7
  /** Suggested base URLs for the console field; operators may enter other URLs. */
9
8
  const BASE_URLS: readonly string[] = [
@@ -46,8 +45,6 @@ export default {
46
45
  defaultBaseUrl: BASE_URLS[0],
47
46
  baseUrlSuggestions: BASE_URLS,
48
47
  normalize: normalizeCompat,
49
- config: protocolConfig,
50
- console: () => ({ config: [] }),
51
48
  reasoningTiers: [],
52
49
  effortSuggestions: EFFORTS,
53
50
  serviceTiers: [],
package/src/web/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Owner: src/web/server.ts, src/web/shared/console-protocol.ts, src/web/shared/client-panel.ts, src/web/client/console-pages/builtins/llm-settings/panel.ts -->
1
+ <!-- Owner: src/web/server.ts, src/web/shared/console-protocol.ts, src/web/shared/client-panel.ts -->
2
2
 
3
3
  # src/web
4
4
 
@@ -50,13 +50,9 @@ manifest 的 `CONSOLE_PROTOCOL_VERSION` 不匹配时,浏览器拒绝加载。
50
50
  扩展面板通过 `/assets/extensions/<包>/<版本>/<文件>` 提供,仅允许 manifest 声明的脚本与样式文件。
51
51
 
52
52
  `createBot()` 通过 `WebAppDeps` 注入事件库、session、运行控制、配置、存储、`consolePageSources` 和扩展信息。
53
- 调试流以 `head` 提供本次 `Persona.sessionHead()` 的合成开头;它不属于持久 session。
54
- `providersLamp` 汇总本地端点可用性,由状态灯接口提供给浏览器。
55
53
 
56
54
  ## 浏览器
57
55
 
58
- `llm-settings` 从端点状态中的 ConfigGroup 渲染连接字段与声明的请求路径,使用 `ctx.setConfig` 保存。
59
-
60
56
  `main.ts` 的 `FEATURES` 包含 `live`、`core`、`usage`、`provider`、`world`、`extensions`、`prompts`、
61
57
  `appearance`、`settings`。贡献页由 manifest 加载,保留路由段 `provider` 交由 `ConsolePageHost` 处理。
62
58
  `features/config/view.ts` 与 `features/storage/view.ts` 是配置组与存储清单的通用视图,运行诊断页与