openxiangda 1.0.165 → 1.0.166

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.
@@ -32,7 +32,7 @@ pnpm typecheck
32
32
  pnpm typecheck:js-code
33
33
  pnpm build
34
34
  pnpm build-js-code
35
- openxiangda workspace publish --profile <name> --form <formCode>
35
+ openxiangda resource publish form-setting --only <formCode> --change <change> --profile <name>
36
36
  openxiangda form export <formCode> --mode package --profile <name> --output ./exports/
37
37
  openxiangda doctor --profile <name> --json
38
38
  openxiangda design gates --topic public-access --json
@@ -42,37 +42,37 @@ openxiangda resource plan <type> --only <code1,code2> --profile <name>
42
42
  openxiangda runtime deploy --change <change> --profile <name> --no-activate --json
43
43
  openxiangda sdd verify <change> --changed --stage prepublish
44
44
  openxiangda release begin --change <change> --profile <name>
45
- openxiangda resource publish <type> --only <code1,code2> --change <change> --profile <name>
46
- openxiangda runtime activate <reviewedReleaseId> --change <change> --profile <name>
45
+ openxiangda resource publish function,automation --only function:<code>,automation:<code> --stage-only --change <change> --profile <name>
46
+ openxiangda release app-finalize --staged-resources-json .openxiangda/releases/<change>/staged-resources.json --change <change> --profile <name>
47
47
  openxiangda release integration-status --profile <name>
48
48
  openxiangda release end --profile <name>
49
49
  openxiangda commands --json
50
50
  ```
51
51
 
52
- 模板已停用无范围的 `pnpm deploy` 聚合入口。日常变更必须使用 `resource plan|publish <type> --only <codes>`(单资源可用 `--code <code>`),Runtime 先 `--no-activate` 生成预览版本,再做带基线校验的激活。如果应用包含 `src/forms/<formCode>/schema.ts`,先执行带同一 `--change` 的精确 form publish 初始化 schema/table。不要直接运行 `pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`。
52
+ 模板已停用无范围的 `pnpm deploy` 聚合入口。日常变更必须使用 `resource plan|publish <type> --only <codes>`(单资源可用 `--code <code>`)。Form bundle、Backend Release 和 Runtime 都先暂存;CLI 会按 `--change` 自动聚合 `.openxiangda/releases/<change>/staged-resources.json`,最后只由一次 Root App finalize 原子激活。不要使用 `workspace publish --form`、单独 `runtime activate`、`pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`。
53
53
 
54
54
  `resource plan` 与 publish dry-run 严格只允许 GET/HEAD。遇到 `READ_ONLY_AUTH_REQUIRED` 时,先执行 `openxiangda auth refresh --profile <name>` 或重新登录再重试;不得在 plan 内自动 POST 刷新 token。
55
55
 
56
56
  完整发布顺序:
57
57
 
58
58
  ```bash
59
- openxiangda workspace publish --profile <name> --change <change> --form <formCode>
60
- openxiangda runtime deploy --change <change> --profile <name> --no-activate --json
61
59
  openxiangda release begin --change <change> --profile <name>
62
- openxiangda resource publish <type> --only <codes> --change <change> --profile <name>
63
- openxiangda runtime activate <reviewedReleaseId> --change <change> --profile <name>
60
+ openxiangda resource publish form-setting --only <formCodes> --change <change> --profile <name> --json
61
+ openxiangda resource publish function,automation --only function:<code>,automation:<code> --stage-only --change <change> --profile <name> --json
62
+ openxiangda runtime deploy --change <change> --profile <name> --no-activate --json
63
+ openxiangda release app-finalize --staged-resources-json .openxiangda/releases/<change>/staged-resources.json --change <change> --profile <name> --json
64
64
  # 将 release begin 冻结的 SHA merge/fast-forward 并 push 到权威主分支
65
65
  openxiangda release integration-status --profile <name>
66
66
  openxiangda release end --profile <name>
67
67
  ```
68
68
 
69
- `pnpm build-js-code` 会检查并打包 `src/js-code-nodes/<code>/index.ts`、`src/automations/<code>/index.ts`、`src/functions/<code>/index.ts`,供 JS_CODE V2、代码自动化和 App Function 资源发布使用。批量目标使用 `pnpm build-js-code --scripts functions:a,functions:b,automations:c`,也兼容重复的 `--script a --script b --source functions`。显式 selector 只校验和构建选中入口及其传递/shared/ambient 依赖;不带 selector 才执行全工作区 TypeScript 校验。构建缓存写入 `.openxiangda/build-cache.json` 并按 scoped target、实际共享依赖、构建配置、Node/Vite/TypeScript 版本及产物校验和判断命中;需要强制重建时追加 `--force`。
69
+ `pnpm build-js-code` 会检查并打包 `src/js-code-nodes/<code>/index.ts`、`src/automations/<code>/index.ts`、`src/functions/<code>/index.ts`,供 JS_CODE V2、代码自动化和 App Function 资源发布使用。批量目标使用 `pnpm build-js-code --scripts functions:a,functions:b,automations:c`,也兼容重复的 `--script a --script b --source functions`。正式 `resource plan/publish` 由 CLI 内置 scoped builder 一次批量构建选中入口及其传递/shared/ambient 依赖,不再为每个资源启动工作区 `pnpm`。缓存写入 `.openxiangda/build-cache.cli-v4.json`,稳定 `source_lineage_v1` 只按 authored source/dependencies 比较,不因构建器升级制造假冲突;需要强制重建时追加 `--force`。
70
70
 
71
71
  App Function 第三方凭据只能在 Function manifest 顶层声明 `secretRefs: [{name, required}]`,并使用 `function_v2` + `runtimeContractVersion: "trusted_node_v2"`;源码通过 `await ctx.secrets.get(name)` 获取。值只能经 `openxiangda secret create|rotate --value-stdin --change <id> --profile <name>` 或隐藏 TTY 配置,禁止进入 Git、`.env`、manifest、源码、构建产物、plan、日志和异常。本地测试只使用 `openxiangda function test --secret-from-env logical=ENV` 的隔离子进程注入。
72
72
 
73
73
  `openxiangda runtime deploy --no-activate` 会构建并上传不可变预览版本;发布前先提交所有可能进入构建的源码/配置。所有 Runtime deploy(包括 `--no-activate`)都会先获取应用发布 lease,并在任何构建和上传前冻结 clean `HEAD` 与当前 active Runtime 父血缘;旧分支返回 `RUNTIME_SOURCE_BASE_DIVERGED`,不能先上传旧 preview 再激活。`openspec/` SDD 证据和生成/状态目录不算源码 dirty。仅审批的回退可使用 `--allow-runtime-rollback --reason "至少 8 个字符"`;`--no-build` 不会跳过守卫。不要手工修改 `dist/index.html`。
74
74
 
75
- Function/Automation 仅因 TypeScript 源码变化被选中时,默认走服务端 source-field PATCH,保留线上 bindings、input/output contracts、metadata、trigger/view 配置和 enabled/published state。整包 manifest 替换必须精确 `--only/--code` 并加 `--replace-manifest --reason "..."`。`release begin --change` 会分别冻结 change base 与实际 clean publish HEAD,并要求后者包含当前远端主分支;旧 worktree 返回 `SOURCE_BASE_DIVERGED`/`RELEASE_SOURCE_BEHIND_MAIN`,期间远端字段变化返回 `RESOURCE_FIELD_CONFLICT`。上线后先 merge/push 冻结 SHA;正常 `release end` 会以 `RELEASE_MAIN_MERGE_PENDING` 阻断未回合提交。权威目标只取远端实时默认 `main`/`master`,本地分支、覆盖参数和租约过期都不能清除证据。
75
+ Function/Automation 走 Backend Release v2;同一个 child 可以混合 create、source-only update 与显式 manifest replacement,并对整个集合做 CAS。正式多资源发布必须使用精确 selector 和 `--stage-only`,不得回退为逐资源直写;整包 manifest 替换还必须显式加 `--replace-manifest --reason "..."`。`release begin --change` 会分别冻结 change base 与实际 clean publish HEAD,并要求后者包含当前远端主分支;旧 worktree 返回 `SOURCE_BASE_DIVERGED`/`RELEASE_SOURCE_BEHIND_MAIN`,期间远端字段变化返回 `RESOURCE_FIELD_CONFLICT`。上线后先 merge/push 冻结 SHA;正常 `release end` 会以 `RELEASE_MAIN_MERGE_PENDING` 阻断未回合提交。权威目标只取远端实时默认 `main`/`master`,本地分支、覆盖参数和租约过期都不能清除证据。
76
76
 
77
77
  ## 应用结构
78
78
 
@@ -25,7 +25,7 @@ const rootDir = path.resolve(process.env.OPENXIANGDA_WORKSPACE_ROOT || defaultRo
25
25
  const args = process.argv.slice(2);
26
26
  const require = createRequire(import.meta.url);
27
27
  const ts = require("typescript");
28
- const CACHE_VERSION = 3;
28
+ const CACHE_VERSION = 4;
29
29
  const builderFingerprint = crypto
30
30
  .createHash("sha256")
31
31
  .update(readFileSync(scriptFile))
@@ -37,7 +37,7 @@ const workspaceBuilderMatches =
37
37
  builderFingerprint;
38
38
  const cacheFileName =
39
39
  path.resolve(defaultRootDir) !== rootDir && !workspaceBuilderMatches
40
- ? "build-cache.cli-v3.json"
40
+ ? "build-cache.cli-v4.json"
41
41
  : "build-cache.json";
42
42
  const CACHE_FILE = path.join(rootDir, ".openxiangda", cacheFileName);
43
43
  const CACHE_LOCK_FILE = `${CACHE_FILE}.lock`;
@@ -255,6 +255,19 @@ function hashFileSet(files, context) {
255
255
  return hash.digest("hex");
256
256
  }
257
257
 
258
+ function sourceFileSetHash(files, context) {
259
+ const hash = crypto.createHash("sha256");
260
+ hash.update(`source-lineage-v1\0${context}\0`);
261
+ for (const file of [...new Set(files)].sort()) {
262
+ const absolute = path.resolve(rootDir, file);
263
+ hash.update(`${file}\0`);
264
+ if (existsSync(absolute) && statSync(absolute).isFile()) hash.update(readFileSync(absolute));
265
+ else hash.update("<missing>");
266
+ hash.update("\0");
267
+ }
268
+ return hash.digest("hex");
269
+ }
270
+
258
271
  function configInputFiles() {
259
272
  return [
260
273
  "package.json",
@@ -461,9 +474,14 @@ async function buildScript(target) {
461
474
  },
462
475
  });
463
476
  const dependencies = collectBuildDependencies(buildResult, entry);
477
+ const entryRelative = relativeWorkspaceFile(entry);
464
478
  const output = outputFile(target);
465
479
  return {
466
480
  inputHash: targetInputHash(target, dependencies),
481
+ sourceHash: sourceFileSetHash(
482
+ [entryRelative, ...dependencies].filter(Boolean),
483
+ `target:${target.kind.name}/${target.scriptCode}`,
484
+ ),
467
485
  outputHash: sha256File(output),
468
486
  dependencies,
469
487
  output: relativeWorkspaceFile(output) || path.relative(rootDir, output),
@@ -9,8 +9,8 @@ const lines = [
9
9
  ' openxiangda runtime deploy --no-activate --change <change> --profile <name>',
10
10
  ' openxiangda sdd verify <change> --changed --stage prepublish',
11
11
  ' openxiangda release begin --change <change> --profile <name>',
12
- ' openxiangda resource publish <type> --only <codes> --change <change> --profile <name>',
13
- ' openxiangda runtime activate <reviewedReleaseId> --change <change> --profile <name>',
12
+ ' openxiangda resource publish function,automation --only function:<code>,automation:<code> --stage-only --change <change> --profile <name>',
13
+ ' openxiangda release app-finalize --staged-resources-json .openxiangda/releases/<change>/staged-resources.json --change <change> --profile <name>',
14
14
  '',
15
15
  '激活后必须把冻结的发布 SHA merge/fast-forward 并 push 到权威主分支:',
16
16
  ' openxiangda release integration-status --profile <name>',
@@ -23,7 +23,7 @@
23
23
  | 只发布改动 / 增量 / 单页 / 单表 | `openxiangda-core` | `... --changed` / `--page <code>` / `--form <code>` / `--only pages/a,forms/b` |
24
24
  | 创建应用 / 新建 app / 初始化工作区 | `openxiangda-app` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` |
25
25
  | 绑定已有应用 | `openxiangda-app` | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
26
- | 创建 / 改表单字段、schema、表单页 | `openxiangda-form` | 编辑 `src/forms/<code>/{schema.ts,page.tsx}` → `workspace publish --form <code>` |
26
+ | 创建 / 改表单字段、schema、表单页 | `openxiangda-form` | 编辑表单并声明 form-setting bundle → `resource publish form-setting --only <code>` 暂存;整应用由 `app-finalize` 激活 |
27
27
  | 导出表单数据 / 图片 / 附件 / Excel / zip | `openxiangda-form` | `openxiangda form export <formCode> --mode xlsx|xlsx-images|package --profile <name>` |
28
28
  | 创建 / 改自定义代码页、portal、看板 | `openxiangda-page` | 编辑 `src/pages/<code>/` → `workspace publish --page <code>` |
29
29
  | 审批流程 / 流程节点 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow validate / create / publish` |
@@ -52,7 +52,7 @@
52
52
  - ✅ 发现平台缺陷、能力缺口、规则不清、反复 workaround、AI 不确定点、用户可见体验问题时,主动 `openxiangda feedback submit --yes`;发送后告诉用户反馈内容和 fingerprint。
53
53
  - ✅ 正式多资源开发优先写 `src/resources/**` 后执行 `openxiangda resource validate|plan|publish <type> --only <codes>`;单资源可用 `--code <code>`。直接 CLI 写平台资源时先 `--dry-run`,需要避免漂移就加 `--write-manifest`。
54
54
  - ✅ `resource plan` 与 publish dry-run 严格只允许 GET/HEAD;遇到 `READ_ONLY_AUTH_REQUIRED` 时先执行 `openxiangda auth refresh --profile <name>` 或重新登录,不得在 plan 内自动 POST 刷新 token。
55
- - ✅ Function/Automation 因 TypeScript 源码变化被选中时,默认走服务端 source-field PATCH,保留线上 bindings、input/output contracts、metadata、trigger/view 配置和 enabled/published state。整包 manifest 替换必须精确 `--only/--code` 并显式加 `--replace-manifest --reason "..."`。
55
+ - ✅ Function/Automation 使用 Backend Release v2;正式多资源发布用精确 `--only/--code` 加 `--stage-only` 暂存,同一 child 可混合 create、source-only update 和显式 manifest replacement,再由 Root App finalize 原子激活。整包 manifest 替换必须另加 `--replace-manifest --reason "..."`。
56
56
  - ✅ 正式 promotion 先 `openxiangda release begin --change <change> --profile <name>`,冻结实际 clean publish HEAD + change/远端字段 baseline,并在任何 live write 前整体 preflight。旧 worktree 会收到 `SOURCE_BASE_DIVERGED`/`RELEASE_SOURCE_BEHIND_MAIN`,期间远端字段变化会收到 `RESOURCE_FIELD_CONFLICT`。所有 publish/activate 继续传同一 `--change`;上线后 merge/push 冻结 SHA,运行 `release integration-status`,再正常 `release end`。未回合会收到 `RELEASE_MAIN_MERGE_PENDING`;权威目标只取远端实时默认 `main`/`master`,本地分支、覆盖参数和租约过期都不能清除证据。
57
57
 
58
58
  ## 严禁
@@ -86,7 +86,7 @@
86
86
 
87
87
  ## JS_CODE 批量构建
88
88
 
89
- `pnpm build-js-code --scripts functions:a,functions:b,automations:c` 可在一次进程中构建多个 App Function / Automation / JS_CODE 目标;也兼容 `--script a --script b --source functions`。显式 selector 只校验和构建选中入口及其传递/shared/ambient 依赖;不带 selector 才保持全工作区 TypeScript 校验。`.openxiangda/build-cache.json` 分 scoped target 记录源码、实际共享依赖、构建配置、Node/Vite/TypeScript 版本及产物校验和,不能只因 `dist/**/index.cjs` 存在就视为有效;强制重建使用 `--force`。
89
+ `pnpm build-js-code --scripts functions:a,functions:b,automations:c` 可在一次进程中构建多个 App Function / Automation / JS_CODE 目标;也兼容重复的 `--script a --script b --source functions`。正式 `resource plan/publish` 使用 CLI 内置 scoped builder 和 `.openxiangda/build-cache.cli-v4.json`,按 authored source/dependencies 的稳定血缘增量构建;手工运行模板内 `pnpm build-js-code` 使用 `.openxiangda/build-cache.json`。两者都不能只因 `dist/**/index.cjs` 存在就视为有效;强制重建使用 `--force`。
90
90
 
91
91
  App Function 第三方凭据只能在 Function manifest 顶层声明 `secretRefs: [{name, required}]`,并使用 `function_v2` + `runtimeContractVersion: "trusted_node_v2"`;源码通过 `await ctx.secrets.get(name)` 获取。值只能用 `openxiangda secret create|rotate --value-stdin --change <id> --profile <name>` 或隐藏 TTY 配置,禁止写入 Git、`.env`、manifest、源码、构建产物、plan、日志和异常。本地联调只允许 `openxiangda function test --secret-from-env logical=ENV`,它不会把值写入 workspace/cache/state。
92
92
 
@@ -25,7 +25,7 @@ const rootDir = path.resolve(process.env.OPENXIANGDA_WORKSPACE_ROOT || defaultRo
25
25
  const args = process.argv.slice(2);
26
26
  const require = createRequire(import.meta.url);
27
27
  const ts = require("typescript");
28
- const CACHE_VERSION = 3;
28
+ const CACHE_VERSION = 4;
29
29
  const builderFingerprint = crypto
30
30
  .createHash("sha256")
31
31
  .update(readFileSync(scriptFile))
@@ -37,7 +37,7 @@ const workspaceBuilderMatches =
37
37
  builderFingerprint;
38
38
  const cacheFileName =
39
39
  path.resolve(defaultRootDir) !== rootDir && !workspaceBuilderMatches
40
- ? "build-cache.cli-v3.json"
40
+ ? "build-cache.cli-v4.json"
41
41
  : "build-cache.json";
42
42
  const CACHE_FILE = path.join(rootDir, ".openxiangda", cacheFileName);
43
43
  const CACHE_LOCK_FILE = `${CACHE_FILE}.lock`;
@@ -255,6 +255,19 @@ function hashFileSet(files, context) {
255
255
  return hash.digest("hex");
256
256
  }
257
257
 
258
+ function sourceFileSetHash(files, context) {
259
+ const hash = crypto.createHash("sha256");
260
+ hash.update(`source-lineage-v1\0${context}\0`);
261
+ for (const file of [...new Set(files)].sort()) {
262
+ const absolute = path.resolve(rootDir, file);
263
+ hash.update(`${file}\0`);
264
+ if (existsSync(absolute) && statSync(absolute).isFile()) hash.update(readFileSync(absolute));
265
+ else hash.update("<missing>");
266
+ hash.update("\0");
267
+ }
268
+ return hash.digest("hex");
269
+ }
270
+
258
271
  function configInputFiles() {
259
272
  return [
260
273
  "package.json",
@@ -461,9 +474,14 @@ async function buildScript(target) {
461
474
  },
462
475
  });
463
476
  const dependencies = collectBuildDependencies(buildResult, entry);
477
+ const entryRelative = relativeWorkspaceFile(entry);
464
478
  const output = outputFile(target);
465
479
  return {
466
480
  inputHash: targetInputHash(target, dependencies),
481
+ sourceHash: sourceFileSetHash(
482
+ [entryRelative, ...dependencies].filter(Boolean),
483
+ `target:${target.kind.name}/${target.scriptCode}`,
484
+ ),
467
485
  outputHash: sha256File(output),
468
486
  dependencies,
469
487
  output: relativeWorkspaceFile(output) || path.relative(rootDir, output),