@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67
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.en.md +66 -257
- package/README.ja.md +66 -257
- package/README.md +66 -257
- package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +435 -247
- package/dist/index.js.map +1 -1
- package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +5 -0
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +8 -3
- package/templates/base/vite.config.ts.hbs +3 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +4 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +20 -55
- package/templates/skills/hai-deploy/middleware.md +5 -11
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +9 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
{{/if}}
|
|
32
32
|
</svelte:head>
|
|
33
33
|
|
|
34
|
-
<section class='
|
|
34
|
+
<section class='mx-auto w-full max-w-[120rem] px-4 py-16 sm:px-6 lg:px-8'>
|
|
35
35
|
{{#if hasI18n}}
|
|
36
36
|
<PageHeader title={m.page_services_title()} description={m.page_services_desc()} />
|
|
37
37
|
{{else}}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
**/.git
|
|
2
|
+
**/node_modules
|
|
3
|
+
**/.pnpm-store
|
|
4
|
+
**/.cache
|
|
5
|
+
**/.svelte-kit
|
|
6
|
+
**/.turbo
|
|
7
|
+
**/build
|
|
8
|
+
**/dist
|
|
9
|
+
**/coverage
|
|
10
|
+
**/playwright-report
|
|
11
|
+
**/test-results
|
|
12
|
+
**/src-tauri/target
|
|
13
|
+
**/hash-core/target
|
|
14
|
+
tmp
|
|
2
15
|
**/*.tar
|
|
3
16
|
.github
|
|
4
17
|
.agents
|
|
5
18
|
.claude
|
|
6
19
|
.codex
|
|
7
20
|
.opencode
|
|
8
|
-
.svelte-kit
|
|
9
|
-
.turbo
|
|
10
21
|
android
|
|
11
|
-
build
|
|
12
|
-
dist
|
|
13
22
|
ios
|
|
14
|
-
node_modules
|
|
15
|
-
playwright-report
|
|
16
|
-
test-results
|
|
17
23
|
**/data
|
|
18
24
|
*.log
|
|
19
25
|
# 仅保留公共基础 .env;分环境配置、私有覆盖及已有交付包不进入构建上下文。
|
|
@@ -23,6 +23,16 @@ test('home page renders from browser', async ({ page }) => {
|
|
|
23
23
|
await expect(page.locator('main, section').first()).toBeVisible()
|
|
24
24
|
await expect(page.locator('header').first()).toBeVisible()
|
|
25
25
|
await expect(page.locator('footer').first()).toBeAttached()
|
|
26
|
+
// 大屏使用可用空间,窄屏由组件内部滚动,不能撑宽整个页面。
|
|
27
|
+
for (const width of [390, 1440, 1920, 2560, 3840]) {
|
|
28
|
+
await page.setViewportSize({ width, height: 960 })
|
|
29
|
+
const availableWidth = await page.evaluate(() => document.documentElement.clientWidth)
|
|
30
|
+
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
31
|
+
const shell = page.locator('.hai-mobile-shell')
|
|
32
|
+
if (await shell.count())
|
|
33
|
+
expect((await shell.boundingBox())!.width).toBeGreaterThan(availableWidth - 2)
|
|
34
|
+
}
|
|
35
|
+
await page.setViewportSize({ width: 390, height: 844 })
|
|
26
36
|
{{#if_eq appType 'h5'}}
|
|
27
37
|
// 在手机宽度验证导航真正切换路由,避免样式失效但首页渲染测试仍通过。
|
|
28
38
|
const nav = page.locator('nav')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "images": [{ "name": "app", "
|
|
1
|
+
{ "images": [{ "name": "app", "dockerfile": "images/app.Dockerfile", "containerPort": {{#if isSvelteOnlyApp}}8080{{else}}3000{{/if}} }] }
|
|
@@ -1,48 +1,51 @@
|
|
|
1
|
-
# syntax=docker/dockerfile:1.7
|
|
2
1
|
{{#if isSvelteOnlyApp}}
|
|
3
|
-
FROM node:24-bookworm-slim AS build
|
|
4
|
-
ARG SQLITE_PREBUILD_MIRROR
|
|
5
|
-
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
2
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
6
3
|
|
|
7
4
|
ENV PNPM_HOME=/pnpm
|
|
8
5
|
ENV PATH=$PNPM_HOME:$PATH
|
|
9
6
|
ENV CI=true
|
|
7
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
10
8
|
WORKDIR /workspace
|
|
11
9
|
|
|
12
|
-
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate
|
|
10
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
11
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
12
|
+
COPY images/dependencies/ ./
|
|
13
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
13
14
|
COPY . .
|
|
14
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch
|
|
15
15
|
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
16
16
|
RUN pnpm compile
|
|
17
17
|
|
|
18
|
-
FROM nginxinc/nginx-unprivileged:1.29-alpine AS runtime
|
|
18
|
+
FROM nginxinc/nginx-unprivileged:1.29.8-alpine AS runtime
|
|
19
19
|
|
|
20
|
-
COPY --chmod=755
|
|
21
|
-
COPY
|
|
20
|
+
COPY --chmod=755 images/web/19-runtime-config.sh /docker-entrypoint.d/19-hai-runtime-config.sh
|
|
21
|
+
COPY images/web/nginx.conf /etc/nginx/conf.d/default.conf
|
|
22
22
|
COPY --from=build /workspace/dist /usr/share/nginx/html
|
|
23
23
|
|
|
24
24
|
EXPOSE 8080
|
|
25
25
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
26
26
|
CMD wget -q -O /dev/null http://127.0.0.1:8080/health || exit 1
|
|
27
27
|
{{else}}
|
|
28
|
-
FROM node:24-bookworm-slim AS build
|
|
29
|
-
ARG SQLITE_PREBUILD_MIRROR
|
|
28
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
29
|
+
ARG SQLITE_PREBUILD_MIRROR=
|
|
30
30
|
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
31
31
|
|
|
32
32
|
ENV PNPM_HOME=/pnpm
|
|
33
33
|
ENV PATH=$PNPM_HOME:$PATH
|
|
34
34
|
ENV CI=true
|
|
35
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
35
36
|
WORKDIR /workspace
|
|
36
37
|
|
|
37
|
-
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate
|
|
38
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
39
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
40
|
+
COPY images/dependencies/ ./
|
|
41
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
38
42
|
COPY . .
|
|
39
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch
|
|
40
43
|
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
41
44
|
RUN pnpm compile
|
|
42
|
-
RUN if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
43
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm pm deploy --filter {{projectName}} --prod /prod/app
|
|
45
|
+
RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
46
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{projectName}} --prod /prod/app
|
|
44
47
|
|
|
45
|
-
FROM node:24-bookworm-slim AS runtime-base
|
|
48
|
+
FROM node:24.22.1-bookworm-slim AS runtime-base
|
|
46
49
|
|
|
47
50
|
ENV NODE_ENV=production
|
|
48
51
|
ENV HOST=0.0.0.0
|
|
@@ -58,5 +61,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
|
58
61
|
CMD node -e "fetch('http://127.0.0.1:'+process.env.PORT+'/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
59
62
|
CMD ["node", "build"]
|
|
60
63
|
|
|
61
|
-
FROM runtime-base AS runtime
|
|
62
64
|
{{/if}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"engines": { "node": "^22.22.2 || ^24.15.0 || >=26.0.0" },
|
|
3
3
|
"name": "{{projectName}}",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.67",
|
|
5
5
|
"private": true,
|
|
6
6
|
"packageManager": "pnpm@12.11.2",
|
|
7
7
|
"description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"dev:prod": "vite dev --mode prod",
|
|
26
26
|
"compile": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}vite build",
|
|
27
27
|
"build": "hai build",
|
|
28
|
+
{{#if features.deploy}}
|
|
28
29
|
"deploy": "hai deploy",
|
|
30
|
+
{{/if}}
|
|
29
31
|
{{#unless isSvelteOnlyApp}}
|
|
30
32
|
"start": "node --env-file=.env --env-file=.env.prod --env-file-if-exists=.env.local build/index.js",
|
|
31
33
|
"start:test": "node --env-file=.env --env-file=.env.test --env-file-if-exists=.env.local build/index.js",
|
|
@@ -73,16 +75,19 @@
|
|
|
73
75
|
"@h-ai/payment": "{{haiPackageSpecifier}}"{{/if}}{{#if features.vecdb}},
|
|
74
76
|
"@h-ai/vecdb": "{{haiPackageSpecifier}}"{{/if}}{{#if features.datapipe}},
|
|
75
77
|
"@h-ai/datapipe": "{{haiPackageSpecifier}}"{{/if}}{{#if features.scheduler}},
|
|
76
|
-
"@h-ai/scheduler": "{{haiPackageSpecifier}}"{{/if}}{{#if features.
|
|
77
|
-
"@h-ai/deploy": "{{haiPackageSpecifier}}"{{/if}}{{#if features.api-client}},
|
|
78
|
+
"@h-ai/scheduler": "{{haiPackageSpecifier}}"{{/if}}{{#if features.api-client}},
|
|
78
79
|
"@h-ai/api-client": "{{haiPackageSpecifier}}"{{/if}}{{#if features.capacitor}},
|
|
79
80
|
"@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/if}}{{#if isCapacitorApp}}{{#unless features.api-client}},
|
|
80
81
|
"@h-ai/api-client": "{{haiPackageSpecifier}}"{{/unless}}{{#unless features.capacitor}},
|
|
81
82
|
"@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/unless}}{{/if}},
|
|
83
|
+
"sm-crypto": "0.5.7",
|
|
82
84
|
"zod": "{{versions.zod}}"
|
|
83
85
|
{{/if}}
|
|
84
86
|
},
|
|
85
87
|
"devDependencies": {
|
|
88
|
+
{{#if features.deploy}}
|
|
89
|
+
"@h-ai/deploy": "{{haiPackageSpecifier}}",
|
|
90
|
+
{{/if}}
|
|
86
91
|
"@h-ai/cli": "{{haiPackageSpecifier}}",
|
|
87
92
|
{{#if isSvelteOnlyApp}}
|
|
88
93
|
"@h-ai/core": "{{haiPackageSpecifier}}",
|
|
@@ -80,7 +80,9 @@ export default defineConfig(({ mode, command }) => {
|
|
|
80
80
|
{{/if}}
|
|
81
81
|
{{#unless isSvelteOnlyApp}}
|
|
82
82
|
ssr: {
|
|
83
|
-
noExternal: [
|
|
83
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
84
|
+
// sm-crypto 同时列入生产依赖,避免 adapter-node 再次打包其动态 require。
|
|
85
|
+
external: ['@h-ai/core', 'sm-crypto'],
|
|
84
86
|
},
|
|
85
87
|
{{/unless}}
|
|
86
88
|
server: {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
1. 先搜索现有实现、引用点、测试和 README。
|
|
42
42
|
2. 小步修改,优先保持目录结构、公开 API 和脚本稳定。
|
|
43
43
|
3. 改动公共类型、配置、模板或共享逻辑时,同步更新所有调用方与文档。
|
|
44
|
-
4.
|
|
44
|
+
4. 一轮修改完成后按影响范围收集全部可执行门禁结果,汇总问题再集中修复、复测;普通失败不立即从头重跑。
|
|
45
45
|
|
|
46
46
|
## 代码注释与示例
|
|
47
47
|
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
## 质量门禁
|
|
54
54
|
|
|
55
|
-
先核对 package.json
|
|
55
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
pnpm typecheck
|
|
@@ -64,7 +64,7 @@ pnpm test:e2e
|
|
|
64
64
|
|
|
65
65
|
## 完成条件
|
|
66
66
|
|
|
67
|
-
- typecheck / lint / build / test / test:e2e
|
|
67
|
+
- typecheck / lint / build / test / test:e2e 状态明确;整轮收集后集中修复,最终必需门禁对最终代码有效通过,未执行或阻塞明确原因,不能宣称完成。
|
|
68
68
|
- README、i18n、测试、代码注释与实现保持一致。
|
|
69
69
|
- 最终回复说明执行过的门禁、已同步的文档 / 依赖方,以及未完成项。
|
|
70
70
|
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
## 质量门禁
|
|
59
59
|
|
|
60
|
-
先核对 package.json
|
|
60
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
63
|
pnpm typecheck
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -71,7 +71,7 @@ pnpm cap:build:android:release
|
|
|
71
71
|
|
|
72
72
|
## 质量门禁
|
|
73
73
|
|
|
74
|
-
先核对 package.json
|
|
74
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
pnpm typecheck
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-api-client
|
|
3
|
-
description: "
|
|
3
|
+
description: "接入或排查 @h-ai/api-client typed API、登录 Token、自动刷新与加密传输;适用于 Web、SSR 和原生客户端。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-api-client
|
|
@@ -15,167 +15,20 @@ description: "使用 @h-ai/api-client 调用 typed API,管理 Token、刷新
|
|
|
15
15
|
| 输出 | typed client;领域响应为 HaiResult |
|
|
16
16
|
| 限制 | 先 init,结束 close;浏览器 Cookie 与服务端配置匹配,原生端使用安全 TokenStorage。不要在多用户 SSR 中共享带用户 token 的单例。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. 读取服务端 contract、apiPrefix、CORS、Cookie 与 transport 配置,选择默认客户端或 `apiClient.create(contract)`。
|
|
21
|
+
2. 浏览器使用 Cookie/内存;SSR 按请求创建客户端,原生使用安全 TokenStorage,不跨用户复用单例。
|
|
22
|
+
3. 检查 init 与每次业务调用的 `success`。登录成功后设置 tokens;优先使用 `createAuthActions` 连接认证 UI。
|
|
23
|
+
4. 同步服务端与客户端的刷新、加密和错误处理;切换配置前 close,任务结束释放客户端。
|
|
24
|
+
5. 验证登录、刷新重试、失败、退出与会话恢复。
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
## 按需参考
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
auth: {},
|
|
32
|
-
})
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
启用传输加密时,只传顶层 `crypto` 句柄;api-client 内部自动调用 `crypto.transport.createClient()`:
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
import { apiClient } from '@h-ai/api-client'
|
|
39
|
-
import { crypto } from '@h-ai/crypto'
|
|
40
|
-
|
|
41
|
-
await crypto.init()
|
|
42
|
-
|
|
43
|
-
await apiClient.init({
|
|
44
|
-
baseUrl: 'https://api.example.com/api/v1',
|
|
45
|
-
auth: {},
|
|
46
|
-
transport: { crypto }, // 默认协商路径:/api/v1/_hai/key-exchange
|
|
47
|
-
})
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
App/Capacitor 场景传入自定义 TokenStorage:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
import { createCapacitorTokenStorage } from '@h-ai/capacitor'
|
|
54
|
-
|
|
55
|
-
await apiClient.init({
|
|
56
|
-
baseUrl: 'https://api.example.com/api/v1',
|
|
57
|
-
auth: { storage: createCapacitorTokenStorage() },
|
|
58
|
-
})
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
> `createCapacitorTokenStorage()` 仅在 Android/iOS 原生环境 + `@aparajita/capacitor-secure-storage` 已安装时持久化 token;纯 Web 不会回退到 `localStorage`。
|
|
62
|
-
|
|
63
|
-
SSR / Node.js 测试场景请显式传入内存存储:
|
|
64
|
-
|
|
65
|
-
```ts
|
|
66
|
-
import { apiClient } from '@h-ai/api-client'
|
|
67
|
-
|
|
68
|
-
await apiClient.init({
|
|
69
|
-
baseUrl: 'https://api.example.com/api/v1',
|
|
70
|
-
auth: { storage: apiClient.tokenStorage.memory() },
|
|
71
|
-
})
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 2. 调用 typed API
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
const login = await apiClient.iam.auth.login({ identifier: 'alice', password: 'secret' })
|
|
78
|
-
if (login.success) {
|
|
79
|
-
// 默认 httpOnly cookie 模式下,refresh token 由服务端 Set-Cookie 管理。
|
|
80
|
-
await apiClient.auth.setTokens(login.data.tokens)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const me = await apiClient.iam.auth.currentUser()
|
|
84
|
-
if (me.success) {
|
|
85
|
-
// currentUser 返回服务端最新角色/权限;不要把权限快照持久化到 localStorage。
|
|
86
|
-
const { roles, permissions, ...user } = me.data
|
|
87
|
-
}
|
|
88
|
-
const upload = await apiClient.storage.presignedUrls.createUpload({ key: 'avatar.png' })
|
|
89
|
-
const answer = await apiClient.ai.chats.sendMessage({ message: 'hello' })
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### 3. 自定义 contract
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import { apiClient } from '@h-ai/api-client'
|
|
96
|
-
import { apiContract } from '@h-ai/api-contract'
|
|
97
|
-
|
|
98
|
-
const contract = apiContract.create({ iam: apiContract.iam })
|
|
99
|
-
const client = apiClient.create(contract)
|
|
100
|
-
await client.init({ baseUrl: 'https://api.example.com/api/v1' })
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
`client.config` / `apiClient.config` 返回的是脱敏后的运行配置快照;`baseUrl` 的内嵌凭证与 `headers.authorization` 等敏感值会自动隐藏。
|
|
104
|
-
|
|
105
|
-
### 4. 关闭
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
await apiClient.close()
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
> 已初始化后再次 `init()` 会返回 `CONFIG_ERROR`,不会覆盖现有 client;切换 `baseUrl` / `fetch` / `transport` 前必须先 `close()`,避免旧 transport 会话泄漏。
|
|
112
|
-
|
|
113
|
-
## Token Storage 适配器
|
|
114
|
-
|
|
115
|
-
| 工厂调用 | 存储位置 | 适用场景 |
|
|
116
|
-
| --- | --- | --- |
|
|
117
|
-
| `apiClient.tokenStorage.httpOnlyCookie()` | httpOnly cookie(服务端管理) | **默认**;浏览器端推荐,refresh token 不暴露给 JS |
|
|
118
|
-
| `apiClient.tokenStorage.memory()` | 内存 | SSR / Node.js 单元测试(需显式传入) |
|
|
119
|
-
| `apiClient.tokenStorage.localStorage()` | localStorage | 非敏感场景(有 XSS 风险,生产不推荐) |
|
|
120
|
-
|
|
121
|
-
### httpOnly Cookie 模式
|
|
122
|
-
|
|
123
|
-
Access token 存在内存中,refresh token 由服务端通过 `HttpOnly` cookie 管理,浏览器 JS 无法读取,可有效防止 XSS 窃取 token。
|
|
124
|
-
|
|
125
|
-
**前提**:服务端需配置 `serv.createApp({ iam, refreshCookie: {} })`,参见 `hai-serv` skill。
|
|
126
|
-
|
|
127
|
-
启用 `transport` 时,登录、业务请求以及 401 后的自动刷新都会复用同一个加密 fetch 链路;不要在业务代码中手写明文 `/auth/refresh` 请求。
|
|
128
|
-
|
|
129
|
-
自动刷新识别真实 HTTP 401(`Response.status`),以及 HTTP 200 的有效失败 HaiResult(`success=false`、字符串 `error.code/message`、`error.httpStatus=401`),成功后只重试一次原请求。内置登录、注册与配置的 `auth.refreshPath` 排除自动刷新,403/500 等其它失败也不刷新。serv 普通 procedure 的 HTTP 状态仍为 200;调用方须检查最终 `success/error.code` 并处理刷新失败后的登录失效。
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
import { apiClient } from '@h-ai/api-client'
|
|
133
|
-
|
|
134
|
-
// apiClient.tokenStorage.httpOnlyCookie() 是默认存储,无需显式指定
|
|
135
|
-
await apiClient.init({
|
|
136
|
-
baseUrl: 'https://api.example.com/api/v1',
|
|
137
|
-
auth: {},
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
// 登录后设置 access token(refresh token 由服务端写入 cookie,前端无需操作)
|
|
141
|
-
const login = await apiClient.iam.auth.login({ identifier: 'alice', password: 'secret' })
|
|
142
|
-
if (login.success) {
|
|
143
|
-
await apiClient.auth.setTokens(login.data.tokens)
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## 核心 API
|
|
148
|
-
|
|
149
|
-
| API | 用途 |
|
|
150
|
-
| --- | --- |
|
|
151
|
-
| `apiClient.init(config)` | 初始化默认 API Service client;重复初始化返回 `CONFIG_ERROR` |
|
|
152
|
-
| `apiClient.close()` | 清理 client 状态 |
|
|
153
|
-
| `apiClient.auth.setTokens(tokens)` | 写入 access token;非 httpOnly 存储会同时写入 refresh token |
|
|
154
|
-
| `apiClient.auth.clear()` | 清理 Token |
|
|
155
|
-
| `apiClient.auth.onTokenRefreshed(cb)` | 监听刷新结果 |
|
|
156
|
-
| `apiClient.create(contract)` | 创建自定义 typed client |
|
|
157
|
-
| `apiClient.tokenStorage.{memory,localStorage,httpOnlyCookie}()` | TokenStorage 工厂 |
|
|
158
|
-
|
|
159
|
-
### Transport 配置
|
|
160
|
-
|
|
161
|
-
| 字段 | 说明 |
|
|
162
|
-
| --- | --- |
|
|
163
|
-
| `transport.crypto` | 已初始化的 `@h-ai/crypto` 实例 |
|
|
164
|
-
| `transport.keyExchangePath` | 相对于 `baseUrl` 的协商子路径,默认 `/_hai/key-exchange` |
|
|
165
|
-
|
|
166
|
-
服务端必须对应启用 `serv.createApp({ transport: { crypto } })`。
|
|
167
|
-
自动刷新会沿用 `transport.encryptedFetch`,并在 httpOnly cookie 模式下发送空请求体 + `credentials: 'include'`,由服务端从 cookie 读取 refresh token。
|
|
168
|
-
|
|
169
|
-
## 常见模式
|
|
170
|
-
|
|
171
|
-
- `baseUrl` 通常包含服务端 `apiPrefix`,例如 `/api/v1`。
|
|
172
|
-
- 业务错误不 throw,统一判断 `HaiResult.success`。
|
|
173
|
-
- 客户端不导入服务端 `procedures` 或 `@h-ai/serv`。
|
|
174
|
-
- 客户端不导入 `@h-ai/crypto` 的内部 transport 工厂,只通过 `transport: { crypto }` 装配。
|
|
175
|
-
- 前端只依赖 `@h-ai/api-client` 和 contract 类型,不依赖业务模块实现。
|
|
176
|
-
- httpOnly cookie 模式需服务端与客户端同步配置,不支持跨域刷新(`SameSite=Strict`)。
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
## 配置驱动认证页面
|
|
180
|
-
|
|
181
|
-
`apiClient.createAuthActions(client)` 为含标准 iam contract 的客户端提供 publicConfig、login、register、sendOtp、requestPasswordReset、confirmPasswordReset。login 输入使用 identifier/password/method/code,method 为 password、ldap、email、sms。成功认证自动调用既有安全 TokenStorage;应用只需把动作传给 AuthPanel,并在 onauthenticated 中刷新自己的用户状态或导航。
|
|
30
|
+
- [使用步骤](reference.md#使用步骤)
|
|
31
|
+
- [Token Storage 适配器](reference.md#token-storage-适配器)
|
|
32
|
+
- [核心 API](reference.md#核心-api)
|
|
33
|
+
- [常见模式](reference.md#常见模式)
|
|
34
|
+
- [配置驱动认证页面](reference.md#配置驱动认证页面)
|