openxiangda 1.0.174 → 1.0.175

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.md CHANGED
@@ -94,7 +94,7 @@ Architecture-class requests are plan-gated by default. For new apps, complex pag
94
94
 
95
95
  Risk is tiered. Read-only/docs/tests are L0. Narrow reversible fixes are L1 and use `sdd quick`; L2/L3 retain explicit approval and exact structured coverage. SDD is streamlined by default: incomplete task/evidence/spec prose warns but does not block. Workspaces that intentionally require prose completion may set `strictDocumentation: true`. Actual argv, exact scope, mainline identity, lease, CAS, destructive operations, Secrets, and Root App atomic activation remain hard gates.
96
96
 
97
- L1 quick records are generated in a compact form and should not be expanded into design essays. Mainline bundles may keep `<profile>` in reviewed command templates; `release publish --profile <name>` binds the real profile to actual argv without rewriting tracked SDD. React SPA page codes remain logical coverage and activate through one Runtime child. `release app-head` returns a compact summary by default; add `--full` for the complete manifest.
97
+ L1 quick records are generated in a compact form and should not be expanded into design essays. Mainline bundles may keep `<profile>` in reviewed command templates; `release publish --profile <name>` binds the real profile to actual argv without rewriting tracked SDD. React SPA page codes remain logical coverage and activate through one Runtime child. `release app-head` and `runtime releases` return compact summaries by default; add `--full` for the complete manifest.
98
98
 
99
99
  Every concurrent task uses its own Git worktree/branch and development change. The mainline release coordinator bundles selected approved changes after merge; dependency impact outside the approved scope is reported as a warning rather than silently widening a small release. Live commands still require canonical exact selectors, and app-wide/delete operations need explicit authority.
100
100
 
package/lib/cli.js CHANGED
@@ -1623,10 +1623,14 @@ async function release(args) {
1623
1623
  const baseline = getStoredChangeBaseline(target, {
1624
1624
  access: 'reconciliation-read',
1625
1625
  });
1626
- const sourceRevision = baseline?.releaseSourceRevision;
1626
+ const changeId = positional[0] || readStringFlag(flags, 'change');
1627
+ const recovered = readReleaseRecoveryContext(target, changeId);
1628
+ const sourceRevision =
1629
+ baseline?.releaseSourceRevision ||
1630
+ recovered?.context?.releaseSourceRevision;
1627
1631
  if (!sourceRevision) {
1628
1632
  fail(
1629
- 'RELEASE_SOURCE_LINEAGE_REQUIRED: 当前 release 没有冻结的 releaseSourceRevision,不能证明发布提交已回到权威主分支'
1633
+ 'RELEASE_SOURCE_LINEAGE_REQUIRED: 当前 release 没有冻结的 releaseSourceRevision;发布已结束时请同时传入 --change <id>,以便从私有执行日志恢复主线证据'
1630
1634
  );
1631
1635
  }
1632
1636
  const result = inspectReleaseSourceIntegration(
@@ -3101,7 +3105,17 @@ async function runAppReleaseCommand(
3101
3105
  let stateReconcileWarning = null;
3102
3106
  try {
3103
3107
  const appHead = await loadRuntimeHeadApp(config, target);
3104
- saveRuntimeHeadState(target, appHead);
3108
+ const stagedRuntime = stagedResources?.find(
3109
+ resource => resource.kind === 'RuntimeRelease'
3110
+ );
3111
+ saveRuntimeHeadState(target, appHead, {
3112
+ id:
3113
+ stagedRuntime?.identity?.releaseId ||
3114
+ stagedRuntime?.releaseId ||
3115
+ null,
3116
+ buildId: stagedRuntime?.metadata?.buildId || null,
3117
+ assetBaseUrl: stagedRuntime?.metadata?.assetBaseUrl || null,
3118
+ });
3105
3119
  } catch (error) {
3106
3120
  stateReconcileWarning = {
3107
3121
  component: 'RuntimeStateReadback',
@@ -9953,11 +9967,11 @@ async function runtime(args) {
9953
9967
  const { flags, positional } = parseArgs(rest);
9954
9968
  if (wantsSubcommandHelp(subcommand, flags)) {
9955
9969
  print([
9956
- '用法: openxiangda runtime deploy|releases|activate [--profile name] [--json]',
9970
+ '用法: openxiangda runtime deploy|releases [--full]|activate [--profile name] [--json]',
9957
9971
  '常用流程:',
9958
9972
  ' npm run build',
9959
9973
  ' openxiangda runtime deploy --no-activate --change <change> --profile <name> --json',
9960
- ' openxiangda runtime releases --profile <name>',
9974
+ ' openxiangda runtime releases --profile <name> [--full]',
9961
9975
  '说明:',
9962
9976
  ' - deploy 上传当前工作区 dist/ 并激活 React SPA runtime release。',
9963
9977
  ' - 默认 --upload-mode auto;multipart 上传遇到 403 时自动切换到平台内置对象存储直传。',
@@ -9984,8 +9998,9 @@ async function runtime(args) {
9984
9998
  target.profileName,
9985
9999
  `/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/runtime/releases`
9986
10000
  );
9987
- if (flags.json) return writeJson(data);
9988
- print(JSON.stringify(data, null, 2));
10001
+ const output = flags.full ? data : summarizeRuntimeReleaseList(data);
10002
+ if (flags.json) return writeJson(output);
10003
+ print(JSON.stringify(output, null, 2));
9989
10004
  return;
9990
10005
  }
9991
10006
 
@@ -10298,6 +10313,7 @@ async function runtime(args) {
10298
10313
  const runtimeContentHash = String(data?.contentHash || '')
10299
10314
  .trim()
10300
10315
  .toLowerCase();
10316
+ const releaseAssetBaseUrl = data?.assetBaseUrl || assetBaseUrl;
10301
10317
  const stagedResource = flags['no-activate']
10302
10318
  ? stripUndefinedValues({
10303
10319
  kind: 'RuntimeRelease',
@@ -10311,6 +10327,7 @@ async function runtime(args) {
10311
10327
  metadata: {
10312
10328
  buildId: data?.buildId || buildId,
10313
10329
  releaseStatus: data?.status || 'staged',
10330
+ assetBaseUrl: releaseAssetBaseUrl,
10314
10331
  },
10315
10332
  })
10316
10333
  : null;
@@ -10335,7 +10352,6 @@ async function runtime(args) {
10335
10352
  target
10336
10353
  )
10337
10354
  : null;
10338
- const releaseAssetBaseUrl = data?.assetBaseUrl || assetBaseUrl;
10339
10355
  const result = {
10340
10356
  appType: target.appType,
10341
10357
  buildId,
@@ -10381,7 +10397,7 @@ async function runtime(args) {
10381
10397
  }
10382
10398
  }
10383
10399
 
10384
- fail('用法: openxiangda runtime deploy|releases|activate [--profile name]');
10400
+ fail('用法: openxiangda runtime deploy|releases [--full]|activate [--profile name]');
10385
10401
  }
10386
10402
 
10387
10403
  async function loadRuntimeHeadApp(config, target) {
@@ -10568,9 +10584,11 @@ async function uploadRuntimeDistFilesStaged(options) {
10568
10584
  async file => {
10569
10585
  const uploaded = await uploadRuntimeDistFile(options, file);
10570
10586
  completed += 1;
10571
- printRuntimeProgress(
10572
- `runtime file uploaded [${completed}/${files.length}] ${file.path} ${formatBytes(file.size)} traceId=${options.traceId}`
10573
- );
10587
+ if (shouldReportRuntimeUploadProgress(completed, files.length)) {
10588
+ printRuntimeProgress(
10589
+ `runtime file uploaded [${completed}/${files.length}] ${file.path} ${formatBytes(file.size)} traceId=${options.traceId}`
10590
+ );
10591
+ }
10574
10592
  return uploaded;
10575
10593
  }
10576
10594
  );
@@ -10625,9 +10643,11 @@ async function uploadRuntimeDistFilesOssDirect(options) {
10625
10643
  );
10626
10644
  const uploaded = await uploadRuntimeDistFileToSignedUrl(options, file, uploadInfo);
10627
10645
  completed += 1;
10628
- printRuntimeProgress(
10629
- `runtime ${formatRuntimeStorageProvider(storageProvider)} file uploaded [${completed}/${files.length}] ${file.path} ${formatBytes(file.size)} traceId=${options.traceId}`
10630
- );
10646
+ if (shouldReportRuntimeUploadProgress(completed, files.length)) {
10647
+ printRuntimeProgress(
10648
+ `runtime ${formatRuntimeStorageProvider(storageProvider)} file uploaded [${completed}/${files.length}] ${file.path} ${formatBytes(file.size)} traceId=${options.traceId}`
10649
+ );
10650
+ }
10631
10651
  return { ...uploaded, storageProvider };
10632
10652
  }
10633
10653
  );
@@ -10889,21 +10909,71 @@ function saveRuntimeReleaseState(target, release) {
10889
10909
  saveProjectState(target.state);
10890
10910
  }
10891
10911
 
10892
- function saveRuntimeHeadState(target, app) {
10912
+ function saveRuntimeHeadState(target, app, fallbackRelease = {}) {
10893
10913
  const activeReleaseId =
10894
- app?.activeRuntimeReleaseId || app?.activeRuntime?.releaseId || null;
10914
+ app?.activeRuntimeReleaseId ||
10915
+ app?.activeRuntime?.releaseId ||
10916
+ fallbackRelease?.id ||
10917
+ fallbackRelease?.releaseId ||
10918
+ null;
10895
10919
  const activeBuildId =
10896
- app?.activeRuntimeBuildId || app?.activeRuntime?.buildId || null;
10920
+ app?.activeRuntimeBuildId ||
10921
+ app?.activeRuntime?.buildId ||
10922
+ fallbackRelease?.buildId ||
10923
+ null;
10897
10924
  if (!activeReleaseId && !activeBuildId) return false;
10898
10925
  saveRuntimeReleaseState(target, {
10899
10926
  id: activeReleaseId,
10900
10927
  buildId: activeBuildId,
10901
10928
  assetBaseUrl:
10902
- app?.runtimeAssetBaseUrl || app?.activeRuntime?.assetBaseUrl || null,
10929
+ app?.runtimeAssetBaseUrl ||
10930
+ app?.activeRuntime?.assetBaseUrl ||
10931
+ fallbackRelease?.assetBaseUrl ||
10932
+ null,
10903
10933
  });
10904
10934
  return true;
10905
10935
  }
10906
10936
 
10937
+ function summarizeRuntimeReleaseList(data) {
10938
+ const items = Array.isArray(data)
10939
+ ? data
10940
+ : Array.isArray(data?.items)
10941
+ ? data.items
10942
+ : [];
10943
+ const summarized = items.map(item =>
10944
+ stripUndefinedValues({
10945
+ id: item?.id,
10946
+ version: item?.version,
10947
+ buildId: item?.buildId,
10948
+ status: item?.status,
10949
+ storageProvider: item?.storageProvider,
10950
+ fileCount: item?.fileCount,
10951
+ sizeBytes: item?.sizeBytes,
10952
+ contentHash: item?.contentHash,
10953
+ sourceRevision: item?.sourceRevision,
10954
+ parentReleaseId: item?.parentReleaseId,
10955
+ parentSourceRevision: item?.parentSourceRevision,
10956
+ lineageBypassReason: item?.lineageBypassReason,
10957
+ lineageBootstrap: item?.lineageBootstrap,
10958
+ releaseNotes: item?.releaseNotes,
10959
+ createdAt: item?.createdAt,
10960
+ activatedAt: item?.activatedAt,
10961
+ })
10962
+ );
10963
+ return Array.isArray(data)
10964
+ ? summarized
10965
+ : {
10966
+ ...(data && typeof data === 'object' ? data : {}),
10967
+ items: summarized,
10968
+ };
10969
+ }
10970
+
10971
+ function shouldReportRuntimeUploadProgress(completed, total) {
10972
+ if (total <= 10) return true;
10973
+ const interval = Math.max(1, Math.ceil(total / 10));
10974
+ return completed === 1 || completed === total || completed % interval === 0;
10975
+ }
10976
+
10907
10977
  function formatBytes(value) {
10908
10978
  const bytes = Number(value || 0);
10909
10979
  if (bytes < 1024) return `${bytes}B`;
@@ -14350,7 +14420,7 @@ async function tryBuildFunctionSourcesWithLegacyWorkspaceBuilder(
14350
14420
 
14351
14421
  let usedLegacyBuilder = false;
14352
14422
  for (const sourceInfo of sourceInfos) {
14353
- const result = runWorkspaceJsCodeBuild(
14423
+ const result = runLegacyWorkspaceJsCodeBuild(
14354
14424
  workspaceRoot,
14355
14425
  sourceInfo.scriptCode,
14356
14426
  sourceInfo.sourceKind
@@ -21237,6 +21307,27 @@ function runWorkspaceJsCodeBuild(workspaceRoot, resolvedScriptCode, sourceKind)
21237
21307
  );
21238
21308
  }
21239
21309
 
21310
+ function runLegacyWorkspaceJsCodeBuild(
21311
+ workspaceRoot,
21312
+ resolvedScriptCode,
21313
+ sourceKind
21314
+ ) {
21315
+ return spawnSync(
21316
+ 'pnpm',
21317
+ [
21318
+ 'build-js-code',
21319
+ '--script',
21320
+ resolvedScriptCode,
21321
+ '--source',
21322
+ sourceKind,
21323
+ ],
21324
+ {
21325
+ cwd: workspaceRoot,
21326
+ encoding: 'utf8',
21327
+ }
21328
+ );
21329
+ }
21330
+
21240
21331
  function isUnsupportedFunctionsBuildScript(result) {
21241
21332
  const output = `${result.stdout || ''}\n${result.stderr || ''}`;
21242
21333
  return (
@@ -91,7 +91,7 @@ openxiangda release publish --change <release-change> --profile <name>
91
91
 
92
92
  `release publish` is the default promotion entrypoint. It verifies without rewriting reviewed `change.json`/`release.json`, waits for the app lease, freezes the App capture after ownership is acquired, executes deterministic exact staged steps, resumes from `.openxiangda/releases/<change>/execution.json`, atomically finalizes, verifies mainline integration, and releases the lease. `release begin` and child commands remain recovery/diagnostic primitives.
93
93
 
94
- Reviewed bundle commands may retain `<profile>` as a template. The explicit real `release publish --profile <name>` value is bound to actual child argv without rewriting tracked SDD. React SPA page codes are logical coverage targets and activate through one Runtime child; they do not require PageRelease. `release app-head` is compact by default; use `--full` only when the complete manifest is required.
94
+ Reviewed bundle commands may retain `<profile>` as a template. The explicit real `release publish --profile <name>` value is bound to actual child argv without rewriting tracked SDD. React SPA page codes are logical coverage targets and activate through one Runtime child; they do not require PageRelease. `release app-head` and `runtime releases` are compact by default; use `--full` only when the complete manifest is required.
95
95
 
96
96
  `release begin --change` freezes a clean committed `HEAD` only when the current branch is the authoritative default `main`/`master` and its commit exactly equals the live remote tip. Before any live write, the CLI preflights the complete target set and rejects source changes during the release. A feature worktree or unpushed main receives `RELEASE_SOURCE_MAINLINE_REQUIRED` / `RELEASE_SOURCE_MAINLINE_NOT_PUSHED`; merge, test, push, and start the one mainline release instead of forcing it. Optional `.git` remote suffix differences are aliases of the same repository; genuinely different remotes still fail closed.
97
97
 
@@ -98,7 +98,7 @@ Reviewed bundle commands may retain `<profile>` as a template. The explicit real
98
98
 
99
99
  `runtime deploy --no-activate` uploads an immutable preview release from a clean committed mainline `HEAD`. It reads a narrow Runtime head instead of the full app snapshot. Before acquiring a lease, the CLI confirms `package.json#scripts.build` and existing dependencies; it uses `npm run build` to execute the declared script so pnpm worktree symlinks do not trigger a reinstall. Immutable Git-base artifact hashes are cached under the Git common directory. `release begin` requires local main/master and the live remote default tip to be identical, so `integration-status` is already satisfied after activation and `release end` does not wait for a later merge.
100
100
 
101
- `release app-head` returns a compact head/resource-count summary by default. Use `--full` only when the complete manifest is required.
101
+ `release app-head` and `runtime releases` return compact summaries by default. Use `--full` only when the complete manifest is required.
102
102
 
103
103
  `resource plan`, `resource publish`, and `runtime deploy` report coarse phases on stderr and emit a heartbeat after 15 seconds without completion. JSON stdout stays machine-readable and includes `timings`; use it to identify whether time is spent in plan, preflight, build, upload, write, or activate instead of rerunning an opaque command.
104
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.174",
3
+ "version": "1.0.175",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {