fix(types): Desktop type safety hardening (TYPE-01)
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- Unify ConnectionState: kernel-types.ts now canonical source with 'handshaking', gateway-types.ts re-exports - PromptTemplateInfo source/status → union literals - PromptVariable.type → union literal - CreateRoleRequest id/permissions → optional - PropertyPanel: replace 13 as any with typed accessor pattern - chatStore: window cast via as unknown as Record
This commit is contained in:
@@ -87,8 +87,13 @@ function renderTypeSpecificFields(
|
||||
data: Partial<WorkflowNodeData>,
|
||||
onChange: (field: string, value: unknown) => void
|
||||
) {
|
||||
// Type-safe property accessor for union-typed node data
|
||||
// Type-safe property accessors for union-typed node data
|
||||
const d = data as Record<string, unknown>;
|
||||
const str = (key: string): string => (d[key] as string) || '';
|
||||
const num = (key: string): number | string => (d[key] as number) ?? '';
|
||||
const bool = (key: string): boolean => (d[key] as boolean) || false;
|
||||
const arr = (key: string): string[] => (d[key] as string[]) || [];
|
||||
const obj = (key: string): Record<string, unknown> => (d[key] as Record<string, unknown>) || {};
|
||||
switch (type) {
|
||||
case 'input':
|
||||
return (
|
||||
@@ -99,7 +104,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.variableName || ''}
|
||||
value={str('variableName')}
|
||||
onChange={(e) => onChange('variableName', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg font-mono"
|
||||
/>
|
||||
@@ -109,7 +114,7 @@ function renderTypeSpecificFields(
|
||||
Default Value
|
||||
</label>
|
||||
<textarea
|
||||
value={d.defaultValue || ''}
|
||||
value={str('defaultValue')}
|
||||
onChange={(e) => {
|
||||
try {
|
||||
const parsed = JSON.parse(e.target.value);
|
||||
@@ -134,7 +139,7 @@ function renderTypeSpecificFields(
|
||||
Template
|
||||
</label>
|
||||
<textarea
|
||||
value={d.template || ''}
|
||||
value={str('template')}
|
||||
onChange={(e) => onChange('template', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg font-mono text-sm"
|
||||
rows={6}
|
||||
@@ -146,7 +151,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.model || ''}
|
||||
value={str('model')}
|
||||
onChange={(e) => onChange('model', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||
placeholder="e.g., gpt-4"
|
||||
@@ -161,7 +166,7 @@ function renderTypeSpecificFields(
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.1"
|
||||
value={d.temperature ?? ''}
|
||||
value={num('temperature')}
|
||||
onChange={(e) => onChange('temperature', parseFloat(e.target.value))}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
@@ -169,7 +174,7 @@ function renderTypeSpecificFields(
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={d.jsonMode || false}
|
||||
checked={bool('jsonMode')}
|
||||
onChange={(e) => onChange('jsonMode', e.target.checked)}
|
||||
className="w-4 h-4 text-blue-600 rounded"
|
||||
/>
|
||||
@@ -187,7 +192,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.skillId || ''}
|
||||
value={str('skillId')}
|
||||
onChange={(e) => onChange('skillId', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg font-mono"
|
||||
/>
|
||||
@@ -197,7 +202,7 @@ function renderTypeSpecificFields(
|
||||
Input Mappings (JSON)
|
||||
</label>
|
||||
<textarea
|
||||
value={JSON.stringify(d.inputMappings || {}, null, 2)}
|
||||
value={JSON.stringify(obj('inputMappings'), null, 2)}
|
||||
onChange={(e) => {
|
||||
try {
|
||||
const parsed = JSON.parse(e.target.value);
|
||||
@@ -222,7 +227,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.handId || ''}
|
||||
value={str('handId')}
|
||||
onChange={(e) => onChange('handId', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg font-mono"
|
||||
/>
|
||||
@@ -233,7 +238,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.action || ''}
|
||||
value={str('action')}
|
||||
onChange={(e) => onChange('action', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||
/>
|
||||
@@ -253,13 +258,13 @@ function renderTypeSpecificFields(
|
||||
<label key={format} className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={(d.formats || []).includes(format)}
|
||||
checked={arr('formats').includes(format)}
|
||||
onChange={(e) => {
|
||||
const formats = d.formats || [];
|
||||
const formats = arr('formats');
|
||||
if (e.target.checked) {
|
||||
onChange('formats', [...formats, format]);
|
||||
} else {
|
||||
onChange('formats', formats.filter((f: string) => f !== format));
|
||||
onChange('formats', formats.filter((f) => f !== format));
|
||||
}
|
||||
}}
|
||||
className="w-4 h-4 text-blue-600 rounded"
|
||||
@@ -275,7 +280,7 @@ function renderTypeSpecificFields(
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={d.outputDir || ''}
|
||||
value={str('outputDir')}
|
||||
onChange={(e) => onChange('outputDir', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||
placeholder="./output"
|
||||
|
||||
@@ -90,7 +90,7 @@ export interface ZclawStreamEvent {
|
||||
}
|
||||
|
||||
// === Connection State ===
|
||||
|
||||
export type ConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'connected' | 'reconnecting';
|
||||
// Re-export from kernel-types to maintain single source of truth
|
||||
export type { ConnectionState } from './kernel-types';
|
||||
|
||||
export type EventCallback = (payload: unknown) => void;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// === Connection & Status Types ===
|
||||
|
||||
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
|
||||
export type ConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'connected' | 'reconnecting';
|
||||
|
||||
export interface KernelStatus {
|
||||
initialized: boolean;
|
||||
|
||||
@@ -188,9 +188,9 @@ export interface PromptTemplateInfo {
|
||||
name: string;
|
||||
category: string;
|
||||
description: string | null;
|
||||
source: string;
|
||||
source: 'builtin' | 'custom';
|
||||
current_version: number;
|
||||
status: string;
|
||||
status: 'active' | 'deprecated' | 'archived';
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export interface PromptVersionInfo {
|
||||
/** Prompt variable definition */
|
||||
export interface PromptVariable {
|
||||
name: string;
|
||||
type: string;
|
||||
type: 'string' | 'number' | 'select' | 'boolean';
|
||||
default_value?: string;
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
@@ -432,10 +432,10 @@ export interface RoleInfo {
|
||||
|
||||
/** Create role request */
|
||||
export interface CreateRoleRequest {
|
||||
id: string;
|
||||
id?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
permissions: string[];
|
||||
permissions?: string[];
|
||||
}
|
||||
|
||||
/** Update role request */
|
||||
|
||||
@@ -354,7 +354,7 @@ if (import.meta.hot) {
|
||||
|
||||
// Dev-only: Expose stores to window for E2E testing
|
||||
if (import.meta.env.DEV && typeof window !== 'undefined') {
|
||||
const w = window as Record<string, unknown>;
|
||||
const w = window as unknown as Record<string, unknown>;
|
||||
w.__ZCLAW_STORES__ = (w.__ZCLAW_STORES__ as Record<string, unknown>) || {};
|
||||
const stores = w.__ZCLAW_STORES__ as Record<string, unknown>;
|
||||
stores.chat = useChatStore;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
| DEAD-05 | 39 个未调用 saas-client 方法 | **PARTIALLY_FIXED** | - | 2026-04-04 | 10 个零调用方法已删除 (healthCheck/listDevices/getRelayTask/getUsage×2/listPrompts/getPrompt/listPromptVersions/getPromptVersion/getPlan),29 个保留供 admin-v2 或未来接入 |
|
||||
| DOC-01 | Tauri 命令数文档 58+ vs 实际 171 | **FIXED** | 06-tauri-backend 文档已更新 175→171 |
|
||||
| DOC-02 | 智能层文档引用已删除模块 | **FALSE_POSITIVE** | 02-intelligence-layer 文档未引用已删除模块,
|
||||
| TYPE-01 | Desktop/Admin 类型不一致 (6 组) | OPEN | - | - | 统一类型定义 |
|
||||
| TYPE-01 | Desktop/Admin 类型不一致 (6 组) | **FIXED** | - | 2026-04-05 | ConnectionState 统一含 handshaking; PromptTemplate source/status + PromptVariable.type 改为 union literal; CreateRoleRequest id/permissions 改可选; PropertyPanel as any 消除; chatStore window cast 修正 |
|
||||
| G-07 | account_api_keys 被 relay 绕过 | **N/A** | Intentional architecture: provider_keys (Key Pool) 做 upstream key rotation/429/failover; account_api_keys 为 account-level token |
|
||||
|
||||
## P3: 中优先级
|
||||
@@ -135,7 +135,7 @@
|
||||
| ID | 问题 | 状态 | 验证方法 |
|
||||
|----|------|------|----------|
|
||||
| V11-P4-01 | ContentBlock 4 处定义(不同域) | **FALSE_POSITIVE** | 4 个 crate 各自服务于不同协议域(消息/MCP/Driver/Hand),按领域隔离是正确设计 |
|
||||
| V11-P4-02 | Desktop ↔ Admin 13+ 类型名称不一致 | **DOCUMENTED** | TYPE-01 已在 V12 Batch 7 部分修复(AgentInfo/KernelStatus),剩余为 admin-v2 独立类型系统 |
|
||||
| V11-P4-02 | Desktop ↔ Admin 13+ 类型名称不一致 | **DOCUMENTED** | TYPE-01 完整修复(ConnectionState union + PromptTemplate literal + CreateRoleRequest optional),剩余为 admin-v2 独立类型系统有意差异 |
|
||||
| V11-P4-03 | 文档数字不一致 (Skills 76 vs 66/75/77) | **FALSE_POSITIVE** | find skills -name SKILL.md = 75,文档统一为 75 |
|
||||
| V11-P4-04 | A2A/WASM feature-gated 未启用 | **FALSE_POSITIVE** | a2a/wasm 在 Cargo.toml 正确 feature-gated,默认不启用,有意设计 |
|
||||
| V11-P4-05 | embedding 生成已禁用 | **FALSE_POSITIVE** | generate_embedding.rs 分块功能已实现,向量生成是 Phase 2 有意延迟,注释已完善 |
|
||||
@@ -272,6 +272,7 @@
|
||||
| 2026-04-05 | SEC2-P3-01 | OPEN → DOCUMENTED | A2A feature-gated 默认不启用 |
|
||||
| 2026-04-05 | SEC2-P3-02 | OPEN → DOCUMENTED | P4 级差异,admin 独立类型系统 |
|
||||
| 2026-04-05 | V11-P2-05 | 部分关闭 → FIXED | 完整审计: 160 @connected + 16 @reserved + 1 未注册 = 177 总命令 |
|
||||
| 2026-04-05 | TYPE-01 | OPEN → FIXED | ConnectionState 统一(含 handshaking); saas-types PromptTemplate/PromptVariable union literal; CreateRoleRequest optional; PropertyPanel as any→类型安全; chatStore window cast |
|
||||
|
||||
## V12 模块化端到端审计修复 (2026-04-04)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user