@chatcode/chatcode-cli 3.0.1 → 3.0.2-rc.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.
- package/README.i18n.yaml +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/{command-errors-BCYv4XyX.js → command-errors-CIYn1nnW.js} +12 -12
- package/lib/{command-trees-CwZW1MG9.js → command-trees-8o99Bws7.js} +2 -2
- package/lib/{component-identity-D46gUHyM.js → component-identity-CnmIKHEF.js} +3 -3
- package/lib/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
- package/lib/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
- package/lib/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
- package/lib/dsh-ecosystem-spec/registry/README.md +14 -0
- package/lib/dsh-ecosystem-spec/registry/contracts/decision-events-v1alpha1.json +13 -0
- package/lib/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
- package/lib/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
- package/lib/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
- package/lib/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
- package/lib/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -0
- package/lib/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -0
- package/lib/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
- package/lib/dsh-ecosystem-spec/schemas/conformance-claim.schema.json +15 -0
- package/lib/dsh-ecosystem-spec/schemas/effect-ledger-record.schema.json +16 -0
- package/lib/dsh-ecosystem-spec/schemas/host-descriptor.schema.json +26 -0
- package/lib/{extension-events-CBuXbK4l.js → extension-events-DciGPKJa.js} +20 -20
- package/lib/{host-access-BtgVRb0u.js → host-access-CmD27uhQ.js} +1 -1
- package/lib/{keymap-AJ-DGy9P.js → keymap-CFChEJ2W.js} +2 -2
- package/lib/{plugin-ByRs5LzO.js → plugin-jvd9PCVt.js} +333 -333
- package/lib/{scenes-C8DOzNbk.js → scenes-ai_8sHDs.js} +3 -3
- package/lib/{settings-sections-ByzvgqAp.js → settings-sections-QQ1-2lhL.js} +3 -3
- package/lib/{themes-By_OcIPQ.js → themes-DTzG_9I_.js} +12 -12
- package/lib/tui-compat-command-trees.js +1 -1
- package/lib/tui-compat-extensions.js +3 -3
- package/lib/tui-compat-plugin-host.js +5 -5
- package/lib/tui-compat-scenes.js +1 -1
- package/lib/tui-compat-settings-sections.js +1 -1
- package/lib/tui-compat-working-activity.js +1 -1
- package/lib/tui-compat-workspaces.js +1 -1
- package/lib/tui-compat.js +5 -5
- package/lib/{tuiDisplayPrefs-B-NDEV0N.js → tuiDisplayPrefs-BCHRRcfe.js} +1 -1
- package/lib/{workspaces-ZRM_mslc.js → workspaces-CznCgVHv.js} +2 -2
- package/package.json +39 -33
- package/runtime/manifest.json +2 -6
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-account/lib/client.js +2 -2
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-skill/lib/types/index.d.ts +1 -1
- package/runtime/node_modules/@deepseek-ai/dsh-skill-filesystem/lib/index.js +7 -1
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.md +0 -9
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/bin/system.node +0 -0
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/package.json +0 -31
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/prebuilds.json +0 -6
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.md +0 -9
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-x64/package.json +0 -31
- package/runtime/node_modules/@deepseek-ai/node-addon-system-darwin-x64/prebuilds.json +0 -6
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.md +0 -11
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-arm64/package.json +0 -31
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-arm64/prebuilds.json +0 -24
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.md +0 -11
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-x64/package.json +0 -31
- package/runtime/node_modules/@deepseek-ai/node-addon-system-linux-x64/prebuilds.json +0 -24
package/README.i18n.yaml
CHANGED
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Production runs require built package and frontend artifacts. From the repositor
|
|
|
71
71
|
|
|
72
72
|
The published CLI directly pins every Harness runtime package referenced by the base/Web bundles and every required peer of its bundled ChatCode plugins. Keep those entries on the same Harness release when publishing: npm's global installer does not preserve pnpm workspace peer unification, and otherwise may hoist an older ABI beside the current runtime.
|
|
73
73
|
|
|
74
|
-
The npm artifact carries fork-owned packages under `runtime/node_modules` and lists them in `runtime/manifest.json`. Profile startup includes
|
|
74
|
+
The npm artifact carries fork-owned JavaScript packages under `runtime/node_modules` and lists them in `runtime/manifest.json`. Platform-specific `node-addon-system` binaries remain exact-version optional npm dependencies, selected for the installation host; they are excluded from the embedded runtime. It also carries the TUI admission registry under `lib/dsh-ecosystem-spec`, where command authorization reads its permission defaults before applying the user's grants file. Profile startup includes the complete runtime package list when resolving presets and plugins, including packages absent from the slim npm dependency list. The launcher resolves embedded packages in memory for both ESM and CommonJS, including imports from external plugins. Startup does not modify the installation directory, so a root-owned installation can be launched by an ordinary user with read access; the user still needs a writable ChatCode CLI home. CLI and Web share the [startup model preference](../../packages/core/agent-default-model/README.md); already running frontends do not switch each other's model.
|
|
75
75
|
|
|
76
76
|
The `@chatcode/chatcode-cli/profile-boot` export provides the shared profile lifecycle to the Desktop host. A resolved application profile supplies its own installation anchor for runtime package resolution while retaining the ChatCode CLI home patch, proxy environment, telemetry switch, patch reload, and bounded shutdown.
|
|
77
77
|
|
package/README.zh.md
CHANGED
|
@@ -71,7 +71,7 @@ profile 目录包含一个 `package.json`,其中记录树外插件依赖,以
|
|
|
71
71
|
|
|
72
72
|
发布版 CLI 会直接固定 base/Web 组合包实际引用的所有 Harness 运行时包,以及随附 ChatCode 插件的全部必需 peer。发布时必须让这些依赖保持在同一个 Harness 版本:npm 全局安装不会保留 pnpm workspace 对 peer 的统一解析,否则可能把旧 ABI 提升到顶层并与当前运行时混用。
|
|
73
73
|
|
|
74
|
-
npm
|
|
74
|
+
npm 产物把分支自有的 JavaScript 包放在 `runtime/node_modules`,并通过 `runtime/manifest.json` 列出它们。各平台的 `node-addon-system` 二进制使用固定版本的 npm 可选依赖,由安装器按目标机器的平台选择,不加入内嵌 runtime。产物还在 `lib/dsh-ecosystem-spec` 中携带 TUI 准入注册表;命令授权先从该目录读取权限默认值,再应用用户的授权文件。Profile 启动解析预设和插件时使用完整的运行时包列表,包括精简 npm 依赖列表中没有的包。启动器在内存中统一解析内嵌包的 ESM 与 CommonJS 导入,也覆盖外部插件的导入。启动不会修改安装目录,因此普通用户只需对 root 安装的目录具有读取权限;用户自己的 ChatCode CLI home 仍须可写。CLI 与 Web 共用[启动模型偏好](../../packages/core/agent-default-model/README.zh.md);已运行的两端不会互相切换模型。
|
|
75
75
|
|
|
76
76
|
`@chatcode/chatcode-cli/profile-boot` 导出向 Desktop Host 提供共享 profile 生命周期。已解析的应用 profile 为运行时包解析指定自己的安装锚点,同时沿用 ChatCode CLI home patch、代理环境、遥测开关、patch 热重载和有界关闭。
|
|
77
77
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { a as compositionRoot, i as bindCallerEffect, o as concreteService, r as assertCallerContext, t as activationContext } from "./host-access-
|
|
2
|
-
import { a as declaresPermission, d as normalizePermissionScope, i as declaresObserverScope, m as scopeCovers, n as componentIdentityOf, o as requireComponentIdentity, p as rawScopeCovers, s as requiresContract, u as INTERCEPT_PERMISSIONS } from "./component-identity-
|
|
3
|
-
import { B as createAdmissionCatalog, G as validateSetInput, H as validateDeleteOutput, K as validateSetOutput, L as TUI_DECISION_EVENT_NAMES, M as loadSpecData, N as registryEntries, P as verifyContractProfiles, S as DATA_DIR, U as validateGetInput, V as validateDeleteInput, W as validateGetOutput, X as defineProtocolDeclaration, Y as projectManifest, j as digestFile, p as cleanScalarText, q as validateMessageEvent, x as readGrantStore } from "./extension-events-
|
|
1
|
+
import { a as compositionRoot, i as bindCallerEffect, o as concreteService, r as assertCallerContext, t as activationContext } from "./host-access-CmD27uhQ.js";
|
|
2
|
+
import { a as declaresPermission, d as normalizePermissionScope, i as declaresObserverScope, m as scopeCovers, n as componentIdentityOf, o as requireComponentIdentity, p as rawScopeCovers, s as requiresContract, u as INTERCEPT_PERMISSIONS } from "./component-identity-CnmIKHEF.js";
|
|
3
|
+
import { B as createAdmissionCatalog, G as validateSetInput, H as validateDeleteOutput, K as validateSetOutput, L as TUI_DECISION_EVENT_NAMES, M as loadSpecData, N as registryEntries, P as verifyContractProfiles, S as DATA_DIR, U as validateGetInput, V as validateDeleteInput, W as validateGetOutput, X as defineProtocolDeclaration, Y as projectManifest, j as digestFile, p as cleanScalarText, q as validateMessageEvent, x as readGrantStore } from "./extension-events-DciGPKJa.js";
|
|
4
4
|
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { Service } from "@deepseek-ai/cordis";
|
|
8
8
|
import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
|
|
9
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
9
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/plugin-spec/validate.js
|
|
10
10
|
/** Semantic validation layered on the official manifest parser/projection. */
|
|
11
11
|
const coordinateKey$1 = (coordinate) => `${coordinate.apiVersion}#${coordinate.kind}`;
|
|
12
12
|
const groupOf = (apiVersion) => apiVersion.split("/")[0] ?? "";
|
|
@@ -124,7 +124,7 @@ function validateHost(index, host) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
//#endregion
|
|
127
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
127
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/plugin-spec/negotiate.js
|
|
128
128
|
/** Five-state admission decision using dsh-std projection and ProtocolCatalog. */
|
|
129
129
|
const coordinateKey = (ref) => `${ref.apiVersion}#${ref.kind}`;
|
|
130
130
|
function negotiate(index, manifest, host, grants = []) {
|
|
@@ -197,7 +197,7 @@ function negotiate(index, manifest, host, grants = []) {
|
|
|
197
197
|
} : { decision: "compatible" };
|
|
198
198
|
}
|
|
199
199
|
//#endregion
|
|
200
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
200
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/plugin-spec/schema-check.js
|
|
201
201
|
/**
|
|
202
202
|
* 零依赖 JSON Schema 校验器——上游 `conformance/tests/run.js` 的 check()
|
|
203
203
|
* 的保真 TS 移植。刻意只实现 vendored schemas 用到的子集:本地 $ref、
|
|
@@ -301,7 +301,7 @@ function check(value, schema, rootSchema = schema, where = "$") {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
//#endregion
|
|
304
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
304
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/host-descriptor.js
|
|
305
305
|
/** Build the current ChatCode CLI TUI Host Descriptor from the pinned admission profile. */
|
|
306
306
|
const HOST_SUPPORTED_CONTRACTS = Object.freeze([
|
|
307
307
|
{
|
|
@@ -453,7 +453,7 @@ function buildHostDescriptor(options) {
|
|
|
453
453
|
});
|
|
454
454
|
}
|
|
455
455
|
//#endregion
|
|
456
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
456
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/effect-ledger.js
|
|
457
457
|
/**
|
|
458
458
|
* Effect ledger (C-060): an append-only JSONL journal of plugin-visible
|
|
459
459
|
* effects at `~/.chatcode-cli/tui/effect-ledger.jsonl`, mounted by the
|
|
@@ -645,7 +645,7 @@ function resumeSequence(file) {
|
|
|
645
645
|
return max + 1;
|
|
646
646
|
}
|
|
647
647
|
//#endregion
|
|
648
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
648
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/plugin-storage.js
|
|
649
649
|
/**
|
|
650
650
|
* storage.local contract surface (C-040, `storage.dsh/v1alpha1#LocalStorage`):
|
|
651
651
|
* per-plugin private persistence, mounted by the dsh-tui-plugin-host row as
|
|
@@ -986,7 +986,7 @@ var TuiPluginStorageRuntime = class extends Service {
|
|
|
986
986
|
}
|
|
987
987
|
};
|
|
988
988
|
//#endregion
|
|
989
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
989
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/message-observer.js
|
|
990
990
|
/**
|
|
991
991
|
* messages.observe contract surface (C-042,
|
|
992
992
|
* `messages.dsh/v1alpha1#MessageObserver`): the host event broker, mounted
|
|
@@ -1494,7 +1494,7 @@ function observerStateFor(runtime) {
|
|
|
1494
1494
|
return state;
|
|
1495
1495
|
}
|
|
1496
1496
|
//#endregion
|
|
1497
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
1497
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/command-attribution.js
|
|
1498
1498
|
/**
|
|
1499
1499
|
* Command ownership attribution — the data source for the C-041 invoke
|
|
1500
1500
|
* checkpoint's per-owner grant decision.
|
|
@@ -1572,7 +1572,7 @@ function commandOwner(ctx, definition) {
|
|
|
1572
1572
|
return ownerMaps.get(key)?.get(handler);
|
|
1573
1573
|
}
|
|
1574
1574
|
//#endregion
|
|
1575
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
1575
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/command-errors.js
|
|
1576
1576
|
/**
|
|
1577
1577
|
* commands contract error-code alignment (C-041,
|
|
1578
1578
|
* `commands.dsh/v1alpha1#Command`): the official `dsh-commands` runtime
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as compositionRoot, i as bindCallerEffect, n as activationFiber, o as concreteService, s as requirePluginCaller } from "./host-access-
|
|
1
|
+
import { a as compositionRoot, i as bindCallerEffect, n as activationFiber, o as concreteService, s as requirePluginCaller } from "./host-access-CmD27uhQ.js";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
3
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/command-trees.js
|
|
4
4
|
/** Provider-neutral subcommand completion registry for terminal front doors. */
|
|
5
5
|
const name = "dsh-tui-command-trees";
|
|
6
6
|
/** Small host-only registry; command execution remains owned by dsh-commands. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as bindCallerEffect, n as activationFiber } from "./host-access-
|
|
1
|
+
import { i as bindCallerEffect, n as activationFiber } from "./host-access-CmD27uhQ.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
3
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/plugin-spec/permission-scope.js
|
|
4
4
|
/** Permission scope normalization shared by admission and runtime checks. */
|
|
5
5
|
const STORAGE_PERMISSIONS = new Set(["storage.local.read", "storage.local.write"]);
|
|
6
6
|
const INTERCEPT_PERMISSIONS = new Set([
|
|
@@ -76,7 +76,7 @@ function rawScopeCovers(permission, declared, actual, componentId) {
|
|
|
76
76
|
return normalizedDeclared !== void 0 && normalizedActual !== void 0 && permissionScopeCovers(permission, normalizedDeclared, normalizedActual);
|
|
77
77
|
}
|
|
78
78
|
//#endregion
|
|
79
|
-
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.
|
|
79
|
+
//#region ../../node_modules/.pnpm/@chatcode+cco-tui@0.10.0-rc.9_08ea48c8555976e562c6368d51cb542b/node_modules/@chatcode/cco-tui/lib/types/dsh-adapter/component-identity.js
|
|
80
80
|
/** Verified Component identity bound to one Cordis activation owner. */
|
|
81
81
|
var ComponentIdentityError = class extends Error {
|
|
82
82
|
code;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ApiReference, ProtocolCatalog, ProtocolDefinition } from '@dsh-std/core'
|
|
2
|
+
export { registerTuiContributionExtensions } from './tui-contributions.js'
|
|
3
|
+
export * from './tui-channel.js'
|
|
4
|
+
|
|
5
|
+
export const DECISION_EVENTS: Readonly<ApiReference & {
|
|
6
|
+
apiVersion: 'tui.dsh/v1alpha1'
|
|
7
|
+
kind: 'DecisionEvents'
|
|
8
|
+
}>
|
|
9
|
+
|
|
10
|
+
export const decisionEventsDefinition: ProtocolDefinition
|
|
11
|
+
export const profileDefinitions: readonly ProtocolDefinition[]
|
|
12
|
+
|
|
13
|
+
export function registerProfileProtocols(catalog: ProtocolCatalog): () => void
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** dsh-TUI private protocol definitions evaluated by @dsh-std/core. */
|
|
2
|
+
|
|
3
|
+
export { registerTuiContributionExtensions } from './tui-contributions.js'
|
|
4
|
+
export * from './tui-channel.js'
|
|
5
|
+
import { tuiChannelDefinition } from './tui-channel.js'
|
|
6
|
+
|
|
7
|
+
export const DECISION_EVENTS = Object.freeze({
|
|
8
|
+
apiVersion: 'tui.dsh/v1alpha1',
|
|
9
|
+
kind: 'DecisionEvents',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
function featureSupport(value, label) {
|
|
13
|
+
if (value === undefined) return Object.freeze({ features: Object.freeze([]) })
|
|
14
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
15
|
+
throw new TypeError(`${label} support spec must be an object`)
|
|
16
|
+
}
|
|
17
|
+
const unknown = Object.keys(value).filter(key => key !== 'features' && !key.startsWith('x-'))
|
|
18
|
+
if (unknown.length > 0) throw new TypeError(`${label} support spec contains unknown field ${JSON.stringify(unknown[0])}`)
|
|
19
|
+
if (value.features !== undefined && (!Array.isArray(value.features)
|
|
20
|
+
|| value.features.some(feature => typeof feature !== 'string' || feature.trim() === ''))) {
|
|
21
|
+
throw new TypeError(`${label} support spec.features must be an array of non-empty strings`)
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze({ features: Object.freeze([...(value.features ?? [])].sort()) })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function providerDefinition(reference, label) {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
...reference,
|
|
29
|
+
validateRequirement(value) {
|
|
30
|
+
if (value !== undefined) throw new TypeError(`${label} requirement does not accept spec in manifest v0.15`)
|
|
31
|
+
return undefined
|
|
32
|
+
},
|
|
33
|
+
validateSupport(value) { return featureSupport(value, label) },
|
|
34
|
+
negotiate(input) {
|
|
35
|
+
const providers = [...new Set(input.supports.map(row => row.participant))].sort()
|
|
36
|
+
const issues = input.requirements.flatMap(row => providers.length === 0
|
|
37
|
+
? [{
|
|
38
|
+
code: row.requirement.optional === true ? 'optional-support-missing' : 'required-support-missing',
|
|
39
|
+
severity: row.requirement.optional === true ? 'warning' : 'error',
|
|
40
|
+
participant: row.participant,
|
|
41
|
+
message: `${label} has no provider in this negotiation scope`,
|
|
42
|
+
}]
|
|
43
|
+
: [])
|
|
44
|
+
return { agreement: Object.freeze({ providers: Object.freeze(providers) }), issues: Object.freeze(issues) }
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const decisionEventsDefinition = providerDefinition(DECISION_EVENTS, 'DecisionEvents')
|
|
50
|
+
|
|
51
|
+
export const profileDefinitions = Object.freeze([
|
|
52
|
+
decisionEventsDefinition,
|
|
53
|
+
tuiChannelDefinition,
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
export function registerProfileProtocols(catalog) {
|
|
57
|
+
const disposers = profileDefinitions.map(definition => catalog.register(definition))
|
|
58
|
+
return () => { for (const dispose of disposers.reverse()) dispose() }
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const TUI_CHANNEL_HTTP_API_VERSION: 'tui.dsh/v1alpha1'
|
|
2
|
+
export const TUI_CHANNEL_HTTP_KIND: 'ChannelEndpoint'
|
|
3
|
+
export const TUI_CHANNEL_HTTP_HEALTH_PATH: '/dsh-tui/v1/health'
|
|
4
|
+
export const TUI_CHANNEL_HTTP_DESCRIPTOR_PATH: '/dsh-tui/v1/endpoint'
|
|
5
|
+
export const TUI_CHANNEL_HTTP_WEBSOCKET_PATH: '/dsh-tui/v1/connection'
|
|
6
|
+
|
|
7
|
+
export interface TuiChannelEndpointDescriptor {
|
|
8
|
+
readonly apiVersion: typeof TUI_CHANNEL_HTTP_API_VERSION
|
|
9
|
+
readonly kind: typeof TUI_CHANNEL_HTTP_KIND
|
|
10
|
+
readonly origin: string
|
|
11
|
+
readonly connection: typeof TUI_CHANNEL_HTTP_WEBSOCKET_PATH
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createTuiChannelEndpointDescriptor(origin: string): TuiChannelEndpointDescriptor
|
|
15
|
+
export function validateTuiChannelEndpointDescriptor(value: unknown): TuiChannelEndpointDescriptor
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const TUI_CHANNEL_HTTP_API_VERSION = 'tui.dsh/v1alpha1'
|
|
2
|
+
export const TUI_CHANNEL_HTTP_KIND = 'ChannelEndpoint'
|
|
3
|
+
export const TUI_CHANNEL_HTTP_HEALTH_PATH = '/dsh-tui/v1/health'
|
|
4
|
+
export const TUI_CHANNEL_HTTP_DESCRIPTOR_PATH = '/dsh-tui/v1/endpoint'
|
|
5
|
+
export const TUI_CHANNEL_HTTP_WEBSOCKET_PATH = '/dsh-tui/v1/connection'
|
|
6
|
+
|
|
7
|
+
export function createTuiChannelEndpointDescriptor(origin) {
|
|
8
|
+
const normalized = new URL(origin)
|
|
9
|
+
if (normalized.protocol !== 'http:' && normalized.protocol !== 'https:') {
|
|
10
|
+
throw new TypeError('TUI Channel endpoint origin must use http or https')
|
|
11
|
+
}
|
|
12
|
+
normalized.pathname = '/'
|
|
13
|
+
normalized.search = ''
|
|
14
|
+
normalized.hash = ''
|
|
15
|
+
return Object.freeze({
|
|
16
|
+
apiVersion: TUI_CHANNEL_HTTP_API_VERSION,
|
|
17
|
+
kind: TUI_CHANNEL_HTTP_KIND,
|
|
18
|
+
origin: normalized.toString().replace(/\/$/u, ''),
|
|
19
|
+
connection: TUI_CHANNEL_HTTP_WEBSOCKET_PATH,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function validateTuiChannelEndpointDescriptor(value) {
|
|
24
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new TypeError('TUI Channel endpoint descriptor must be an object')
|
|
25
|
+
if (value.apiVersion !== TUI_CHANNEL_HTTP_API_VERSION || value.kind !== TUI_CHANNEL_HTTP_KIND) {
|
|
26
|
+
throw new TypeError('TUI Channel endpoint descriptor version or kind is unsupported')
|
|
27
|
+
}
|
|
28
|
+
if (typeof value.origin !== 'string') throw new TypeError('TUI Channel endpoint origin must be a string')
|
|
29
|
+
createTuiChannelEndpointDescriptor(value.origin)
|
|
30
|
+
if (value.connection !== TUI_CHANNEL_HTTP_WEBSOCKET_PATH) throw new TypeError('TUI Channel endpoint connection path is unsupported')
|
|
31
|
+
return value
|
|
32
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ApiReference, ProtocolDefinition } from '@dsh-std/core'
|
|
2
|
+
|
|
3
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | { readonly [key: string]: JsonValue }
|
|
4
|
+
|
|
5
|
+
export const TUI_CHANNEL: Readonly<ApiReference & {
|
|
6
|
+
apiVersion: 'tui.dsh/v1alpha1'
|
|
7
|
+
kind: 'Channel'
|
|
8
|
+
}>
|
|
9
|
+
export const TUI_CHANNEL_WIRE_REVISION: 6
|
|
10
|
+
export const TUI_CHANNEL_FEATURES: readonly string[]
|
|
11
|
+
|
|
12
|
+
export interface TuiChannelRequirementSpec {
|
|
13
|
+
readonly wireRevision: number
|
|
14
|
+
readonly features: readonly string[]
|
|
15
|
+
}
|
|
16
|
+
export interface TuiChannelSupportSpec extends TuiChannelRequirementSpec {}
|
|
17
|
+
|
|
18
|
+
export interface TuiChannelSnapshot {
|
|
19
|
+
readonly wireRevision: number
|
|
20
|
+
readonly channelId: string
|
|
21
|
+
readonly version: number
|
|
22
|
+
readonly state: Readonly<Record<string, JsonValue>>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TuiChannelWorkspaceFlowChoice {
|
|
26
|
+
readonly id: string
|
|
27
|
+
readonly label: string
|
|
28
|
+
readonly description?: string
|
|
29
|
+
readonly badge?: string
|
|
30
|
+
readonly action: string
|
|
31
|
+
readonly input?: {
|
|
32
|
+
readonly action: string
|
|
33
|
+
readonly initialValue?: string
|
|
34
|
+
readonly placeholder?: string
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type TuiChannelWorkspaceFlowResult =
|
|
39
|
+
| { readonly kind: 'target'; readonly target: import('./tui-contributions.js').WorkspaceTarget }
|
|
40
|
+
| { readonly kind: 'choices'; readonly title: string; readonly choices: readonly TuiChannelWorkspaceFlowChoice[] }
|
|
41
|
+
|
|
42
|
+
export type TuiChannelInput =
|
|
43
|
+
| { readonly operation: 'open'; readonly value: { readonly workspace?: string; readonly sessionId?: string; readonly options?: JsonValue } }
|
|
44
|
+
| { readonly operation: 'subscribe'; readonly value: { readonly channelId: string; readonly afterVersion: number } }
|
|
45
|
+
| { readonly operation: 'invoke'; readonly value: { readonly channelId: string; readonly method: string; readonly arguments: readonly JsonValue[] } }
|
|
46
|
+
| { readonly operation: 'close'; readonly value: { readonly channelId: string } }
|
|
47
|
+
|
|
48
|
+
export interface TuiChannelInvokeOutput {
|
|
49
|
+
readonly value: JsonValue
|
|
50
|
+
readonly valueDefined?: boolean
|
|
51
|
+
readonly snapshot?: TuiChannelSnapshot
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const tuiChannelDefinition: ProtocolDefinition
|
|
55
|
+
export function validateTuiChannelRequirement(value: unknown): TuiChannelRequirementSpec
|
|
56
|
+
export function validateTuiChannelSupport(value: unknown): TuiChannelSupportSpec
|
|
57
|
+
export function validateTuiChannelInput(operation: string, value: unknown): object
|
|
58
|
+
export function validateTuiChannelSnapshot(value: unknown): TuiChannelSnapshot
|
|
59
|
+
export function validateTuiChannelOutput(operation: string, value: unknown): object
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { defineCapabilityProtocol } from '@dsh-std/connection'
|
|
2
|
+
|
|
3
|
+
export const TUI_CHANNEL = Object.freeze({
|
|
4
|
+
apiVersion: 'tui.dsh/v1alpha1',
|
|
5
|
+
kind: 'Channel',
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export const TUI_CHANNEL_WIRE_REVISION = 6
|
|
9
|
+
|
|
10
|
+
export const TUI_CHANNEL_FEATURES = Object.freeze([
|
|
11
|
+
'commands',
|
|
12
|
+
'credentials',
|
|
13
|
+
'diagnostics',
|
|
14
|
+
'files',
|
|
15
|
+
'models',
|
|
16
|
+
'modes',
|
|
17
|
+
'presets',
|
|
18
|
+
'presentation',
|
|
19
|
+
'provider-setup',
|
|
20
|
+
'scenes',
|
|
21
|
+
'session-history',
|
|
22
|
+
'session-input',
|
|
23
|
+
'session-lifecycle',
|
|
24
|
+
'session-state',
|
|
25
|
+
'settings',
|
|
26
|
+
'skills',
|
|
27
|
+
'subagents',
|
|
28
|
+
'trace',
|
|
29
|
+
'workspaces',
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
export const tuiChannelDefinition = defineCapabilityProtocol({
|
|
33
|
+
...TUI_CHANNEL,
|
|
34
|
+
validateRequirement: validateTuiChannelRequirement,
|
|
35
|
+
validateSupport: validateTuiChannelSupport,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export function validateTuiChannelRequirement(value) {
|
|
39
|
+
const spec = exactRecord(value, ['wireRevision', 'features'], 'Channel requirement')
|
|
40
|
+
const wireRevision = positiveInteger(spec.wireRevision, 'Channel requirement.wireRevision')
|
|
41
|
+
const features = featureList(spec.features, 'Channel requirement.features')
|
|
42
|
+
return Object.freeze({ wireRevision, features })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function validateTuiChannelSupport(value) {
|
|
46
|
+
const spec = exactRecord(value, ['wireRevision', 'features'], 'Channel support')
|
|
47
|
+
const wireRevision = positiveInteger(spec.wireRevision, 'Channel support.wireRevision')
|
|
48
|
+
const features = featureList(spec.features, 'Channel support.features')
|
|
49
|
+
return Object.freeze({ wireRevision, features })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateTuiChannelInput(operation, value) {
|
|
53
|
+
switch (operation) {
|
|
54
|
+
case 'open': {
|
|
55
|
+
const input = exactRecord(value, ['workspace', 'sessionId', 'options'], 'Channel.open input')
|
|
56
|
+
optionalString(input.workspace, 'Channel.open input.workspace')
|
|
57
|
+
optionalString(input.sessionId, 'Channel.open input.sessionId')
|
|
58
|
+
if (input.options !== undefined) jsonValue(input.options, 'Channel.open input.options', new Set())
|
|
59
|
+
return input
|
|
60
|
+
}
|
|
61
|
+
case 'subscribe': {
|
|
62
|
+
const input = exactRecord(value, ['channelId', 'afterVersion'], 'Channel.subscribe input')
|
|
63
|
+
nonEmpty(input.channelId, 'Channel.subscribe input.channelId')
|
|
64
|
+
nonNegativeInteger(input.afterVersion, 'Channel.subscribe input.afterVersion')
|
|
65
|
+
return input
|
|
66
|
+
}
|
|
67
|
+
case 'invoke': {
|
|
68
|
+
const input = exactRecord(value, ['channelId', 'method', 'arguments'], 'Channel.invoke input')
|
|
69
|
+
nonEmpty(input.channelId, 'Channel.invoke input.channelId')
|
|
70
|
+
nonEmpty(input.method, 'Channel.invoke input.method')
|
|
71
|
+
if (!Array.isArray(input.arguments)) throw new TypeError('Channel.invoke input.arguments must be an array')
|
|
72
|
+
jsonValue(input.arguments, 'Channel.invoke input.arguments', new Set())
|
|
73
|
+
return input
|
|
74
|
+
}
|
|
75
|
+
case 'close': {
|
|
76
|
+
const input = exactRecord(value, ['channelId'], 'Channel.close input')
|
|
77
|
+
nonEmpty(input.channelId, 'Channel.close input.channelId')
|
|
78
|
+
return input
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
throw new TypeError(`Channel operation ${JSON.stringify(operation)} is not defined`)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function validateTuiChannelSnapshot(value) {
|
|
86
|
+
const snapshot = exactRecord(value, ['wireRevision', 'channelId', 'version', 'state'], 'Channel snapshot')
|
|
87
|
+
positiveInteger(snapshot.wireRevision, 'Channel snapshot.wireRevision')
|
|
88
|
+
nonEmpty(snapshot.channelId, 'Channel snapshot.channelId')
|
|
89
|
+
nonNegativeInteger(snapshot.version, 'Channel snapshot.version')
|
|
90
|
+
const state = exactRecord(snapshot.state, Object.keys(snapshot.state ?? {}), 'Channel snapshot.state')
|
|
91
|
+
jsonValue(state, 'Channel snapshot.state', new Set())
|
|
92
|
+
return snapshot
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function validateTuiChannelOutput(operation, value) {
|
|
96
|
+
if (operation === 'open' || operation === 'subscribe') return validateTuiChannelSnapshot(value)
|
|
97
|
+
if (operation === 'invoke') {
|
|
98
|
+
const output = exactRecord(value, ['value', 'valueDefined', 'snapshot'], 'Channel.invoke output')
|
|
99
|
+
jsonValue(output.value, 'Channel.invoke output.value', new Set())
|
|
100
|
+
if (output.valueDefined !== undefined && typeof output.valueDefined !== 'boolean') {
|
|
101
|
+
throw new TypeError('Channel.invoke output.valueDefined must be boolean')
|
|
102
|
+
}
|
|
103
|
+
if (output.snapshot !== undefined) validateTuiChannelSnapshot(output.snapshot)
|
|
104
|
+
return output
|
|
105
|
+
}
|
|
106
|
+
if (operation === 'close') {
|
|
107
|
+
const output = exactRecord(value, ['closed'], 'Channel.close output')
|
|
108
|
+
if (output.closed !== true) throw new TypeError('Channel.close output.closed must be true')
|
|
109
|
+
return output
|
|
110
|
+
}
|
|
111
|
+
throw new TypeError(`Channel operation ${JSON.stringify(operation)} is not defined`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function exactRecord(value, allowed, label) {
|
|
115
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new TypeError(`${label} must be an object`)
|
|
116
|
+
const unknown = Object.keys(value).filter(key => !allowed.includes(key) && !key.startsWith('x-'))
|
|
117
|
+
if (unknown.length > 0) throw new TypeError(`${label} contains unknown field ${JSON.stringify(unknown[0])}`)
|
|
118
|
+
return value
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function featureList(value, label) {
|
|
122
|
+
if (!Array.isArray(value) || value.length === 0) throw new TypeError(`${label} must be a non-empty array`)
|
|
123
|
+
const features = value.map((feature, index) => {
|
|
124
|
+
nonEmpty(feature, `${label}[${index}]`)
|
|
125
|
+
return feature
|
|
126
|
+
})
|
|
127
|
+
if (new Set(features).size !== features.length) throw new TypeError(`${label} contains duplicates`)
|
|
128
|
+
return Object.freeze([...features].sort())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function positiveInteger(value, label) {
|
|
132
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError(`${label} must be a positive integer`)
|
|
133
|
+
return value
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function nonNegativeInteger(value, label) {
|
|
137
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${label} must be a non-negative integer`)
|
|
138
|
+
return value
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function nonEmpty(value, label) {
|
|
142
|
+
if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${label} must be a non-empty string`)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function optionalString(value, label) {
|
|
146
|
+
if (value !== undefined) nonEmpty(value, label)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function jsonValue(value, label, ancestors) {
|
|
150
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'string') return
|
|
151
|
+
if (typeof value === 'number') {
|
|
152
|
+
if (!Number.isFinite(value)) throw new TypeError(`${label} must contain only finite numbers`)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
if (typeof value !== 'object') throw new TypeError(`${label} must be a JSON value`)
|
|
156
|
+
if (ancestors.has(value)) throw new TypeError(`${label} must not contain cycles`)
|
|
157
|
+
ancestors.add(value)
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
for (const [index, item] of value.entries()) jsonValue(item, `${label}[${index}]`, ancestors)
|
|
160
|
+
} else {
|
|
161
|
+
const prototype = Object.getPrototypeOf(value)
|
|
162
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${label} must contain only plain objects`)
|
|
163
|
+
for (const [key, item] of Object.entries(value)) jsonValue(item, `${label}.${key}`, ancestors)
|
|
164
|
+
}
|
|
165
|
+
ancestors.delete(value)
|
|
166
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ApiReference } from '@dsh-std/core'
|
|
2
|
+
import type { ManifestDefinitionCatalog, ManifestObjectDefinition } from '@dsh-std/manifest'
|
|
3
|
+
|
|
4
|
+
export const API_VERSION: 'tui.dsh/v1alpha1'
|
|
5
|
+
export const SETTINGS_SECTION: Readonly<ApiReference & { kind: 'SettingsSection' }>
|
|
6
|
+
export const SCENE: Readonly<ApiReference & { kind: 'Scene' }>
|
|
7
|
+
|
|
8
|
+
export type LocalizedText = Readonly<Record<string, string>>
|
|
9
|
+
export type SettingsFieldKind = 'text' | 'number' | 'boolean' | 'select'
|
|
10
|
+
export interface SettingsFieldOption { readonly value: string; readonly label: string; readonly titles?: LocalizedText }
|
|
11
|
+
export interface SettingsFieldSpec {
|
|
12
|
+
readonly path: readonly string[]
|
|
13
|
+
readonly label: string
|
|
14
|
+
readonly titles?: LocalizedText
|
|
15
|
+
readonly hint?: string
|
|
16
|
+
readonly hintTitles?: LocalizedText
|
|
17
|
+
readonly kind: SettingsFieldKind
|
|
18
|
+
readonly options?: readonly SettingsFieldOption[]
|
|
19
|
+
readonly placeholder?: string
|
|
20
|
+
readonly secretRef?: string
|
|
21
|
+
}
|
|
22
|
+
export interface SettingsSectionSpec {
|
|
23
|
+
readonly namespace: string
|
|
24
|
+
readonly title: string
|
|
25
|
+
readonly titles?: LocalizedText
|
|
26
|
+
readonly fields: readonly SettingsFieldSpec[]
|
|
27
|
+
}
|
|
28
|
+
export interface SettingsSectionHandler {}
|
|
29
|
+
|
|
30
|
+
export interface SceneSpec { readonly title?: string; readonly titles?: LocalizedText }
|
|
31
|
+
export interface SceneHandler<Props = unknown, Result = unknown> {
|
|
32
|
+
readonly component: (props: Props) => Result
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const settingsSectionExtensionDefinition: ManifestObjectDefinition
|
|
36
|
+
export const sceneExtensionDefinition: ManifestObjectDefinition
|
|
37
|
+
export const contributionExtensionDefinitions: readonly ManifestObjectDefinition[]
|
|
38
|
+
export function validateSettingsSectionSpec(value: unknown): Readonly<SettingsSectionSpec>
|
|
39
|
+
export function validateSceneSpec(value: unknown): Readonly<SceneSpec>
|
|
40
|
+
export function registerTuiContributionExtensions(catalog: ManifestDefinitionCatalog): () => void
|
|
41
|
+
export function assertSettingsSectionHandler(value: unknown): asserts value is SettingsSectionHandler
|
|
42
|
+
export function assertSceneHandler(value: unknown): asserts value is SceneHandler
|