openxiangda 1.0.83 → 1.0.85

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/lib/http.js CHANGED
@@ -1,11 +1,16 @@
1
1
  const { maskText } = require('./utils');
2
2
 
3
+ const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
4
+
3
5
  async function requestJson(baseUrl, apiPath, options = {}) {
4
6
  if (typeof fetch !== 'function') {
5
7
  throw new Error('当前 Node.js 版本不支持 fetch,请使用 Node.js 18 或更高版本');
6
8
  }
7
9
 
8
10
  const url = `${baseUrl.replace(/\/+$/, '')}${apiPath}`;
11
+ const timeoutMs = normalizeTimeoutMs(options.timeoutMs, DEFAULT_REQUEST_TIMEOUT_MS);
12
+ const controller = new AbortController();
13
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
9
14
  const headers = {
10
15
  accept: 'application/json',
11
16
  ...(options.headers || {}),
@@ -17,12 +22,23 @@ async function requestJson(baseUrl, apiPath, options = {}) {
17
22
  headers.authorization = `Bearer ${options.accessToken}`;
18
23
  }
19
24
 
20
- const response = await fetch(url, {
21
- method: options.method || 'GET',
22
- headers,
23
- body:
24
- options.body === undefined ? undefined : JSON.stringify(options.body),
25
- });
25
+ let response;
26
+ try {
27
+ response = await fetch(url, {
28
+ method: options.method || 'GET',
29
+ headers,
30
+ body:
31
+ options.body === undefined ? undefined : JSON.stringify(options.body),
32
+ signal: controller.signal,
33
+ });
34
+ } catch (error) {
35
+ if (isAbortError(error)) {
36
+ throw new Error(maskText(`HTTP request timed out after ${timeoutMs}ms: ${apiPath}`));
37
+ }
38
+ throw error;
39
+ } finally {
40
+ clearTimeout(timer);
41
+ }
26
42
 
27
43
  const text = await response.text();
28
44
  let payload = null;
@@ -42,6 +58,19 @@ async function requestJson(baseUrl, apiPath, options = {}) {
42
58
  return payload;
43
59
  }
44
60
 
61
+ function normalizeTimeoutMs(value, fallback) {
62
+ const parsed = Number(value);
63
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
64
+ return fallback;
65
+ }
66
+
67
+ function isAbortError(error) {
68
+ return (
69
+ error?.name === 'AbortError' ||
70
+ String(error?.message || '').toLowerCase().includes('aborted')
71
+ );
72
+ }
73
+
45
74
  module.exports = {
46
75
  requestJson,
47
76
  };
@@ -28,6 +28,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
28
28
  | 审批流程 / workflow / 流程节点 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow validate / create / publish` |
29
29
  | 自动化 / 定时任务 / 提交触发 / cron | `openxiangda-workflow-automation` | `openxiangda automation validate / create / publish / enable` |
30
30
  | App Function / function_call / 可复用后端逻辑 | `openxiangda-workflow-automation` | declare `src/resources/functions/<code>.json` + `src/functions/<code>/index.ts` |
31
+ | 应用登录 / Auth SDK / 手机号验证码 / CAS / 钉钉免登 | `openxiangda-architecture-design` + `openxiangda-page` | design security gate first, then declare `src/resources/auth/<code>.json` and use `createAuthClient` / `LoginPage` |
31
32
  | 角色 / 权限组 / 字段权限 / 数据范围 / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
32
33
  | 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
33
34
  | 登录 / 切换平台 / profile / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
@@ -37,10 +38,12 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
37
38
  ### Hard rules — always
38
39
 
39
40
  - ✅ Publish classic workspaces through `openxiangda workspace publish --profile <name>`; publish React SPA workspaces through `openxiangda resource publish` + `openxiangda runtime deploy`.
41
+ - ✅ `runtime deploy` defaults to staged multipart `dist/` uploads plus a final manifest. Use `--upload-mode legacy-json` only for old platform servers.
40
42
  - ✅ User token lives in `~/.openxiangda/profiles.json`; project state in `.openxiangda/state.json` (IDs only).
41
43
  - ✅ Each profile (dev / prod / ...) has its own `appType` and resource IDs; never copy a `formUuid` / `pageId` / `workflowId` / `automationId` across profiles.
42
44
  - ✅ Run `openxiangda update check --json` at the start of substantial work; if `updateAvailable`, run `openxiangda update install` and `openxiangda skill install --force`.
43
45
  - ✅ For non-trivial app requirements, run the architecture design gate first: forms, fields, pages, permissions, data views, status/workflow, automation, notifications, and development tasks must be decided before coding.
46
+ - ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
44
47
  - ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
45
48
  - ✅ List pages, linked options, remote selectors, and report drill-downs must use paginated server-side queries with explicit searchable fields. Do not fetch a large page and filter in local state.
46
49
  - ✅ Treat workflow forms as an exception. Ordinary ticket/order/task/asset lifecycles use status fields, state machines, action logs, permissions, and automations; workflows are for real approval tasks.
@@ -141,6 +144,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
141
144
  - For repeated read-only multi-form queries or predefined dashboard metrics, create a data view manifest in `src/resources/data-views/`. Use `storageMode: "materialized"` for refreshed lists/reports that tolerate delay; use `storageMode: "live"` for bounded real-time joins where source changes must appear immediately. Use row views plus `sdk.dataView.query` for joined lists/lookups; use `viewType: "aggregate"` plus `sdk.dataView.stats` for count/sum/avg/min/max statistics. Add `indexes` only for materialized views. Do not use data views for single-form CRUD, simple linkedForm selects, writes, write-back, unbounded realtime joins, or ad-hoc BI. Read `references/data-views.md` before designing one.
142
145
  - For external APIs, create a connector manifest in `src/resources/connectors/` and call it from pages through `sdk.connector`; never put third-party API keys in page source.
143
146
  - For reusable backend logic shared by pages, automations, and workflows, create an App Function in `src/resources/functions/<functionCode>.json` plus `src/functions/<functionCode>/index.ts`. Call it from pages with `sdk.function.invoke`, from automation/workflow graphs with `function_call`, or from the runtime endpoint when the caller has app automation management permission. Current App Function MVP exposes controlled helpers such as `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; it does not expose raw SQL or Redis.
147
+ - For application login, declare auth resources in `src/resources/auth/<code>.json` and publish them with `openxiangda resource publish`. App Function auth providers may validate external credentials and return only an identity assertion; they must never issue tokens, set cookies, or write platform user/binding tables directly. The platform auth service decides create/bind/reject and issues tokens.
144
148
  - Before scaffolding a real app, complex page, data management page, portal shell, status lifecycle, role governance, or automation, read `references/best-practices.md` and pick the architecture first. Prefer copying the relevant pattern from `examples/best-practices/` into `src/` instead of generating a single large page file.
145
149
  - Before publishing to another platform, verify the workspace is bound for that profile. Resource IDs from one profile must not be reused for another profile.
146
150
  - Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
@@ -180,7 +184,7 @@ Core CLI / state:
180
184
  - `references/architecture-design.md` — OpenXiangda architecture design gate, anti-pattern rejection, question gates, final design template, and black-box demo scenario.
181
185
  - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile isolation rules.
182
186
  - `references/connector-resources.md` — `src/resources` manifests, connector schema, App Function boundary, and platform runtime calls.
183
- - `references/resource-manifest-cheatsheet.md` — 复制即用的 connector / data-view / notification / App Function / workflow / automation / JS_CODE / role / permission group / settings / menu manifest 骨架与运行时调用示例。在创建任何 `src/resources/` 资源前先看这份。
187
+ - `references/resource-manifest-cheatsheet.md` — 复制即用的 auth / connector / data-view / notification / App Function / workflow / automation / JS_CODE / role / permission group / settings / menu manifest 骨架与运行时调用示例。在创建任何 `src/resources/` 资源前先看这份。
184
188
  - `references/data-views.md` — `src/resources/data-views` materialized/live data view resources, DSL, permissions, refresh, CLI commands, and runtime SDK usage.
185
189
  - `references/notifications.md` — `src/resources/notifications`, notification templates/type bindings, and `sdk.notification` / `ctx.notification`.
186
190
  - `references/best-practices.md` — initialized examples for modular pages, state lifecycles, role governance, permission isolation, high-performance queries, portal shells, workflow boundaries, and automation patterns.
@@ -24,6 +24,7 @@ This design flow is for applications built on OpenXiangda. The output is not a g
24
24
  - status machines or workflows
25
25
  - automations, JS_CODE V2 nodes, and App Functions
26
26
  - notifications and connectors
27
+ - application login/auth methods, registration policy, identity matching, and provider boundaries
27
28
  - publish and acceptance steps
28
29
 
29
30
  The design is complete only when another agent can implement it without deciding major architecture tradeoffs.
@@ -72,6 +73,8 @@ Do not insult the person. Criticize the proposal and explain the technical conse
72
73
  | Materialized view for hard realtime data | Users see stale data | `storageMode: "live"` only for bounded realtime joins, or direct source queries/App Function |
73
74
  | Live view for unbounded heavy joins | Slow runtime queries | Materialized view with scheduled refresh and indexes |
74
75
  | JS_CODE for reusable business service | Logic gets duplicated in graph nodes | App Function for shared backend logic; JS_CODE only for node-local trigger logic |
76
+ | Auth provider issues tokens or writes users directly | Breaks platform account, binding, permission, and audit boundaries | Provider returns identity assertion only; platform creates/binds/rejects and issues token |
77
+ | Phone-code login auto-registers by default | Allows uncontrolled account creation from weak identity proof | Default registration is reject; enable auto-create/whitelist only after explicit confirmation |
75
78
  | Raw native form controls | Inconsistent with platform runtime and validation | OpenXiangda platform components first, Ant Design wrappers second |
76
79
  | Multiple form permission groups without stable local codes | CLI state and platform `resourceCode` cannot reliably distinguish groups on the same form | Give every group a unique `code`, for example `ticket_reporter_view` and `ticket_repairer_view` |
77
80
  | Assuming resource publish means workflow is active | Workflow resources may exist as drafts until explicitly published | Verify `workflow list` shows `isPublished: true`; run `workflow publish <workflowCode>` when needed |
@@ -153,6 +156,20 @@ Default: reusable calculations and validations become App Functions; one-off bac
153
156
  - Are templates reusable resources?
154
157
  - Are external credentials needed? If yes, use connectors/resources, never page source secrets.
155
158
 
159
+ ### Login And Auth
160
+
161
+ Ask these before generating any login/auth design:
162
+
163
+ - Which methods are enabled: password, DingTalk in-app free login, CAS/SSO, phone code, guest?
164
+ - What is the phone registration policy: reject, auto-create, bind existing only, whitelist, or manual approval?
165
+ - What are the identity match keys and priorities: `phone`, `email`, `externalId`, `unionId`, `jobNumber`, `username`?
166
+ - Which App Function provider validates each external credential, and what identity assertion fields must it return?
167
+ - Which default app roles/page groups/data scopes should new users receive, if registration is enabled?
168
+ - What are the security parameters: code TTL, send frequency, failed attempts, IP/device limits, audit fields, and failure message disclosure?
169
+ - Is CAS/DingTalk configured by the platform tenant, or delegated to an app-level provider function?
170
+
171
+ Default: registration is rejected; provider functions return identity assertions only; platform auth service owns account creation, binding, permission assignment, cookies, and tokens.
172
+
156
173
  ### UI Design
157
174
 
158
175
  Ask whether to use Product Design or another design skill for:
@@ -213,6 +230,14 @@ For simple CRUD/admin lists, design can proceed with the appropriate OpenXiangda
213
230
  - After publishing workflow resources, verify `workflow list` and record whether `isPublished` is true.
214
231
  - In React SPA and classic workspaces, JS_CODE/App Function TypeScript lives under `src/js-code-nodes`, `src/automations`, or `src/functions`; build with `pnpm build-js-code`.
215
232
 
233
+ ### Auth
234
+
235
+ - Auth resources use stable local codes under `src/resources/auth/`.
236
+ - Use `/view/:appType/login` or `LoginPage` from `openxiangda/runtime/react` for default React login.
237
+ - Use `createAuthClient({ appType, servicePrefix })` for custom React login pages.
238
+ - Phone-code providers are App Functions. They validate send/verify events and return identity assertions; they do not issue tokens or mutate platform users.
239
+ - New-user registration must be explicit, with default roles and identity conflict behavior documented.
240
+
216
241
  ## Final Document Template
217
242
 
218
243
  ```markdown
@@ -262,17 +287,26 @@ For simple CRUD/admin lists, design can proceed with the appropriate OpenXiangda
262
287
  - JS_CODE V2 nodes:
263
288
  - App Functions:
264
289
 
265
- ## 8. Notifications And Connectors
290
+ ## 8. Login And Auth
291
+
292
+ - Enabled methods:
293
+ - Registration policy:
294
+ - Identity matching keys:
295
+ - Provider functions:
296
+ - Default roles/permissions:
297
+ - Security parameters:
298
+
299
+ ## 9. Notifications And Connectors
266
300
 
267
301
  | Scenario | Trigger | Channel | Recipients | Template/resource | Notes |
268
302
  |---|---|---|---|---|---|
269
303
 
270
- ## 9. Development Task Table
304
+ ## 10. Development Task Table
271
305
 
272
306
  | Phase | Task | Resources/files | Validation | Publish step |
273
307
  |---|---|---|---|---|
274
308
 
275
- ## 10. Acceptance Criteria
309
+ ## 11. Acceptance Criteria
276
310
 
277
311
  - Functional checks:
278
312
  - Permission checks:
@@ -281,7 +315,7 @@ For simple CRUD/admin lists, design can proceed with the appropriate OpenXiangda
281
315
  - Notification/automation checks:
282
316
  - Workflow checks: real approval flows show `isPublished: true`.
283
317
 
284
- ## 11. Open Questions And Confirmed Assumptions
318
+ ## 12. Open Questions And Confirmed Assumptions
285
319
 
286
320
  - Confirmed:
287
321
  - Still open:
@@ -12,6 +12,7 @@ Common folders:
12
12
  - `automations`
13
13
  - `data-views`
14
14
  - `functions`
15
+ - `auth`
15
16
  - `permissions/page-groups`
16
17
  - `permissions/form-groups`
17
18
  - `settings/forms`
@@ -33,6 +34,8 @@ Data view manifests live under `src/resources/data-views/` and define read-only
33
34
 
34
35
  App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Use JS_CODE V2 only for node-local workflow/automation scripts.
35
36
 
37
+ Auth manifests live under `src/resources/auth/`. Use them to enable app-level login methods and bind phone-code/CAS/custom providers to App Functions. Auth provider functions are called only by the platform auth flow. They validate external credentials and return identity assertions such as `phone`, `email`, `externalId`, or `unionId`; they must not issue tokens, set cookies, or mutate platform user/binding tables.
38
+
36
39
  ```json
37
40
  {
38
41
  "code": "crm",
@@ -14,6 +14,7 @@ Guidelines:
14
14
  - Use `sdk.function.invoke(code, { input })` for reusable backend business logic declared under `src/resources/functions/` and `src/functions/`. Do not implement multi-form orchestration, connector fan-out, notification orchestration, or permission-sensitive backend rules directly in a page component.
15
15
  - Use `sdk.connector.invoke`, `sdk.connector.call("connector.api")`, or `sdk.connector.download` for external services. The SDK calls the platform runtime connector endpoint; it must not call third-party domains directly.
16
16
  - Use `sdk.notification.sendByType` and `batchSendByType` for reusable business messages. Custom notification types must be declared in `src/resources/notifications/` and published with `openxiangda resource publish`.
17
+ - Use `createAuthClient` from `openxiangda/runtime` or `LoginPage` / `useAuth` from `openxiangda/runtime/react` for application login pages. Auth provider App Functions return identity assertions only; platform auth owns create/bind/reject/token decisions.
17
18
  - For the current user's department hierarchy, use `sdk.department.getCurrentUserParentDepartments()`; do not hardcode `GET /department/:id/parentDepartments` in page code.
18
19
  - Use `sdk.auth.logoutAndRedirect({ loginUrl })` for user logout when the page should return after login. It calls the platform logout endpoint, appends the current page URL as `callback`, and redirects to the login URL. Use `sdk.auth.logout()` only when the page wants to handle redirect itself.
19
20
  - Use `sdk.role.getMyRoles()`, `sdk.role.getCurrentRole()`, and `sdk.role.switchAppRole()` for current-user app role switching. Pass `roleId: ""` to switch back to all app roles.
@@ -102,6 +103,44 @@ const result = await sdk.function.invoke("reservation_reminder_summary", {
102
103
 
103
104
  Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Current runtime invocation requires the caller to have app automation management permission; ordinary page users should normally consume function-backed data through a permission-aware page or a narrower backend resource.
104
105
 
106
+ Application auth:
107
+
108
+ ```tsx
109
+ import { LoginPage, useAuth, useLoginMethods } from "openxiangda/runtime/react";
110
+
111
+ export function DefaultLogin() {
112
+ return <LoginPage />;
113
+ }
114
+
115
+ export function CustomPhoneLogin() {
116
+ const auth = useAuth();
117
+ const methods = useLoginMethods();
118
+
119
+ async function submit(phone: string, code: string, challengeId: string) {
120
+ await auth.phoneCodeLogin({ phone, code, challengeId });
121
+ }
122
+
123
+ return null;
124
+ }
125
+ ```
126
+
127
+ Standalone client:
128
+
129
+ ```ts
130
+ import { createAuthClient } from "openxiangda/runtime";
131
+
132
+ const auth = createAuthClient({ appType, servicePrefix: "/service" });
133
+ const methods = await auth.getMethods();
134
+ const sent = await auth.sendPhoneCode({ phone, purpose: "login" });
135
+ const result = await auth.phoneCodeLogin({
136
+ phone,
137
+ code,
138
+ challengeId: sent.challengeId,
139
+ });
140
+ ```
141
+
142
+ For phone-code auth, the App Function provider receives `event`, `appType`, `method`, `credential`, `requestId`, `request`, and `challenge`. It may return `{ ok, providerState }` for send and `{ ok, identity }` for verify. It must not return `token`, `accessToken`, `refreshToken`, cookies, or mutate platform account tables.
143
+
105
144
  Logout and current-user role switching:
106
145
 
107
146
  ```ts
@@ -9,6 +9,7 @@
9
9
  - **平台 ID 由 CLI 解析**:`openxiangda resource publish --profile <name>` 把 code 解析成当前 profile 的真实 ID 并写回 `.openxiangda/state.json`。
10
10
  - **永远不写密钥**:第三方 API key、token、secret、password、authorization、headers 等绝不出现在 `src/resources/`,平台管理员在后台配置。
11
11
  - **多 profile 不共享 ID**:`dev` / `prod` 各自维护一份资源 ID 映射,复制 manifest 即可复用,不要复制平台 ID。
12
+ - **Auth provider 只返回身份声明**:登录 provider App Function 不能发 token、写 cookie、直接改用户表或绑定表;平台按策略创建/绑定/拒绝。
12
13
 
13
14
  ## 命令
14
15
 
@@ -20,6 +21,86 @@ openxiangda resource publish --profile <name> --prune # 删除 manifest 未声
20
21
  openxiangda resource pull --profile <name> # 拉取平台资源回写到本地(用于初次同步)
21
22
  ```
22
23
 
24
+ ## 0. Auth — `src/resources/auth/<code>.json`
25
+
26
+ ```json
27
+ {
28
+ "code": "default",
29
+ "name": "Default App Login",
30
+ "status": "active",
31
+ "configJson": {
32
+ "methods": [
33
+ { "type": "password", "enabled": true, "label": "账号密码" },
34
+ { "type": "dingtalk", "enabled": true, "label": "钉钉免登" },
35
+ { "type": "sso", "enabled": true, "label": "CAS", "protocol": "cas" },
36
+ {
37
+ "type": "phone_code",
38
+ "enabled": true,
39
+ "label": "手机号验证码",
40
+ "ttlSeconds": 300,
41
+ "sendFrequencySeconds": 60,
42
+ "maxAttempts": 5,
43
+ "provider": { "functionCode": "auth_phone_code" }
44
+ }
45
+ ],
46
+ "registration": { "mode": "reject" },
47
+ "binding": { "mode": "auto" },
48
+ "matching": {
49
+ "keys": ["phone", "email", "externalId", "unionId", "jobNumber", "username"]
50
+ },
51
+ "defaultRoleCodes": []
52
+ }
53
+ }
54
+ ```
55
+
56
+ Provider function:
57
+
58
+ ```ts
59
+ export default async function authPhoneCodeProvider(ctx, input) {
60
+ if (input.event === "phone_code.send") {
61
+ return { ok: true, providerState: { nonce: "vendor-message-id" } };
62
+ }
63
+
64
+ if (input.event === "phone_code.verify") {
65
+ if (input.credential.code !== "123456") {
66
+ return { ok: false, message: "验证码错误" };
67
+ }
68
+ return {
69
+ ok: true,
70
+ identity: {
71
+ phone: input.credential.phone,
72
+ externalId: `phone:${input.credential.phone}`
73
+ }
74
+ };
75
+ }
76
+
77
+ return { ok: false, message: "不支持的认证事件" };
78
+ }
79
+ ```
80
+
81
+ React 默认页:
82
+
83
+ ```tsx
84
+ import { LoginPage } from "openxiangda/runtime/react";
85
+
86
+ export default function AppLogin() {
87
+ return <LoginPage />;
88
+ }
89
+ ```
90
+
91
+ 自定义登录页:
92
+
93
+ ```ts
94
+ import { createAuthClient } from "openxiangda/runtime";
95
+
96
+ const auth = createAuthClient({ appType, servicePrefix: "/service" });
97
+ const methods = await auth.getMethods();
98
+ const sent = await auth.sendPhoneCode({ phone, purpose: "login" });
99
+ await auth.phoneCodeLogin({ phone, code, challengeId: sent.challengeId });
100
+ ```
101
+
102
+ 设计前必须确认启用方式、注册策略、身份匹配键、provider 边界、默认权限、安全参数、第三方配置归属。
103
+
23
104
  ## 1. Connector — `src/resources/connectors/<code>.json`
24
105
 
25
106
  ```json
@@ -64,7 +64,7 @@ openxiangda resource publish --profile <name>
64
64
  openxiangda runtime deploy --profile <name>
65
65
  ```
66
66
 
67
- `runtime deploy` builds and activates the app-level SPA release for `/view/:appType/*`.
67
+ `runtime deploy` builds and activates the app-level SPA release for `/view/:appType/*`. It uploads `dist/` through staged multipart file uploads by default; use `--upload-mode legacy-json` only as an older-server fallback.
68
68
  Publish React SPA forms separately with `openxiangda workspace publish --form <formCode>`;
69
69
  with `runtimeMode: "react-spa"` this is schema-only and does not require OSS.
70
70
 
@@ -43,7 +43,7 @@ openxiangda resource publish --profile <name>
43
43
  openxiangda runtime deploy --profile <name>
44
44
  ```
45
45
 
46
- `runtime deploy` builds the Vite app with a release-specific asset base, uploads `dist/` to the platform runtime release store, and activates the release unless `--no-activate` is passed.
46
+ `runtime deploy` builds the Vite app with a release-specific asset base, uploads `dist/` to the platform runtime release store, and activates the release unless `--no-activate` is passed. The default upload mode is staged multipart file upload plus a final release manifest; use `--upload-mode legacy-json` only for older platform servers that have not been upgraded.
47
47
 
48
48
  For React SPA forms, still use targeted form publish to create/bind the form and
49
49
  sync schema:
@@ -282,8 +282,11 @@ openxiangda runtime deploy --profile prod
282
282
  It is responsible for:
283
283
 
284
284
  1. Running the workspace build command with `OPENXIANGDA_APP_TYPE`, `OPENXIANGDA_BUILD_ID`, and `OPENXIANGDA_RUNTIME_ASSET_BASE`.
285
- 2. Uploading `dist/` to `/openxiangda-api/v1/apps/:appType/runtime/releases`.
286
- 3. Activating the new runtime release so `/view/:appType/*` enters the React app.
285
+ 2. Uploading each `dist/` file to `/openxiangda-api/v1/apps/:appType/runtime/releases/files` with a shared trace id.
286
+ 3. Submitting the final release manifest to `/openxiangda-api/v1/apps/:appType/runtime/releases` without inline base64 content.
287
+ 4. Activating the new runtime release so `/view/:appType/*` enters the React app.
288
+
289
+ Runtime upload/finalize timeout defaults to 120s. Override with `--upload-timeout-ms <ms>` or `OPENXIANGDA_RUNTIME_UPLOAD_TIMEOUT_MS`; progress and `traceId` are written to stderr, while `--json` keeps stdout as final JSON only.
287
290
 
288
291
  ## References
289
292
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.83",
3
+ "version": "1.0.85",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {