create-windy 0.6.18 → 0.6.19

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.
Files changed (32) hide show
  1. package/dist/cli.js +126 -54
  2. package/package.json +1 -1
  3. package/template/.windy-template.json +2 -2
  4. package/template/README.md +5 -2
  5. package/template/apps/agent-server/README.md +6 -0
  6. package/template/apps/agent-server/src/southwind_agent_server/app.py +23 -0
  7. package/template/apps/agent-server/src/southwind_agent_server/health_routes.py +9 -0
  8. package/template/apps/agent-server/src/southwind_agent_server/runtime_compatibility.py +89 -0
  9. package/template/apps/agent-server/tests/test_runtime_compatibility.py +119 -0
  10. package/template/apps/server/src/agent/remote-runtime-compatibility.test.ts +147 -0
  11. package/template/apps/server/src/agent/remote-runtime.ts +66 -4
  12. package/template/apps/server/src/agent/runtime-bootstrap.ts +1 -0
  13. package/template/apps/web/src/components/common/FormDialog.vue +1 -0
  14. package/template/apps/web/src/components/common/ModalLayout.webtest.ts +1 -0
  15. package/template/apps/web/src/components/ui/dialog/DialogContent.vue +1 -1
  16. package/template/apps/web/src/components/ui/dialog/DialogScrollContent.vue +1 -1
  17. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +1 -0
  18. package/template/apps/web/src/pages/system/components/user-account/CreateDepartmentDialog.vue +182 -0
  19. package/template/apps/web/src/pages/system/components/user-account/CreateDepartmentDialog.webtest.ts +93 -0
  20. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +59 -0
  21. package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.vue +21 -71
  22. package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.webtest.ts +71 -0
  23. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +48 -27
  24. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +30 -1
  25. package/template/docs/architecture/ai-runtime.md +8 -15
  26. package/template/docs/platform/agent-runtime.md +5 -0
  27. package/template/packages/server-sdk/contracts/agent-runtime-compatibility/v1/offer.json +22 -0
  28. package/template/packages/server-sdk/index.ts +19 -0
  29. package/template/packages/server-sdk/package.json +2 -0
  30. package/template/packages/server-sdk/src/agent-runtime-compatibility.contract.test.ts +143 -0
  31. package/template/packages/server-sdk/src/agent-runtime-compatibility.public-api.test.ts +19 -0
  32. package/template/packages/server-sdk/src/agent-runtime-compatibility.ts +284 -0
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => {
33
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
34
  var __require = import.meta.require;
35
35
 
36
- // ../../node_modules/cli-width/index.js
36
+ // ../../node_modules/.bun/cli-width@4.1.0/node_modules/cli-width/index.js
37
37
  var require_cli_width = __commonJS((exports, module2) => {
38
38
  module2.exports = cliWidth;
39
39
  function normalizeOpts(options) {
@@ -73,7 +73,7 @@ var require_cli_width = __commonJS((exports, module2) => {
73
73
  }
74
74
  });
75
75
 
76
- // ../../node_modules/mute-stream/lib/index.js
76
+ // ../../node_modules/.bun/mute-stream@3.0.0/node_modules/mute-stream/lib/index.js
77
77
  var require_lib = __commonJS((exports, module2) => {
78
78
  var Stream = __require("stream");
79
79
 
@@ -3921,6 +3921,8 @@ bun run build
3921
3921
  bun run windy doctor
3922
3922
  bun run windy update --check
3923
3923
  bun run windy update --dry-run
3924
+ bun run windy update
3925
+ # \u5982\u9700\u5728\u672C\u6B21\u66F4\u65B0\u4E2D\u663E\u5F0F\u6267\u884C\u9879\u76EE\u8D28\u91CF\u811A\u672C\uFF1Abun run windy update --verify
3924
3926
  bun run windy module list
3925
3927
  ${moduleActivationCommands}
3926
3928
  ${includeOxc ? `bun run lint
@@ -3931,11 +3933,12 @@ ${includeOxc ? "\u63D0\u4EA4\u65F6 Husky \u4F1A\u901A\u8FC7 lint-staged \u4E0E O
3931
3933
 
3932
3934
  \u66F4\u65B0\u524D\u65E0\u9700\u63D0\u4EA4\u3001\u6682\u5B58\u6216\u6E05\u7406\u4E1A\u52A1\u6539\u52A8\u3002\`--dry-run\` \u53EA\u751F\u6210\u8BA1\u5212\uFF0C\u4E0D\u5199\u9879\u76EE\uFF1B\u65E0\u5173 WIP
3933
3935
  \u4FDD\u6301\u539F\u6837\uFF0C\u547D\u4E2D\u540C\u4E00\u8DEF\u5F84\u65F6\u6309\u51BB\u7ED3\u5FEB\u7167\u4E0E\u4E09\u65B9\u5408\u5E76 fail closed\u3002\u6B63\u5F0F\u66F4\u65B0\u5148\u5907\u4EFD\uFF0C\u518D\u505A
3934
- package \u5B57\u6BB5\u7EA7\u5408\u5E76\u3001\u5171\u4EAB\u811A\u624B\u67B6\u4E09\u65B9\u5408\u5E76\u4E0E\u5B8C\u6574\u9A8C\u8BC1\u3002\u6574\u4E2A \`apps/web/src\` \u548C
3936
+ package \u5B57\u6BB5\u7EA7\u5408\u5E76\u3001\u5171\u4EAB\u811A\u624B\u67B6\u4E09\u65B9\u5408\u5E76\u4E0E\u5B89\u5168\u6536\u53E3\uFF1B\u9ED8\u8BA4\u4E0D\u6267\u884C\u9879\u76EE lint\u3001typecheck\u3001
3937
+ test \u6216 build\uFF0C\u4F20\u5165 \`--verify\` \u624D\u663E\u5F0F\u6267\u884C\u5E76\u8BB0\u5F55\u7ED3\u679C\u3002\u6574\u4E2A \`apps/web/src\` \u548C
3935
3938
  \`apps/server/src/app\` \u9ED8\u8BA4\u4FDD\u6301\u9879\u76EE\u7248\u672C\uFF1B\u5DF2\u767B\u8BB0\u7248\u672C recipe \u53EA\u4FEE\u6539\u5176\u58F0\u660E\u7684\u7A84\u951A\u70B9\u3002
3936
3939
  \u4EFB\u4F55\u51B2\u7A81\u90FD\u4F1A\u5728\u5199\u5165\u524D\u4E2D\u6B62\uFF0C\u6A21\u5757\u542F\u505C\u5728\u4EA4\u4E92
3937
3940
  \u7EC8\u7AEF\u786E\u8BA4\uFF1BCI\u3001Agent \u6216\u5176\u5B83\u975E TTY \u73AF\u5883\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 \`--yes\`\u3002
3938
- \u65E0\u5E76\u53D1\u53D8\u5316\u7684\u9A8C\u8BC1\u5931\u8D25\u4F1A\u81EA\u52A8\u56DE\u6EDA\uFF1B\u4E2D\u65AD\u6062\u590D\u4F7F\u7528
3941
+ \u5B89\u5168\u68C0\u67E5\u5931\u8D25\u4F1A\u81EA\u52A8\u56DE\u6EDA\uFF1B\u9879\u76EE\u8D28\u91CF\u811A\u672C\u5931\u8D25\u672C\u8EAB\u4E0D\u56DE\u6EDA\u5DF2\u5B89\u5168\u5E94\u7528\u7684\u66F4\u65B0\u3002\u4E2D\u65AD\u6062\u590D\u4F7F\u7528
3939
3942
  \`bun run windy doctor --repair\`\u3002\u8BE5\u547D\u4EE4\u5728\u63D0\u4EA4\u70B9\u524D\u4FDD\u7559\u5E76\u53D1\u7F16\u8F91\u5E76\u56DE\u6EDA\u5176\u4F59\u8DEF\u5F84\uFF0C
3940
3943
  \u5728 committed \u63D0\u4EA4\u70B9\u540E\u53EA\u5B8C\u6210\u4E8B\u52A1\u72B6\u6001\u4E0E\u5907\u4EFD\u6E05\u7406\uFF0C\u4E0D\u56DE\u6EDA\u9879\u76EE\u6587\u4EF6\u3002
3941
3944
 
@@ -4666,7 +4669,7 @@ function isMissing6(error) {
4666
4669
  import { createInterface as createInterface2 } from "readline/promises";
4667
4670
  import { stdin, stdout } from "process";
4668
4671
 
4669
- // ../../node_modules/@inquirer/core/dist/lib/key.js
4672
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/key.js
4670
4673
  var keybindings = ["emacs", "vim"];
4671
4674
  var keybindingLookup = new Set(keybindings);
4672
4675
  function isKeybinding(value) {
@@ -4683,7 +4686,7 @@ var isDownKey = (key, keybindings2 = []) => key.name === "down" || keybindings2.
4683
4686
  var isSpaceKey = (key) => key.name === "space";
4684
4687
  var isNumberKey = (key) => "1234567890".includes(key.name);
4685
4688
  var isEnterKey = (key) => key.name === "enter" || key.name === "return";
4686
- // ../../node_modules/@inquirer/core/dist/lib/errors.js
4689
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/errors.js
4687
4690
  class AbortPromptError extends Error {
4688
4691
  name = "AbortPromptError";
4689
4692
  message = "Prompt was aborted";
@@ -4709,10 +4712,10 @@ class HookError extends Error {
4709
4712
  class ValidationError extends Error {
4710
4713
  name = "ValidationError";
4711
4714
  }
4712
- // ../../node_modules/@inquirer/core/dist/lib/use-state.js
4715
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-state.js
4713
4716
  import { AsyncResource as AsyncResource2 } from "async_hooks";
4714
4717
 
4715
- // ../../node_modules/@inquirer/core/dist/lib/hook-engine.js
4718
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/hook-engine.js
4716
4719
  import { AsyncLocalStorage, AsyncResource } from "async_hooks";
4717
4720
  var hookStorage = new AsyncLocalStorage;
4718
4721
  function createStore(rl) {
@@ -4817,7 +4820,7 @@ var effectScheduler = {
4817
4820
  }
4818
4821
  };
4819
4822
 
4820
- // ../../node_modules/@inquirer/core/dist/lib/use-state.js
4823
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-state.js
4821
4824
  function isFactory(value) {
4822
4825
  return typeof value === "function";
4823
4826
  }
@@ -4838,7 +4841,7 @@ function useState(defaultValue) {
4838
4841
  });
4839
4842
  }
4840
4843
 
4841
- // ../../node_modules/@inquirer/core/dist/lib/use-effect.js
4844
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-effect.js
4842
4845
  function useEffect(cb, depArray) {
4843
4846
  withPointer((pointer) => {
4844
4847
  const oldDeps = pointer.get();
@@ -4850,10 +4853,10 @@ function useEffect(cb, depArray) {
4850
4853
  });
4851
4854
  }
4852
4855
 
4853
- // ../../node_modules/@inquirer/core/dist/lib/theme.js
4856
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/theme.js
4854
4857
  import { styleText } from "util";
4855
4858
 
4856
- // ../../node_modules/@inquirer/figures/dist/index.js
4859
+ // ../../node_modules/.bun/@inquirer+figures@2.0.7/node_modules/@inquirer/figures/dist/index.js
4857
4860
  import process2 from "process";
4858
4861
  function isUnicodeSupported() {
4859
4862
  if (!process2.platform.startsWith("win")) {
@@ -5142,7 +5145,7 @@ var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
5142
5145
  var dist_default = figures;
5143
5146
  var replacements = Object.entries(specialMainSymbols);
5144
5147
 
5145
- // ../../node_modules/@inquirer/core/dist/lib/theme.js
5148
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/theme.js
5146
5149
  var defaultTheme = {
5147
5150
  prefix: {
5148
5151
  idle: styleText("blue", "?"),
@@ -5170,7 +5173,7 @@ function getDefaultTheme() {
5170
5173
  };
5171
5174
  }
5172
5175
 
5173
- // ../../node_modules/@inquirer/core/dist/lib/make-theme.js
5176
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/make-theme.js
5174
5177
  function isPlainObject(value) {
5175
5178
  if (typeof value !== "object" || value === null)
5176
5179
  return false;
@@ -5198,7 +5201,7 @@ function makeTheme(...themes) {
5198
5201
  return deepMerge(...themesToMerge);
5199
5202
  }
5200
5203
 
5201
- // ../../node_modules/@inquirer/core/dist/lib/use-prefix.js
5204
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-prefix.js
5202
5205
  function usePrefix({ status = "idle", theme }) {
5203
5206
  const [showLoader, setShowLoader] = useState(false);
5204
5207
  const [tick, setTick] = useState(0);
@@ -5228,7 +5231,7 @@ function usePrefix({ status = "idle", theme }) {
5228
5231
  const iconName = status === "loading" ? "idle" : status;
5229
5232
  return typeof prefix === "string" ? prefix : prefix[iconName] ?? prefix["idle"];
5230
5233
  }
5231
- // ../../node_modules/@inquirer/core/dist/lib/use-memo.js
5234
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-memo.js
5232
5235
  function useMemo(fn, dependencies) {
5233
5236
  return withPointer((pointer) => {
5234
5237
  const prev = pointer.get();
@@ -5240,12 +5243,12 @@ function useMemo(fn, dependencies) {
5240
5243
  return prev.value;
5241
5244
  });
5242
5245
  }
5243
- // ../../node_modules/@inquirer/core/dist/lib/use-ref.js
5246
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-ref.js
5244
5247
  function useRef(val) {
5245
5248
  return useState({ current: val })[0];
5246
5249
  }
5247
5250
 
5248
- // ../../node_modules/@inquirer/core/dist/lib/use-keypress.js
5251
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/use-keypress.js
5249
5252
  function useKeypress(userHandler) {
5250
5253
  const signal = useRef(userHandler);
5251
5254
  signal.current = userHandler;
@@ -5263,10 +5266,10 @@ function useKeypress(userHandler) {
5263
5266
  };
5264
5267
  }, []);
5265
5268
  }
5266
- // ../../node_modules/@inquirer/core/dist/lib/utils.js
5269
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/utils.js
5267
5270
  var import_cli_width = __toESM(require_cli_width(), 1);
5268
5271
 
5269
- // ../../node_modules/fast-string-truncated-width/dist/utils.js
5272
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
5270
5273
  var getCodePointsLength = (() => {
5271
5274
  const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
5272
5275
  return (input) => {
@@ -5285,7 +5288,7 @@ var isWideNotCJKTNotEmoji = (x) => {
5285
5288
  return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
5286
5289
  };
5287
5290
 
5288
- // ../../node_modules/fast-string-truncated-width/dist/index.js
5291
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
5289
5292
  var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
5290
5293
  var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
5291
5294
  var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
@@ -5384,7 +5387,7 @@ var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {})
5384
5387
  };
5385
5388
  var dist_default2 = getStringTruncatedWidth;
5386
5389
 
5387
- // ../../node_modules/fast-string-width/dist/index.js
5390
+ // ../../node_modules/.bun/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
5388
5391
  var NO_TRUNCATION2 = {
5389
5392
  limit: Infinity,
5390
5393
  ellipsis: "",
@@ -5395,7 +5398,7 @@ var fastStringWidth = (input, options = {}) => {
5395
5398
  };
5396
5399
  var dist_default3 = fastStringWidth;
5397
5400
 
5398
- // ../../node_modules/fast-wrap-ansi/lib/main.js
5401
+ // ../../node_modules/.bun/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
5399
5402
  var ESC = "\x1B";
5400
5403
  var CSI = "\x9B";
5401
5404
  var END_CODE = 39;
@@ -5607,7 +5610,7 @@ function wrapAnsi(string, columns, options) {
5607
5610
  `);
5608
5611
  }
5609
5612
 
5610
- // ../../node_modules/@inquirer/core/dist/lib/utils.js
5613
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/utils.js
5611
5614
  function breakLines(content, width) {
5612
5615
  return content.split(`
5613
5616
  `).flatMap((line) => wrapAnsi(line, width, { trim: false, wordWrap: false }).split(`
@@ -5618,7 +5621,7 @@ function readlineWidth() {
5618
5621
  return import_cli_width.default({ defaultWidth: 80, output: readline().output });
5619
5622
  }
5620
5623
 
5621
- // ../../node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
5624
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
5622
5625
  function usePointerPosition({ active, renderedItems, pageSize, loop }) {
5623
5626
  const state = useRef({
5624
5627
  lastPointer: active,
@@ -5684,12 +5687,12 @@ function usePagination({ items, active, renderItem, pageSize, loop = true }) {
5684
5687
  return pageBuffer.filter((line) => typeof line === "string").join(`
5685
5688
  `);
5686
5689
  }
5687
- // ../../node_modules/@inquirer/core/dist/lib/create-prompt.js
5690
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/create-prompt.js
5688
5691
  var import_mute_stream = __toESM(require_lib(), 1);
5689
5692
  import * as readline2 from "readline";
5690
5693
  import { AsyncResource as AsyncResource3 } from "async_hooks";
5691
5694
 
5692
- // ../../node_modules/signal-exit/dist/mjs/signals.js
5695
+ // ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/signals.js
5693
5696
  var signals = [];
5694
5697
  signals.push("SIGHUP", "SIGINT", "SIGTERM");
5695
5698
  if (process.platform !== "win32") {
@@ -5699,7 +5702,7 @@ if (process.platform === "linux") {
5699
5702
  signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
5700
5703
  }
5701
5704
 
5702
- // ../../node_modules/signal-exit/dist/mjs/index.js
5705
+ // ../../node_modules/.bun/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js
5703
5706
  var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
5704
5707
  var kExitEmitter = Symbol.for("signal-exit emitter");
5705
5708
  var global = globalThis;
@@ -5897,10 +5900,10 @@ var {
5897
5900
  unload
5898
5901
  } = signalExitWrap(processOk(process3) ? new SignalExit(process3) : new SignalExitFallback);
5899
5902
 
5900
- // ../../node_modules/@inquirer/core/dist/lib/screen-manager.js
5903
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/screen-manager.js
5901
5904
  import { stripVTControlCharacters } from "util";
5902
5905
 
5903
- // ../../node_modules/@inquirer/ansi/dist/index.js
5906
+ // ../../node_modules/.bun/@inquirer+ansi@2.0.7/node_modules/@inquirer/ansi/dist/index.js
5904
5907
  var ESC2 = "\x1B[";
5905
5908
  var cursorLeft = ESC2 + "G";
5906
5909
  var cursorHide = ESC2 + "?25l";
@@ -5916,7 +5919,7 @@ var cursorTo = (x, y) => {
5916
5919
  var eraseLine = ESC2 + "2K";
5917
5920
  var eraseLines = (lines) => lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : "";
5918
5921
 
5919
- // ../../node_modules/@inquirer/core/dist/lib/screen-manager.js
5922
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/screen-manager.js
5920
5923
  var height = (content) => content.split(`
5921
5924
  `).length;
5922
5925
  var lastLine = (content) => content.split(`
@@ -5982,7 +5985,7 @@ class ScreenManager {
5982
5985
  }
5983
5986
  }
5984
5987
 
5985
- // ../../node_modules/@inquirer/core/dist/lib/promise-polyfill.js
5988
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/promise-polyfill.js
5986
5989
  class PromisePolyfill extends Promise {
5987
5990
  static withResolver() {
5988
5991
  let resolve6;
@@ -5995,7 +5998,7 @@ class PromisePolyfill extends Promise {
5995
5998
  }
5996
5999
  }
5997
6000
 
5998
- // ../../node_modules/@inquirer/core/dist/lib/create-prompt.js
6001
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/create-prompt.js
5999
6002
  import path from "path";
6000
6003
  var nativeSetImmediate = globalThis.setImmediate;
6001
6004
  function getCallSites() {
@@ -6106,7 +6109,7 @@ function createPrompt(view) {
6106
6109
  };
6107
6110
  return prompt;
6108
6111
  }
6109
- // ../../node_modules/@inquirer/core/dist/lib/Separator.js
6112
+ // ../../node_modules/.bun/@inquirer+core@11.2.1+7cb241fc07b679d9/node_modules/@inquirer/core/dist/lib/Separator.js
6110
6113
  import { styleText as styleText2 } from "util";
6111
6114
  class Separator {
6112
6115
  separator = styleText2("dim", Array.from({ length: 15 }).join(dist_default.line));
@@ -6120,7 +6123,7 @@ class Separator {
6120
6123
  return Boolean(choice && typeof choice === "object" && "type" in choice && choice.type === "separator");
6121
6124
  }
6122
6125
  }
6123
- // ../../node_modules/@inquirer/checkbox/dist/index.js
6126
+ // ../../node_modules/.bun/@inquirer+checkbox@5.2.1+7cb241fc07b679d9/node_modules/@inquirer/checkbox/dist/index.js
6124
6127
  import { styleText as styleText3 } from "util";
6125
6128
  var checkboxTheme = {
6126
6129
  icon: {
@@ -8492,6 +8495,11 @@ var recipes = [
8492
8495
  id: "starter-0.6.17-to-0.6.18",
8493
8496
  from: "0.6.17",
8494
8497
  to: "0.6.18"
8498
+ },
8499
+ {
8500
+ id: "starter-0.6.18-to-0.6.19",
8501
+ from: "0.6.18",
8502
+ to: "0.6.19"
8495
8503
  }
8496
8504
  ];
8497
8505
  function resolveRecipeChain(sourceVersion, targetVersion) {
@@ -8537,8 +8545,7 @@ function parseVersion2(version) {
8537
8545
  }
8538
8546
 
8539
8547
  // src/update/updater.ts
8540
- import { readFile as readFile24, rm as rm8 } from "fs/promises";
8541
- import { join as join29 } from "path";
8548
+ import { rm as rm8 } from "fs/promises";
8542
8549
 
8543
8550
  // src/update/artifacts.ts
8544
8551
  import { mkdtemp, readFile as readFile15 } from "fs/promises";
@@ -10859,12 +10866,48 @@ function safeTemporaryPath(root, path2) {
10859
10866
  return target;
10860
10867
  }
10861
10868
 
10869
+ // src/update/project-quality-verification.ts
10870
+ import { readFile as readFile24 } from "fs/promises";
10871
+ import { join as join29 } from "path";
10872
+ var qualityScriptNames = ["lint", "typecheck", "test", "build"];
10873
+ function skippedProjectQualityVerification() {
10874
+ return {
10875
+ status: "skipped",
10876
+ steps: [
10877
+ {
10878
+ name: "\u9879\u76EE\u8D28\u91CF\u9A8C\u8BC1\uFF08lint/typecheck/test/build\uFF09",
10879
+ status: "skipped"
10880
+ }
10881
+ ]
10882
+ };
10883
+ }
10884
+ async function runProjectQualityVerification(projectDirectory, execute) {
10885
+ const packageJson = JSON.parse(await readFile24(join29(projectDirectory, "package.json"), "utf8"));
10886
+ const steps = [];
10887
+ for (const name of qualityScriptNames) {
10888
+ const stepName = `bun run ${name}`;
10889
+ if (!packageJson.scripts?.[name]) {
10890
+ steps.push({ name: stepName, status: "skipped" });
10891
+ continue;
10892
+ }
10893
+ try {
10894
+ await execute("bun", ["run", name], projectDirectory);
10895
+ steps.push({ name: stepName, status: "passed" });
10896
+ } catch {
10897
+ steps.push({ name: stepName, status: "failed" });
10898
+ return { status: "failed", steps };
10899
+ }
10900
+ }
10901
+ return { status: "passed", steps };
10902
+ }
10903
+
10862
10904
  // src/update/updater-report.ts
10863
- async function writeUpdateReport(result2, steps, relativePath2, expected) {
10905
+ async function writeUpdateReport(result2, steps, projectQualityVerification, relativePath2, expected) {
10864
10906
  const lines = [
10865
10907
  `# Windy \u66F4\u65B0\u62A5\u544A ${result2.plan.id}`,
10866
10908
  "",
10867
- "- \u9A8C\u8BC1\u72B6\u6001\uFF1A\u5DF2\u901A\u8FC7\uFF1B\u4E8B\u52A1\u7ED3\u679C\u4EE5 update-state \u6700\u7EC8\u63D0\u4EA4\u4E3A\u51C6",
10909
+ "- \u5B89\u5168\u63D0\u4EA4\u72B6\u6001\uFF1A\u5DF2\u901A\u8FC7\uFF1B\u4E8B\u52A1\u7ED3\u679C\u4EE5 update-state \u6700\u7EC8\u63D0\u4EA4\u4E3A\u51C6",
10910
+ `- \u9879\u76EE\u8D28\u91CF\u9A8C\u8BC1\uFF1A${qualityStatusLabel(projectQualityVerification)}`,
10868
10911
  `- \u6765\u6E90\u7248\u672C\uFF1A${result2.plan.sourceVersion}`,
10869
10912
  `- \u76EE\u6807\u7248\u672C\uFF1A${result2.plan.targetVersion}`,
10870
10913
  `- Recipe\uFF1A${result2.plan.recipes.join("\u3001") || "\u65E0"}`,
@@ -10883,10 +10926,14 @@ async function writeUpdateReport(result2, steps, relativePath2, expected) {
10883
10926
  ...result2.plan.manualCleanup.map(({ message }) => `- ${message}`)
10884
10927
  ] : [],
10885
10928
  "",
10886
- "## \u9A8C\u8BC1",
10929
+ "## \u5B89\u5168\u68C0\u67E5\u4E0E\u53EF\u9009\u9A8C\u8BC1",
10887
10930
  "",
10888
10931
  ...steps.map(({ name, status }) => `- ${name}\uFF1A${status}`),
10889
10932
  "",
10933
+ ...projectQualityVerification === "skipped" ? [
10934
+ "\u672C\u6B21\u66F4\u65B0\u672A\u6267\u884C\u9879\u76EE\u7EA7 lint/typecheck/test/build\u3002\u9700\u8981\u5B8C\u6574\u8D28\u91CF\u9A8C\u8BC1\u65F6\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u6309\u5B9E\u9645\u5B58\u5728\u7684\u811A\u672C\u663E\u5F0F\u8FD0\u884C\u8FD9\u4E9B\u547D\u4EE4\u3002",
10935
+ ""
10936
+ ] : [],
10890
10937
  "\u4E8B\u52A1\u63D0\u4EA4\u540E\u81EA\u52A8\u6E05\u7406\u5907\u4EFD\uFF1B\u82E5\u63D0\u793A\u6E05\u7406\u5F85\u5B8C\u6210\uFF0C\u8BF7\u8FD0\u884C `bun run windy doctor --repair`\u3002",
10891
10938
  ""
10892
10939
  ];
@@ -10895,6 +10942,13 @@ async function writeUpdateReport(result2, steps, relativePath2, expected) {
10895
10942
  await writeOwnedTransactionFile(result2.plan.projectDirectory, relativePath2, content, expected);
10896
10943
  return expectationForContent(relativePath2, content);
10897
10944
  }
10945
+ function qualityStatusLabel(status) {
10946
+ if (status === "passed")
10947
+ return "\u5DF2\u901A\u8FC7\uFF08\u663E\u5F0F --verify\uFF09";
10948
+ if (status === "failed")
10949
+ return "\u672A\u901A\u8FC7\uFF1B\u5347\u7EA7\u4ECD\u6309\u5B89\u5168\u68C0\u67E5\u7ED3\u679C\u63D0\u4EA4";
10950
+ return "\u5DF2\u8DF3\u8FC7\uFF08\u9ED8\u8BA4\u4E0D\u6267\u884C lint/typecheck/test/build\uFF09";
10951
+ }
10898
10952
 
10899
10953
  // src/update/updater.ts
10900
10954
  class DefaultStarterUpdater {
@@ -10903,12 +10957,14 @@ class DefaultStarterUpdater {
10903
10957
  #beforeTransactionFinalize;
10904
10958
  #finalizeTransaction;
10905
10959
  #skipInstall;
10960
+ #verifyProject;
10906
10961
  constructor(dependencies = {}) {
10907
10962
  this.#artifacts = dependencies.artifacts ?? new PublishedArtifactProvider;
10908
10963
  this.#execute = dependencies.execute ?? runCommand;
10909
10964
  this.#beforeTransactionFinalize = dependencies.beforeTransactionFinalize;
10910
10965
  this.#finalizeTransaction = dependencies.finalizeTransaction ?? finalizeOwnedTransaction;
10911
10966
  this.#skipInstall = dependencies.skipInstall ?? false;
10967
+ this.#verifyProject = dependencies.verifyProject ?? false;
10912
10968
  }
10913
10969
  async plan(input) {
10914
10970
  return createUpdatePlan(input, this.#artifacts);
@@ -10979,18 +11035,11 @@ class DefaultStarterUpdater {
10979
11035
  const plannedPaths = new Set(result2.plan.files.map(({ path: path2 }) => path2));
10980
11036
  replaceExpectations(result2, verificationSnapshot.filter(({ path: path2 }) => !metadataPaths2.has(path2) && !plannedPaths.has(path2)));
10981
11037
  verificationExpectationsFrozen = true;
10982
- const packageJson = JSON.parse(await readFile24(join29(result2.plan.projectDirectory, "package.json"), "utf8"));
10983
- for (const name of ["lint", "typecheck", "test", "build"]) {
10984
- if (!packageJson.scripts?.[name]) {
10985
- steps.push({ name: `bun run ${name}`, status: "skipped" });
10986
- continue;
10987
- }
10988
- await this.#execute("bun", ["run", name], result2.plan.projectDirectory);
10989
- steps.push({ name: `bun run ${name}`, status: "passed" });
10990
- }
11038
+ const projectQualityVerification = this.#verifyProject ? await runProjectQualityVerification(result2.plan.projectDirectory, this.#execute) : skippedProjectQualityVerification();
11039
+ steps.push(...projectQualityVerification.steps);
10991
11040
  const metadataExpectations = await finalize(result2, stabilized);
10992
11041
  const reportPath = `.windy/reports/${result2.plan.id}.md`;
10993
- const reportExpectation = await writeUpdateReport(result2, steps, reportPath, expectedForPath(result2, reportPath));
11042
+ const reportExpectation = await writeUpdateReport(result2, steps, projectQualityVerification.status, reportPath, expectedForPath(result2, reportPath));
10994
11043
  replaceExpectations(result2, [...metadataExpectations, reportExpectation]);
10995
11044
  await rm8(result2.plan.temporaryRoot, { recursive: true, force: true });
10996
11045
  await this.#beforeTransactionFinalize?.({ result: result2, reportPath });
@@ -11000,7 +11049,13 @@ class DefaultStarterUpdater {
11000
11049
  expectations: result2.finalizationExpectations,
11001
11050
  workspaceMembershipExpectations: result2.plan.finalWorkspaceMembershipExpectations ?? result2.plan.workspaceMembershipExpectations
11002
11051
  });
11003
- return { updateId: result2.plan.id, status: "passed", steps, reportPath };
11052
+ return {
11053
+ updateId: result2.plan.id,
11054
+ status: "passed",
11055
+ projectQualityVerification: projectQualityVerification.status,
11056
+ steps,
11057
+ reportPath
11058
+ };
11004
11059
  } catch (error) {
11005
11060
  await rm8(result2.plan.temporaryRoot, { recursive: true, force: true });
11006
11061
  if (error instanceof TransactionCommittedCleanupPendingError) {
@@ -11013,7 +11068,7 @@ class DefaultStarterUpdater {
11013
11068
  if (verificationExpectationsFrozen) {
11014
11069
  const conflicts = await retainOwnedTransactionExpectationConflicts(result2.plan.projectDirectory, result2.transactionOwnerToken, result2.finalizationExpectations);
11015
11070
  if (conflicts.length) {
11016
- throw new Error(`\u66F4\u65B0\u9A8C\u8BC1\u671F\u95F4\u68C0\u6D4B\u5230\u5E76\u53D1\u4FEE\u6539\uFF0C\u4E8B\u52A1\u73B0\u573A\u5DF2\u4FDD\u7559\uFF1A${conflicts.join("\u3001")}`, { cause: error });
11071
+ throw new Error(`\u66F4\u65B0\u5B89\u5168\u6536\u53E3\u671F\u95F4\u68C0\u6D4B\u5230\u5E76\u53D1\u4FEE\u6539\uFF0C\u4E8B\u52A1\u73B0\u573A\u5DF2\u4FDD\u7559\uFF1A${conflicts.join("\u3001")}`, { cause: error });
11017
11072
  }
11018
11073
  }
11019
11074
  await restoreUpdateState(result2.plan.projectDirectory, {
@@ -11025,7 +11080,7 @@ class DefaultStarterUpdater {
11025
11080
  phase: "ready",
11026
11081
  manifestHash: result2.expectedManifestHash
11027
11082
  });
11028
- throw new Error(`\u66F4\u65B0\u9A8C\u8BC1\u5931\u8D25\uFF0C\u9879\u76EE\u5DF2\u56DE\u6EDA\u5230 ${result2.plan.sourceVersion}\uFF1A${error instanceof Error ? error.message : String(error)}`, { cause: error });
11083
+ throw new Error(`\u66F4\u65B0\u5B89\u5168\u68C0\u67E5\u5931\u8D25\uFF0C\u9879\u76EE\u5DF2\u56DE\u6EDA\u5230 ${result2.plan.sourceVersion}\uFF1A${error instanceof Error ? error.message : String(error)}`, { cause: error });
11029
11084
  }
11030
11085
  }
11031
11086
  }
@@ -11671,6 +11726,20 @@ function actionLabel(action) {
11671
11726
  return action === "add" ? "\u5B89\u88C5" : action === "update" ? "\u66F4\u65B0" : "\u5378\u8F7D";
11672
11727
  }
11673
11728
 
11729
+ // src/update/cli-quality-output.ts
11730
+ function printProjectQualityVerification(status) {
11731
+ if (status === "passed") {
11732
+ console.log("\u9879\u76EE\u8D28\u91CF\u9A8C\u8BC1\uFF1A\u5DF2\u901A\u8FC7\uFF08\u663E\u5F0F --verify\uFF09");
11733
+ return;
11734
+ }
11735
+ if (status === "failed") {
11736
+ console.error("\u9879\u76EE\u8D28\u91CF\u9A8C\u8BC1\uFF1A\u672A\u901A\u8FC7\uFF1B\u5347\u7EA7\u5DF2\u5B89\u5168\u63D0\u4EA4\uFF0C\u8BF7\u67E5\u770B\u4E0A\u65B9\u5931\u8D25\u547D\u4EE4\u548C\u66F4\u65B0\u62A5\u544A");
11737
+ process.exitCode = 1;
11738
+ return;
11739
+ }
11740
+ console.log("\u9879\u76EE\u8D28\u91CF\u9A8C\u8BC1\uFF1A\u5DF2\u8DF3\u8FC7\uFF1B\u53EF\u6309\u9700\u663E\u5F0F\u8FD0\u884C lint\u3001typecheck\u3001test \u548C build");
11741
+ }
11742
+
11674
11743
  // src/update/cli.ts
11675
11744
  async function runLifecycleCommand(args, dependencies = {}) {
11676
11745
  const command = args[0];
@@ -11745,6 +11814,7 @@ var updateHelpText = `\u7528\u6CD5\uFF1Acreate-windy update [\u9009\u9879]
11745
11814
  --to <\u7248\u672C> \u66F4\u65B0\u5230\u6307\u5B9A\u7248\u672C
11746
11815
  --cwd <\u76EE\u5F55> \u6307\u5B9A\u9879\u76EE\u76EE\u5F55
11747
11816
  --skip-install \u66F4\u65B0\u65F6\u4E0D\u6267\u884C bun install
11817
+ --verify \u663E\u5F0F\u6267\u884C\u9879\u76EE lint/typecheck/test/build
11748
11818
  --force \u5F3A\u5236\u91C7\u7528\u5E73\u53F0\u57FA\u7EBF\u53C2\u4E0E\u4E09\u65B9\u5408\u5E76
11749
11819
  -h, --help \u663E\u793A update \u5E2E\u52A9\u5E76\u9000\u51FA
11750
11820
  `;
@@ -11777,7 +11847,8 @@ async function runUpdate(args, projectDirectory) {
11777
11847
  return;
11778
11848
  }
11779
11849
  const updater = new DefaultStarterUpdater({
11780
- skipInstall: args.includes("--skip-install")
11850
+ skipInstall: args.includes("--skip-install"),
11851
+ verifyProject: args.includes("--verify")
11781
11852
  });
11782
11853
  const plan = await updater.plan({
11783
11854
  projectDirectory,
@@ -11800,6 +11871,7 @@ async function runUpdate(args, projectDirectory) {
11800
11871
  const environmentRepair = await repairLocalEnvironment(projectDirectory);
11801
11872
  console.log(`
11802
11873
  \u66F4\u65B0\u5B8C\u6210\uFF1A${plan.sourceVersion} \u2192 ${plan.targetVersion}`);
11874
+ printProjectQualityVerification(report.projectQualityVerification);
11803
11875
  console.log(`\u62A5\u544A\uFF1A${report.reportPath}`);
11804
11876
  printEnvironmentRepair(environmentRepair);
11805
11877
  if (environmentRepair.missingKeys.length || environmentRepair.duplicateKeys.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-windy",
3
- "version": "0.6.18",
3
+ "version": "0.6.19",
4
4
  "description": "创建单组织、单租户、私有部署优先的 Windy 企业 Dashboard Starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "templateCommit": "517a96ca3489632c4083fe08921935bb3f4b1781",
4
- "generatorVersion": "0.6.18",
3
+ "templateCommit": "f2e5ced778480dc66e9dd03dd79023b9eda766a1",
4
+ "generatorVersion": "0.6.19",
5
5
  "modules": [
6
6
  {
7
7
  "name": "system",
@@ -160,6 +160,8 @@ bun run build
160
160
  bun run windy doctor
161
161
  bun run windy update --check
162
162
  bun run windy update --dry-run
163
+ bun run windy update
164
+ # 如需在本次更新中显式执行项目质量脚本:bun run windy update --verify
163
165
  bun run windy module list
164
166
  # 当前组合没有可安全 runtime disable 的已安装可选模块
165
167
  bun run lint
@@ -170,11 +172,12 @@ bun run format
170
172
 
171
173
  更新前无需提交、暂存或清理业务改动。`--dry-run` 只生成计划,不写项目;无关 WIP
172
174
  保持原样,命中同一路径时按冻结快照与三方合并 fail closed。正式更新先备份,再做
173
- package 字段级合并、共享脚手架三方合并与完整验证。整个 `apps/web/src` 和
175
+ package 字段级合并、共享脚手架三方合并与安全收口;默认不执行项目 lint、typecheck、
176
+ test 或 build,传入 `--verify` 才显式执行并记录结果。整个 `apps/web/src` 和
174
177
  `apps/server/src/app` 默认保持项目版本;已登记版本 recipe 只修改其声明的窄锚点。
175
178
  任何冲突都会在写入前中止,模块启停在交互
176
179
  终端确认;CI、Agent 或其它非 TTY 环境必须显式传入 `--yes`。
177
- 无并发变化的验证失败会自动回滚;中断恢复使用
180
+ 安全检查失败会自动回滚;项目质量脚本失败本身不回滚已安全应用的更新。中断恢复使用
178
181
  `bun run windy doctor --repair`。该命令在提交点前保留并发编辑并回滚其余路径,
179
182
  在 committed 提交点后只完成事务状态与备份清理,不回滚项目文件。
180
183
 
@@ -15,6 +15,7 @@ uv run mypy
15
15
 
16
16
  HTTP 接口:
17
17
 
18
+ - `GET /api/agent/runtime/compatibility`(内部认证、V1 协议 Offer)
18
19
  - `POST /api/agent/runs`
19
20
  - `GET /api/agent/runs/:runId/events`
20
21
  - `GET /api/agent/runs/:runId/result`
@@ -24,6 +25,11 @@ Run 创建使用 `Idempotency-Key`。事件以 SSE 传输,支持 `Last-Event-I
24
25
  断线重放,并周期发送 comment heartbeat。Provider 或 Engine 异常只映射为 canonical
25
26
  安全错误,不把原始异常正文暴露给调用方。
26
27
 
28
+ 兼容 Offer 返回 Engine identity、支持的协议版本、传输能力、callback auth profile 及三层摘要。
29
+ 新 Windy Server 会在 Run 请求头固定所选版本和 Offer digest;新 Agent Server 仍接受完全不带 pin
30
+ 的旧调用方,但拒绝半套或不匹配 pin。它只用于同一受控部署,不能把内部 token 发给外部业务系统。
31
+ 跨系统限制见 [Remote Agent Runtime 兼容契约](../../docs/architecture/agent-runtime-compatibility.md)。
32
+
27
33
  配置以下仅服务端可见的环境变量后启用 Direct Engine:
28
34
 
29
35
  ```bash
@@ -34,6 +34,12 @@ from .http_helpers import (
34
34
  )
35
35
  from .report_routes import create_report_router
36
36
  from .report_store import AgentRuntimeReportStore
37
+ from .runtime_compatibility import (
38
+ OFFER_DIGEST_HEADER,
39
+ PROTOCOL_HEADER,
40
+ RuntimeCompatibilityError,
41
+ validate_runtime_compatibility_pin,
42
+ )
37
43
  from .runtime_factory import (
38
44
  default_engine,
39
45
  default_report_store,
@@ -191,11 +197,28 @@ def create_app(
191
197
  request: StartRunRequest,
192
198
  idempotency_key: Annotated[str, Header(alias="Idempotency-Key")],
193
199
  authorization: Annotated[str | None, Header()] = None,
200
+ runtime_protocol: Annotated[str | None, Header(alias=PROTOCOL_HEADER)] = None,
201
+ runtime_offer_digest: Annotated[
202
+ str | None, Header(alias=OFFER_DIGEST_HEADER)
203
+ ] = None,
194
204
  ) -> StartRunResponse | JSONResponse:
195
205
  if not authorized(authorization, internal_token):
196
206
  return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
197
207
  if not idempotency_key.strip():
198
208
  return safe_error(400, "INVALID_INPUT", "幂等键不能为空", False)
209
+ try:
210
+ validate_runtime_compatibility_pin(
211
+ runtime_protocol,
212
+ runtime_offer_digest,
213
+ runtime_engine.engine_id,
214
+ )
215
+ except RuntimeCompatibilityError:
216
+ return safe_error(
217
+ 409,
218
+ "RUNTIME_UNAVAILABLE",
219
+ "Agent Runtime compatibility pin 不匹配",
220
+ False,
221
+ )
199
222
  try:
200
223
  await require_vision_readiness(runtime_engine, request)
201
224
  except SafeEngineError as error:
@@ -7,6 +7,7 @@ from fastapi import FastAPI, Header
7
7
  from fastapi.responses import JSONResponse
8
8
 
9
9
  from .http_helpers import authorized, safe_error
10
+ from .runtime_compatibility import COMPATIBILITY_PATH, runtime_compatibility_offer
10
11
  from .service import AgentRunService
11
12
  from .vlm_readiness import unavailable_vlm_snapshot
12
13
 
@@ -82,3 +83,11 @@ def install_health_routes(
82
83
  "Agent Runtime Model coordinate 不可用",
83
84
  True,
84
85
  )
86
+
87
+ @application.get(COMPATIBILITY_PATH, response_model=None)
88
+ async def runtime_compatibility(
89
+ authorization: Annotated[str | None, Header()] = None,
90
+ ) -> dict[str, object] | JSONResponse:
91
+ if not authorized(authorization, internal_token):
92
+ return safe_error(403, "FORBIDDEN", "内部调用认证失败", False)
93
+ return runtime_compatibility_offer(runtime.engine.engine_id)