---
phase: 01-credential-slot-api
plan: 01
subsystem: api-client
tags: [api-client, credential-slot, milestone-v1.0, brownfield]
requires: []
provides:
  - artifact: lib/api/credential-slot.ts
    description: 凭据槽位 API 客户端模块（类型 + adapter + GET/PUT 函数）
  - export: getCredentialSlot
    from: lib/api/credential-slot.ts
    via: lib/api/index.ts (具名 re-export)
  - export: updateCredentialSlot
    from: lib/api/credential-slot.ts
    via: lib/api/index.ts (具名 re-export)
  - type: CredentialSlot
    from: lib/api/credential-slot.ts
    via: lib/api/index.ts (具名 re-export)
  - type: CredentialSlotUpdatePayload
    from: lib/api/credential-slot.ts
    via: lib/api/index.ts (具名 re-export)
affects:
  - lib/api/index.ts
tech_stack:
  added: []
  patterns:
    - "1:1 复刻 lib/api/ai-models.ts 风格（mapBackend* + 双保险解包 + export const fn = async）"
    - "类型命名屏障：accessTokenMasked vs accessToken 在 TS 编译期切断脱敏字符串回写 bug"
    - "barrel re-export：lib/api/index.ts 末尾具名 re-export，与现有 export * 风格混用合法"
key_files:
  created:
    - lib/api/credential-slot.ts
  modified:
    - lib/api/index.ts
decisions:
  - 采用「具名 re-export」而非 `export *`：符合 RESEARCH 问题 6 + CONTEXT.md 锁定写法，可读性最高、未来重名冲突可控
  - PUT body 不带 `updated_at`：沿用 updateAiModel / updateOutfit 约定，由后端 auto_now 维护
  - `BackendCredentialSlot` 不导出：仅 adapter 入参类型，与 `mapBackend*` 模块级私有约定一致
  - 路径写 `/v1/admin/credential-slot/`（不带 `/api` 前缀）：API_BASE_URL 已含 `/api`
metrics:
  duration_seconds: 76
  completed_date: 2026-05-08
  tasks_completed: 2
  files_changed: 2
requirements:
  - CRED-FE-01
---

# Phase 1 Plan 01-01：凭据槽位 API 客户端 Summary

**One-liner**：1:1 复刻 ai-models.ts 风格落地 `lib/api/credential-slot.ts`，封装 GET/PUT + camelCase 类型 + adapter，并在 `lib/api/index.ts` 末尾具名 re-export 4 个公共符号。

## 背景

Milestone v1.0「通用凭据槽位前端集成」启动 plan，纯逻辑层（无 UI），为 Phase 2（RBAC + AI 模型页入口）/ Phase 3（编辑对话框 + Sonner 反馈）提供调用层基础。本 plan 同时建立类型层屏障：`CredentialSlot.accessTokenMasked`（脱敏）vs `CredentialSlotUpdatePayload.accessToken`（明文）字段名故意不同，让 TS 编译期切断「把脱敏掩码当真值回写 PUT」这条 bug 路径。

## Tasks Executed

### Task 1：新建 lib/api/credential-slot.ts（类型 + adapter + GET/PUT）
- **状态**: ✅ 完成
- **Commit**: `a0d0b9c`（父级 Lila-Server 仓库）
- **文件**: `lib/api/credential-slot.ts`（新增，64 行 / 2620 字节）
- **产物**:
  - `interface BackendCredentialSlot`（snake_case，模块级私有不导出）
  - `export interface CredentialSlot { appId, accessTokenMasked, updatedAt }`（公共响应类型）
  - `export interface CredentialSlotUpdatePayload { appId, accessToken }`（公共提交载荷类型）
  - `function mapBackendCredentialSlot(raw)`（模块级私有 adapter，snake → camel）
  - `export const getCredentialSlot = async (): Promise<CredentialSlot>` — 走 `apiClient.get('/v1/admin/credential-slot/')`
  - `export const updateCredentialSlot = async (payload): Promise<CredentialSlot>` — 走 `apiClient.put('/v1/admin/credential-slot/', { app_id, access_token })`
- **关键串命中**:
  - `response.data?.data || response.data` 双保险解包：**2 次**（GET / PUT 各一）
  - `apiClient.get('/v1/admin/credential-slot/')`：1 次
  - `apiClient.put('/v1/admin/credential-slot/'`：1 次
  - `/api/v1/admin/credential-slot`（重复 /api 前缀）：**0 次** ✓（路径正确）
  - PUT body 字面量不含 `updated_at` 键 ✓（仅注释行提及）
- **自动验证**: `node -e ...` 9 个 regex 检查 + 0 路径前缀检查 + 双保险解包计数 = 2 → 退出码 0，打印 `OK`

### Task 2：lib/api/index.ts 末尾追加具名 re-export
- **状态**: ✅ 完成
- **Commit**: `c072bbe`（父级 Lila-Server 仓库）
- **文件**: `lib/api/index.ts`（修改，197 → 204 行，+7 行内容；diff 显示 8 insertions 含末尾 newline 重排）
- **追加位置**: `handleApiError` 函数定义（L191-196）之后（L197 空行 + L198-204 新增块）
- **追加内容**（7 行）:
  ```typescript

  // 凭据槽位（Milestone v1.0 通用凭据槽位前端集成 — Phase 1 / CRED-FE-01）
  export {
    getCredentialSlot,
    updateCredentialSlot,
    type CredentialSlot,
    type CredentialSlotUpdatePayload,
  } from './credential-slot'
  ```
- **关键串命中**:
  - `from './credential-slot'`：1 次
  - `getCredentialSlot,`：1 次
  - `updateCredentialSlot,`：1 次
  - `type CredentialSlot,`：1 次
  - `type CredentialSlotUpdatePayload,`：1 次
  - `export * from './credential-slot'`（错误的 barrel 风格）：**0 次** ✓
- **现有内容保留**: `import * as client from "./client"`、`export * from "./card"/"./upload"/"./food"`、`usersApi`、`rolesApi`、`handleApiError` 全部不变
- **自动验证**: `node -e ...` 5 个 regex 计数检查 + barrel 风格反向检查 + card 导出保留检查 + handleApiError 保留检查 → 退出码 0，打印 `OK`

## 累计 Commit 列表

| # | Hash | Message | Files |
|---|------|---------|-------|
| 1 | `a0d0b9c` | feat(01-01): 新建 lib/api/credential-slot.ts 凭据槽位 API 客户端 | qy-lty-admin/lib/api/credential-slot.ts |
| 2 | `c072bbe` | feat(01-01): lib/api/index.ts 末尾追加凭据槽位具名 re-export | qy-lty-admin/lib/api/index.ts |

（最终 SUMMARY + STATE 提交另行追加，见底部）

## Success Criteria 自检

- [x] `lib/api/credential-slot.ts` 文件存在
- [x] 文件导出 `CredentialSlot` 类型 + `CredentialSlotUpdatePayload` 类型 + `getCredentialSlot` 函数 + `updateCredentialSlot` 函数共 4 个公共符号
- [x] `mapBackendCredentialSlot` 函数已定义（私有，未导出）
- [x] GET / PUT 路径**精确**为 `/v1/admin/credential-slot/`（不含重复 `/api`）
- [x] GET 与 PUT 函数体内各含 1 次 `response.data?.data || response.data` 双保险解包（共 2 次）
- [x] PUT body 字面量**不含** `updated_at`
- [x] `lib/api/index.ts` 末尾通过具名 re-export 暴露 4 个符号，路径 `./credential-slot`
- [x] `lib/api/index.ts` 中现有 `export * from "./card"/"./upload"/"./food"` 与 `usersApi` / `rolesApi` / `handleApiError` 完全不变
- [x] 两个文件均为合法 UTF-8（无 BOM 干扰、无残缺字符）

## Deviations from Plan

**无** — plan 执行 0 偏差。所有锁定写法（路径、解包行、PUT body 不含 updated_at、私有 adapter、具名 re-export 风格、追加位置）均严格按 PLAN action 落地。

## 与后续 plan 的衔接

- **Plan 01-02**（同一 phase）将处理：`docs/修改记录.md` 顶部追加 Phase 1 条目 + 跑双重验证（`npm run lint` + `npx tsc --noEmit`）+ 探针验证 barrel 入口
- **Phase 2** 起可用 `import { getCredentialSlot, updateCredentialSlot, type CredentialSlot, type CredentialSlotUpdatePayload } from '@/lib/api'` 直接消费本 plan 产物
- **本 plan 不写 `docs/修改记录.md`** — 集中由 Plan 01-02 落地，避免 Phase 1 内部多次写入

## Known Stubs

无 — 本 plan 是纯 API 客户端层，所有产物（类型、adapter、API 函数）都已完整实现并可直接消费。无任何占位或 TODO。

## 字节级关键产物对比

| 文件 | 状态 | 行数（前→后） | 字节 |
|------|------|--------------|------|
| `lib/api/credential-slot.ts` | 新增 | 0 → 64 | 2620 |
| `lib/api/index.ts` | 修改 | 197 → 204 | (追加 7 行) |

## Self-Check: PASSED

- [x] `lib/api/credential-slot.ts` 存在 (FOUND)
- [x] `lib/api/index.ts` 末尾包含具名 re-export 块 (FOUND)
- [x] commit `a0d0b9c` 在 git log 中 (FOUND, 父级 Lila-Server 仓库)
- [x] commit `c072bbe` 在 git log 中 (FOUND, 父级 Lila-Server 仓库)
- [x] Task 1 / Task 2 verify.automated 命令均退出码 0 + 打印 `OK`

---

*生成时间：2026-05-08*
*执行 Agent：gsd-executor (Opus 4.7)*
*父仓库 commit hash：a0d0b9c (Task 1) / c072bbe (Task 2)*
