@freelog-cli/cli2 0.5.3 → 0.5.4

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/dist/bin/index.js CHANGED
@@ -9168,11 +9168,11 @@ var require_mime_types = __commonJS({
9168
9168
  }
9169
9169
  return exts[0];
9170
9170
  }
9171
- function lookup(path19) {
9172
- if (!path19 || typeof path19 !== "string") {
9171
+ function lookup(path20) {
9172
+ if (!path20 || typeof path20 !== "string") {
9173
9173
  return false;
9174
9174
  }
9175
- var extension2 = extname("x." + path19).toLowerCase().substr(1);
9175
+ var extension2 = extname("x." + path20).toLowerCase().substr(1);
9176
9176
  if (!extension2) {
9177
9177
  return false;
9178
9178
  }
@@ -10287,7 +10287,7 @@ var require_form_data = __commonJS({
10287
10287
  "use strict";
10288
10288
  var CombinedStream = require_combined_stream();
10289
10289
  var util4 = __require("util");
10290
- var path19 = __require("path");
10290
+ var path20 = __require("path");
10291
10291
  var http3 = __require("http");
10292
10292
  var https2 = __require("https");
10293
10293
  var parseUrl2 = __require("url").parse;
@@ -10418,11 +10418,11 @@ var require_form_data = __commonJS({
10418
10418
  FormData3.prototype._getContentDisposition = function(value, options) {
10419
10419
  var filename;
10420
10420
  if (typeof options.filepath === "string") {
10421
- filename = path19.normalize(options.filepath).replace(/\\/g, "/");
10421
+ filename = path20.normalize(options.filepath).replace(/\\/g, "/");
10422
10422
  } else if (options.filename || value && (value.name || value.path)) {
10423
- filename = path19.basename(options.filename || value && (value.name || value.path));
10423
+ filename = path20.basename(options.filename || value && (value.name || value.path));
10424
10424
  } else if (value && value.readable && hasOwn(value, "httpVersion")) {
10425
- filename = path19.basename(value.client._httpMessage.path || "");
10425
+ filename = path20.basename(value.client._httpMessage.path || "");
10426
10426
  }
10427
10427
  if (filename) {
10428
10428
  return 'filename="' + escapeHeaderParam(filename) + '"';
@@ -13339,9 +13339,9 @@ function isVisitable(thing) {
13339
13339
  function removeBrackets(key) {
13340
13340
  return utils_default.endsWith(key, "[]") ? key.slice(0, -2) : key;
13341
13341
  }
13342
- function renderKey(path19, key, dots) {
13343
- if (!path19) return key;
13344
- return path19.concat(key).map(function each(token, i) {
13342
+ function renderKey(path20, key, dots) {
13343
+ if (!path20) return key;
13344
+ return path20.concat(key).map(function each(token, i) {
13345
13345
  token = removeBrackets(token);
13346
13346
  return !dots && i ? "[" + token + "]" : token;
13347
13347
  }).join(dots ? "." : "");
@@ -13427,13 +13427,13 @@ function toFormData(obj, formData, options) {
13427
13427
  return currentValue;
13428
13428
  });
13429
13429
  }
13430
- function defaultVisitor(value, key, path19) {
13430
+ function defaultVisitor(value, key, path20) {
13431
13431
  let arr = value;
13432
13432
  if (utils_default.isReactNative(formData) && utils_default.isReactNativeBlob(value)) {
13433
- formData.append(renderKey(path19, key, dots), convertValue(value));
13433
+ formData.append(renderKey(path20, key, dots), convertValue(value));
13434
13434
  return false;
13435
13435
  }
13436
- if (value && !path19 && typeof value === "object") {
13436
+ if (value && !path20 && typeof value === "object") {
13437
13437
  if (utils_default.endsWith(key, "{}")) {
13438
13438
  key = metaTokens ? key : key.slice(0, -2);
13439
13439
  value = stringifyWithDepthLimit(value, 1);
@@ -13452,7 +13452,7 @@ function toFormData(obj, formData, options) {
13452
13452
  if (isVisitable(value)) {
13453
13453
  return true;
13454
13454
  }
13455
- formData.append(renderKey(path19, key, dots), convertValue(value));
13455
+ formData.append(renderKey(path20, key, dots), convertValue(value));
13456
13456
  return false;
13457
13457
  }
13458
13458
  const exposedHelpers = Object.assign(predicates, {
@@ -13460,17 +13460,17 @@ function toFormData(obj, formData, options) {
13460
13460
  convertValue,
13461
13461
  isVisitable
13462
13462
  });
13463
- function build(value, path19, depth = 0) {
13463
+ function build(value, path20, depth = 0) {
13464
13464
  if (utils_default.isUndefined(value)) return;
13465
13465
  throwIfMaxDepthExceeded(depth);
13466
13466
  if (stack.indexOf(value) !== -1) {
13467
- throw new Error("Circular reference detected in " + path19.join("."));
13467
+ throw new Error("Circular reference detected in " + path20.join("."));
13468
13468
  }
13469
13469
  stack.push(value);
13470
13470
  utils_default.forEach(value, function each(el, key) {
13471
- const result = !(utils_default.isUndefined(el) || el === null) && visitor.call(formData, el, utils_default.isString(key) ? key.trim() : key, path19, exposedHelpers);
13471
+ const result = !(utils_default.isUndefined(el) || el === null) && visitor.call(formData, el, utils_default.isString(key) ? key.trim() : key, path20, exposedHelpers);
13472
13472
  if (result === true) {
13473
- build(el, path19 ? path19.concat(key) : [key], depth + 1);
13473
+ build(el, path20 ? path20.concat(key) : [key], depth + 1);
13474
13474
  }
13475
13475
  });
13476
13476
  stack.pop();
@@ -13682,7 +13682,7 @@ var platform_default = {
13682
13682
  // ../../node_modules/.pnpm/axios@1.19.0/node_modules/axios/lib/helpers/toURLEncodedForm.js
13683
13683
  function toURLEncodedForm(data, options) {
13684
13684
  return toFormData_default(data, new platform_default.classes.URLSearchParams(), {
13685
- visitor: function(value, key, path19, helpers) {
13685
+ visitor: function(value, key, path20, helpers) {
13686
13686
  if (platform_default.isNode && utils_default.isBuffer(value)) {
13687
13687
  this.append(key, value.toString("base64"));
13688
13688
  return false;
@@ -13704,14 +13704,14 @@ function throwIfDepthExceeded(index) {
13704
13704
  }
13705
13705
  }
13706
13706
  function parsePropPath(name) {
13707
- const path19 = [];
13707
+ const path20 = [];
13708
13708
  const pattern = /[^.[\]]+|\[([^.[\]]*)]/g;
13709
13709
  let match;
13710
13710
  while ((match = pattern.exec(name)) !== null) {
13711
- throwIfDepthExceeded(path19.length);
13712
- path19.push(match[0] === "[]" ? "" : match[1] || match[0]);
13711
+ throwIfDepthExceeded(path20.length);
13712
+ path20.push(match[0] === "[]" ? "" : match[1] || match[0]);
13713
13713
  }
13714
- return path19;
13714
+ return path20;
13715
13715
  }
13716
13716
  function arrayToObject(arr) {
13717
13717
  const obj = {};
@@ -13726,12 +13726,12 @@ function arrayToObject(arr) {
13726
13726
  return obj;
13727
13727
  }
13728
13728
  function formDataToJSON(formData) {
13729
- function buildPath(path19, value, target, index) {
13729
+ function buildPath(path20, value, target, index) {
13730
13730
  throwIfDepthExceeded(index);
13731
- let name = path19[index++];
13731
+ let name = path20[index++];
13732
13732
  if (name === "__proto__") return true;
13733
13733
  const isNumericKey = Number.isFinite(+name);
13734
- const isLast = index >= path19.length;
13734
+ const isLast = index >= path20.length;
13735
13735
  name = !name && utils_default.isArray(target) ? target.length : name;
13736
13736
  if (isLast) {
13737
13737
  if (utils_default.hasOwnProp(target, name)) {
@@ -13744,7 +13744,7 @@ function formDataToJSON(formData) {
13744
13744
  if (!utils_default.hasOwnProp(target, name) || !utils_default.isObject(target[name])) {
13745
13745
  target[name] = [];
13746
13746
  }
13747
- const result = buildPath(path19, value, target[name], index);
13747
+ const result = buildPath(path20, value, target[name], index);
13748
13748
  if (result && utils_default.isArray(target[name])) {
13749
13749
  target[name] = arrayToObject(target[name]);
13750
13750
  }
@@ -15462,9 +15462,9 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
15462
15462
  auth = urlUsername + ":" + urlPassword;
15463
15463
  }
15464
15464
  auth && headers.delete("authorization");
15465
- let path19;
15465
+ let path20;
15466
15466
  try {
15467
- path19 = buildURL(
15467
+ path20 = buildURL(
15468
15468
  parsed.pathname + parsed.search,
15469
15469
  own2("params"),
15470
15470
  own2("paramsSerializer")
@@ -15483,7 +15483,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
15483
15483
  false
15484
15484
  );
15485
15485
  const options = Object.assign(/* @__PURE__ */ Object.create(null), {
15486
- path: path19,
15486
+ path: path20,
15487
15487
  method,
15488
15488
  headers: toByteStringHeaderObject(headers),
15489
15489
  agents: { http: httpAgent, https: httpsAgent },
@@ -15885,14 +15885,14 @@ var isURLSameOrigin_default = platform_default.hasStandardBrowserEnv ? /* @__PUR
15885
15885
  var cookies_default = platform_default.hasStandardBrowserEnv ? (
15886
15886
  // Standard browser envs support document.cookie
15887
15887
  {
15888
- write(name, value, expires, path19, domain, secure, sameSite) {
15888
+ write(name, value, expires, path20, domain, secure, sameSite) {
15889
15889
  if (typeof document === "undefined") return;
15890
15890
  const cookie = [`${name}=${encodeURIComponent(value)}`];
15891
15891
  if (utils_default.isNumber(expires)) {
15892
15892
  cookie.push(`expires=${new Date(expires).toUTCString()}`);
15893
15893
  }
15894
- if (utils_default.isString(path19)) {
15895
- cookie.push(`path=${path19}`);
15894
+ if (utils_default.isString(path20)) {
15895
+ cookie.push(`path=${path20}`);
15896
15896
  }
15897
15897
  if (utils_default.isString(domain)) {
15898
15898
  cookie.push(`domain=${domain}`);
@@ -17786,14 +17786,14 @@ function copy(a, s, t2) {
17786
17786
  if (s[m]) t2[m] = s[m];
17787
17787
  });
17788
17788
  }
17789
- function getLastOfPath(object, path19, Empty) {
17789
+ function getLastOfPath(object, path20, Empty) {
17790
17790
  function cleanKey(key2) {
17791
17791
  return key2 && key2.indexOf("###") > -1 ? key2.replace(/###/g, ".") : key2;
17792
17792
  }
17793
17793
  function canNotTraverseDeeper() {
17794
17794
  return !object || typeof object === "string";
17795
17795
  }
17796
- var stack = typeof path19 !== "string" ? [].concat(path19) : path19.split(".");
17796
+ var stack = typeof path20 !== "string" ? [].concat(path20) : path20.split(".");
17797
17797
  while (stack.length > 1) {
17798
17798
  if (canNotTraverseDeeper()) return {};
17799
17799
  var key = cleanKey(stack.shift());
@@ -17810,18 +17810,18 @@ function getLastOfPath(object, path19, Empty) {
17810
17810
  k: cleanKey(stack.shift())
17811
17811
  };
17812
17812
  }
17813
- function setPath(object, path19, newValue) {
17814
- var _getLastOfPath = getLastOfPath(object, path19, Object), obj = _getLastOfPath.obj, k = _getLastOfPath.k;
17813
+ function setPath(object, path20, newValue) {
17814
+ var _getLastOfPath = getLastOfPath(object, path20, Object), obj = _getLastOfPath.obj, k = _getLastOfPath.k;
17815
17815
  obj[k] = newValue;
17816
17816
  }
17817
- function pushPath(object, path19, newValue, concat) {
17818
- var _getLastOfPath2 = getLastOfPath(object, path19, Object), obj = _getLastOfPath2.obj, k = _getLastOfPath2.k;
17817
+ function pushPath(object, path20, newValue, concat) {
17818
+ var _getLastOfPath2 = getLastOfPath(object, path20, Object), obj = _getLastOfPath2.obj, k = _getLastOfPath2.k;
17819
17819
  obj[k] = obj[k] || [];
17820
17820
  if (concat) obj[k] = obj[k].concat(newValue);
17821
17821
  if (!concat) obj[k].push(newValue);
17822
17822
  }
17823
- function getPath(object, path19) {
17824
- var _getLastOfPath3 = getLastOfPath(object, path19), obj = _getLastOfPath3.obj, k = _getLastOfPath3.k;
17823
+ function getPath(object, path20) {
17824
+ var _getLastOfPath3 = getLastOfPath(object, path20), obj = _getLastOfPath3.obj, k = _getLastOfPath3.k;
17825
17825
  if (!obj) return void 0;
17826
17826
  return obj[k];
17827
17827
  }
@@ -17943,11 +17943,11 @@ function _isNativeReflectConstruct() {
17943
17943
  return false;
17944
17944
  }
17945
17945
  }
17946
- function deepFind(obj, path19) {
17946
+ function deepFind(obj, path20) {
17947
17947
  var keySeparator = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : ".";
17948
17948
  if (!obj) return void 0;
17949
- if (obj[path19]) return obj[path19];
17950
- var paths = path19.split(keySeparator);
17949
+ if (obj[path20]) return obj[path20];
17950
+ var paths = path20.split(keySeparator);
17951
17951
  var current = obj;
17952
17952
  for (var i = 0; i < paths.length; ++i) {
17953
17953
  if (!current) return void 0;
@@ -17965,7 +17965,7 @@ function deepFind(obj, path19) {
17965
17965
  }
17966
17966
  if (mix === void 0) return void 0;
17967
17967
  if (mix === null) return null;
17968
- if (path19.endsWith(p)) {
17968
+ if (path20.endsWith(p)) {
17969
17969
  if (typeof mix === "string") return mix;
17970
17970
  if (p && typeof mix[p] === "string") return mix[p];
17971
17971
  }
@@ -18022,13 +18022,13 @@ var ResourceStore = (function(_EventEmitter) {
18022
18022
  var options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {};
18023
18023
  var keySeparator = options.keySeparator !== void 0 ? options.keySeparator : this.options.keySeparator;
18024
18024
  var ignoreJSONStructure = options.ignoreJSONStructure !== void 0 ? options.ignoreJSONStructure : this.options.ignoreJSONStructure;
18025
- var path19 = [lng, ns];
18026
- if (key && typeof key !== "string") path19 = path19.concat(key);
18027
- if (key && typeof key === "string") path19 = path19.concat(keySeparator ? key.split(keySeparator) : key);
18025
+ var path20 = [lng, ns];
18026
+ if (key && typeof key !== "string") path20 = path20.concat(key);
18027
+ if (key && typeof key === "string") path20 = path20.concat(keySeparator ? key.split(keySeparator) : key);
18028
18028
  if (lng.indexOf(".") > -1) {
18029
- path19 = lng.split(".");
18029
+ path20 = lng.split(".");
18030
18030
  }
18031
- var result = getPath(this.data, path19);
18031
+ var result = getPath(this.data, path20);
18032
18032
  if (result || !ignoreJSONStructure || typeof key !== "string") return result;
18033
18033
  return deepFind(this.data && this.data[lng] && this.data[lng][ns], key, keySeparator);
18034
18034
  }
@@ -18040,15 +18040,15 @@ var ResourceStore = (function(_EventEmitter) {
18040
18040
  };
18041
18041
  var keySeparator = this.options.keySeparator;
18042
18042
  if (keySeparator === void 0) keySeparator = ".";
18043
- var path19 = [lng, ns];
18044
- if (key) path19 = path19.concat(keySeparator ? key.split(keySeparator) : key);
18043
+ var path20 = [lng, ns];
18044
+ if (key) path20 = path20.concat(keySeparator ? key.split(keySeparator) : key);
18045
18045
  if (lng.indexOf(".") > -1) {
18046
- path19 = lng.split(".");
18046
+ path20 = lng.split(".");
18047
18047
  value = ns;
18048
- ns = path19[1];
18048
+ ns = path20[1];
18049
18049
  }
18050
18050
  this.addNamespaces(ns);
18051
- setPath(this.data, path19, value);
18051
+ setPath(this.data, path20, value);
18052
18052
  if (!options.silent) this.emit("added", lng, ns, key, value);
18053
18053
  }
18054
18054
  }, {
@@ -18070,21 +18070,21 @@ var ResourceStore = (function(_EventEmitter) {
18070
18070
  var options = arguments.length > 5 && arguments[5] !== void 0 ? arguments[5] : {
18071
18071
  silent: false
18072
18072
  };
18073
- var path19 = [lng, ns];
18073
+ var path20 = [lng, ns];
18074
18074
  if (lng.indexOf(".") > -1) {
18075
- path19 = lng.split(".");
18075
+ path20 = lng.split(".");
18076
18076
  deep = resources;
18077
18077
  resources = ns;
18078
- ns = path19[1];
18078
+ ns = path20[1];
18079
18079
  }
18080
18080
  this.addNamespaces(ns);
18081
- var pack = getPath(this.data, path19) || {};
18081
+ var pack = getPath(this.data, path20) || {};
18082
18082
  if (deep) {
18083
18083
  deepExtend(pack, resources, overwrite);
18084
18084
  } else {
18085
18085
  pack = _objectSpread$1(_objectSpread$1({}, pack), resources);
18086
18086
  }
18087
- setPath(this.data, path19, pack);
18087
+ setPath(this.data, path20, pack);
18088
18088
  if (!options.silent) this.emit("added", lng, ns, resources);
18089
18089
  }
18090
18090
  }, {
@@ -19078,10 +19078,10 @@ var Interpolator = (function() {
19078
19078
  }
19079
19079
  var handleFormat = function handleFormat2(key) {
19080
19080
  if (key.indexOf(_this.formatSeparator) < 0) {
19081
- var path19 = getPathWithDefaults(data, defaultData, key);
19082
- return _this.alwaysFormat ? _this.format(path19, void 0, lng, _objectSpread$3(_objectSpread$3(_objectSpread$3({}, options), data), {}, {
19081
+ var path20 = getPathWithDefaults(data, defaultData, key);
19082
+ return _this.alwaysFormat ? _this.format(path20, void 0, lng, _objectSpread$3(_objectSpread$3(_objectSpread$3({}, options), data), {}, {
19083
19083
  interpolationkey: key
19084
- })) : path19;
19084
+ })) : path20;
19085
19085
  }
19086
19086
  var p = key.split(_this.formatSeparator);
19087
19087
  var k = p.shift().trim();
@@ -23985,7 +23985,7 @@ function addSharedOptions(command) {
23985
23985
  "--json",
23986
23986
  // i18n: cli.flag.json
23987
23987
  "\u4EE5 JSON \u8F93\u51FA"
23988
- ).option("--resource <selector>", "\u9009\u62E9\u672C\u5730\u8D44\u6E90\uFF08id:\u3001name:\u3001title:\u3001artifact: \u6216 file:N.json\uFF09");
23988
+ ).option("--resource <selector>", "\u9009\u62E9\u672C\u5730\u8D44\u6E90\uFF08id:\u3001name:\u3001artifact: \u6216 file:N.json\uFF09");
23989
23989
  }
23990
23990
  function readSharedOptions(command) {
23991
23991
  const raw = command.optsWithGlobals();
@@ -24273,28 +24273,47 @@ function sha256(content) {
24273
24273
  function readText(filePath) {
24274
24274
  return existsSync3(filePath) ? readFileSync2(filePath, "utf8") : null;
24275
24275
  }
24276
- function validateTransaction(raw) {
24276
+ function resolveTransactionTarget(cwd, entryPath, allowLegacyIndex = false) {
24277
+ const root = path4.resolve(freelogDir(cwd));
24278
+ const allowed = allowLegacyIndex && entryPath === "index.json" || entryPath === ".sequence" || entryPath === ".pending-operation.json" || /^[1-9]\d*(?:\.version)?\.json$/.test(entryPath);
24279
+ if (!allowed || path4.isAbsolute(entryPath) || entryPath.includes("/") || entryPath.includes("\\")) {
24280
+ throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u5305\u542B\u4E0D\u5141\u8BB8\u7684\u76EE\u6807", "LOCAL_TXN_INVALID");
24281
+ }
24282
+ const target = path4.resolve(root, entryPath);
24283
+ if (path4.dirname(target) !== root) {
24284
+ throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u5305\u542B\u8D8A\u754C\u76EE\u6807", "LOCAL_TXN_INVALID");
24285
+ }
24286
+ return target;
24287
+ }
24288
+ function validateTransaction(cwd, raw) {
24277
24289
  const value = raw;
24278
24290
  if (value?.schemaVersion !== 1 || !Array.isArray(value.entries) || value.entries.length === 0) {
24279
24291
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u65E0\u6548\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "LOCAL_TXN_INVALID");
24280
24292
  }
24293
+ const targets = /* @__PURE__ */ new Set();
24281
24294
  for (const entry2 of value.entries) {
24282
24295
  if (!entry2 || typeof entry2.path !== "string" || entry2.before !== null && typeof entry2.before !== "string" || entry2.after !== null && typeof entry2.after !== "string" || entry2.beforeSha256 !== sha256(entry2.before) || entry2.afterSha256 !== sha256(entry2.after)) {
24283
24296
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "LOCAL_TXN_INVALID");
24284
24297
  }
24298
+ const target = resolveTransactionTarget(cwd, entry2.path, true);
24299
+ if (targets.has(target)) {
24300
+ throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u5305\u542B\u91CD\u590D\u76EE\u6807", "LOCAL_TXN_INVALID");
24301
+ }
24302
+ targets.add(target);
24285
24303
  }
24286
24304
  return value;
24287
24305
  }
24288
- function applyEntry(entry2) {
24289
- const current = readText(entry2.path);
24306
+ function applyEntry(cwd, entry2) {
24307
+ const target = resolveTransactionTarget(cwd, entry2.path, true);
24308
+ const current = readText(target);
24290
24309
  if (current === entry2.after) return;
24291
24310
  if (current !== entry2.before) {
24292
- throw new CliError(`\u672C\u5730\u4E8B\u52A1\u4E0E ${path4.basename(entry2.path)} \u51B2\u7A81\uFF0C\u62D2\u7EDD\u731C\u6D4B\u8986\u76D6`, "LOCAL_TXN_CONFLICT");
24311
+ throw new CliError(`\u672C\u5730\u4E8B\u52A1\u4E0E ${entry2.path} \u51B2\u7A81\uFF0C\u62D2\u7EDD\u731C\u6D4B\u8986\u76D6`, "LOCAL_TXN_CONFLICT");
24293
24312
  }
24294
24313
  if (entry2.after === null) {
24295
- if (existsSync3(entry2.path)) unlinkSync2(entry2.path);
24314
+ if (existsSync3(target)) unlinkSync2(target);
24296
24315
  } else {
24297
- atomicWriteFile(entry2.path, entry2.after);
24316
+ atomicWriteFile(target, entry2.after);
24298
24317
  }
24299
24318
  }
24300
24319
  function recoverPendingTransaction(cwd) {
@@ -24306,23 +24325,25 @@ function recoverPendingTransaction(cwd) {
24306
24325
  } catch {
24307
24326
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u65E0\u6CD5\u89E3\u6790\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "LOCAL_TXN_INVALID");
24308
24327
  }
24309
- const transaction2 = validateTransaction(raw);
24310
- for (const entry2 of transaction2.entries) applyEntry(entry2);
24328
+ const transaction2 = validateTransaction(cwd, raw);
24329
+ for (const entry2 of transaction2.entries) applyEntry(cwd, entry2);
24311
24330
  unlinkSync2(txnPath);
24312
24331
  }
24313
24332
  function commitLocalTransaction(cwd, changes) {
24314
24333
  if (changes.length === 0) return;
24315
- const root = `${path4.resolve(freelogDir(cwd))}${path4.sep}`;
24334
+ const root = path4.resolve(freelogDir(cwd));
24316
24335
  const paths = /* @__PURE__ */ new Set();
24317
24336
  const entries = changes.map((change) => {
24318
24337
  const target = path4.resolve(change.path);
24319
- if (!target.startsWith(root) || paths.has(target)) {
24338
+ if (path4.dirname(target) !== root || paths.has(target)) {
24320
24339
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u76EE\u6807\u65E0\u6548", "LOCAL_TXN_INVALID");
24321
24340
  }
24341
+ const relativePath = path4.basename(target);
24342
+ resolveTransactionTarget(cwd, relativePath);
24322
24343
  paths.add(target);
24323
24344
  const before = readText(target);
24324
24345
  return {
24325
- path: target,
24346
+ path: relativePath,
24326
24347
  before,
24327
24348
  beforeSha256: sha256(before),
24328
24349
  after: change.content,
@@ -24332,7 +24353,7 @@ function commitLocalTransaction(cwd, changes) {
24332
24353
  if (entries.length === 0) return;
24333
24354
  atomicWriteFile(transactionFilePath(cwd), `${JSON.stringify({ schemaVersion: 1, entries }, null, 2)}
24334
24355
  `);
24335
- for (const entry2 of entries) applyEntry(entry2);
24356
+ for (const entry2 of entries) applyEntry(cwd, entry2);
24336
24357
  unlinkSync2(transactionFilePath(cwd));
24337
24358
  }
24338
24359
 
@@ -24456,6 +24477,7 @@ var IDENTITY_FILE_RE = /^([1-9]\d*)\.json$/;
24456
24477
  var inputFields = {
24457
24478
  subject: z2.literal("resource"),
24458
24479
  resourceId: z2.string().min(1).optional(),
24480
+ resourceName: z2.string().min(1).optional(),
24459
24481
  name: z2.string().min(1).optional(),
24460
24482
  title: z2.string().min(1).optional(),
24461
24483
  typeCode: z2.string().min(1),
@@ -24469,9 +24491,10 @@ var storedSchema = z2.object({
24469
24491
  ...inputFields
24470
24492
  }).strict().superRefine((identity, ctx) => {
24471
24493
  const hasResourceId = identity.resourceId !== void 0;
24494
+ const hasResourceName = identity.resourceName !== void 0;
24472
24495
  const hasName = identity.name !== void 0;
24473
24496
  const hasTitle = identity.title !== void 0;
24474
- const bound = hasResourceId || hasName || hasTitle;
24497
+ const bound = hasResourceId || hasResourceName || hasName || hasTitle;
24475
24498
  if (hasResourceId !== hasName) {
24476
24499
  ctx.addIssue({
24477
24500
  code: ZodIssueCode.custom,
@@ -24479,8 +24502,11 @@ var storedSchema = z2.object({
24479
24502
  message: "\u7ED1\u5B9A\u8EAB\u4EFD\u5FC5\u987B\u540C\u65F6\u5305\u542B resourceId \u548C name"
24480
24503
  });
24481
24504
  }
24482
- if (!hasResourceId && !hasName && hasTitle) {
24483
- ctx.addIssue({ code: ZodIssueCode.custom, path: ["title"], message: "\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u4E0D\u80FD\u5199\u5165 title" });
24505
+ if (hasResourceName && (!hasResourceId || !hasName)) {
24506
+ ctx.addIssue({ code: ZodIssueCode.custom, path: ["resourceName"], message: "\u5B8C\u6574\u8D44\u6E90\u6807\u8BC6\u53EA\u80FD\u5C5E\u4E8E\u5DF2\u7ED1\u5B9A\u8EAB\u4EFD" });
24507
+ }
24508
+ if (!hasResourceId && !hasName && (hasTitle || hasResourceName)) {
24509
+ ctx.addIssue({ code: ZodIssueCode.custom, path: [hasTitle ? "title" : "resourceName"], message: "\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u4E0D\u80FD\u5199\u5165\u7ED1\u5B9A\u7F13\u5B58" });
24484
24510
  }
24485
24511
  if (!bound && identity.env !== void 0) {
24486
24512
  ctx.addIssue({
@@ -24496,6 +24522,9 @@ function freelogDir(cwd) {
24496
24522
  function identityFilePath(cwd, n) {
24497
24523
  return path6.join(freelogDir(cwd), `${n}.json`);
24498
24524
  }
24525
+ function identitySequenceFilePath(cwd) {
24526
+ return path6.join(freelogDir(cwd), ".sequence");
24527
+ }
24499
24528
  function assertIdentityNumber(n) {
24500
24529
  if (!Number.isInteger(n) || n < 1) {
24501
24530
  throw new CliError("\u8EAB\u4EFD\u7F16\u53F7\u65E0\u6548", "IDENTITY_INVALID");
@@ -24516,7 +24545,7 @@ function throwZodAsCliError(error2) {
24516
24545
  if (field === "subject") {
24517
24546
  throw new CliError("\u672C\u671F\u53EA\u652F\u6301\u5355\u8D44\u6E90", "IDENTITY_SUBJECT_UNSUPPORTED");
24518
24547
  }
24519
- if (field === "name" || field === "resourceId" || field === "title") {
24548
+ if (field === "name" || field === "resourceName" || field === "resourceId" || field === "title") {
24520
24549
  throw new CliError("\u7ED1\u5B9A\u8EAB\u4EFD\u5B57\u6BB5\u4E0D\u5B8C\u6574", "IDENTITY_BINDING_INVALID");
24521
24550
  }
24522
24551
  if (field === "filePath") {
@@ -24529,6 +24558,7 @@ function toStored(data) {
24529
24558
  schemaVersion: 1,
24530
24559
  subject: data.subject,
24531
24560
  ...data.resourceId ? { resourceId: data.resourceId } : {},
24561
+ ...data.resourceName ? { resourceName: data.resourceName } : {},
24532
24562
  ...data.name ? { name: data.name } : {},
24533
24563
  ...data.title ? { title: data.title } : {},
24534
24564
  typeCode: data.typeCode,
@@ -24548,6 +24578,7 @@ function toDiskObject(identity) {
24548
24578
  schemaVersion: 1,
24549
24579
  subject: identity.subject,
24550
24580
  ...identity.resourceId ? { resourceId: identity.resourceId } : {},
24581
+ ...identity.resourceName ? { resourceName: identity.resourceName } : {},
24551
24582
  ...identity.name ? { name: identity.name } : {},
24552
24583
  ...identity.title ? { title: identity.title } : {},
24553
24584
  typeCode: identity.typeCode,
@@ -24575,6 +24606,7 @@ function prepareIdentityUpdate(cwd, n, patch) {
24575
24606
  const identity = normalize({
24576
24607
  subject: parsed.subject ?? current.subject,
24577
24608
  resourceId: parsed.resourceId ?? current.resourceId,
24609
+ resourceName: parsed.resourceName ?? current.resourceName,
24578
24610
  name: parsed.name ?? current.name,
24579
24611
  title: parsed.title ?? current.title,
24580
24612
  typeCode: parsed.typeCode ?? current.typeCode,
@@ -24585,7 +24617,23 @@ function prepareIdentityUpdate(cwd, n, patch) {
24585
24617
  }
24586
24618
  function prepareIdentityCreate(cwd, input2) {
24587
24619
  const numbers = listIdentityNumbers(cwd);
24588
- return { n: numbers.length === 0 ? 1 : Math.max(...numbers) + 1, ...parseCreateInput(input2) };
24620
+ const highestOnDisk = numbers.length === 0 ? 0 : Math.max(...numbers);
24621
+ const sequencePath = identitySequenceFilePath(cwd);
24622
+ let highestAllocated = 0;
24623
+ if (existsSync5(sequencePath)) {
24624
+ const raw = readFileSync4(sequencePath, "utf8").trim();
24625
+ const parsed = Number(raw);
24626
+ if (!/^[1-9]\d*$/.test(raw) || !Number.isSafeInteger(parsed)) {
24627
+ throw new CliError(".freelog/.sequence \u65E0\u6548\uFF0C\u8BF7\u5148\u5907\u4EFD\u540E\u6062\u590D", "IDENTITY_SEQUENCE_INVALID");
24628
+ }
24629
+ highestAllocated = parsed;
24630
+ }
24631
+ return { n: Math.max(highestOnDisk, highestAllocated) + 1, ...parseCreateInput(input2) };
24632
+ }
24633
+ function serializeIdentitySequence(n) {
24634
+ assertIdentityNumber(n);
24635
+ return `${n}
24636
+ `;
24589
24637
  }
24590
24638
  function parsePatchInput(input2) {
24591
24639
  const parsed = patchSchema.safeParse(input2);
@@ -24631,7 +24679,10 @@ function listIdentities(cwd) {
24631
24679
  function createIdentity(cwd, input2) {
24632
24680
  return withProjectLock(cwd, () => {
24633
24681
  const identity = prepareIdentityCreate(cwd, input2);
24634
- writeIdentityFile(cwd, identity.n, identity);
24682
+ commitLocalTransaction(cwd, [
24683
+ { path: identityFilePath(cwd, identity.n), content: serializeIdentity(identity) },
24684
+ { path: identitySequenceFilePath(cwd), content: serializeIdentitySequence(identity.n) }
24685
+ ]);
24635
24686
  return identity;
24636
24687
  }, "create-identity");
24637
24688
  }
@@ -24644,7 +24695,7 @@ function updateIdentity(cwd, n, patch) {
24644
24695
  }
24645
24696
 
24646
24697
  // src/local/resolve.ts
24647
- import { existsSync as existsSync7, readdirSync as readdirSync2 } from "fs";
24698
+ import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
24648
24699
 
24649
24700
  // src/local/draft.ts
24650
24701
  import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync4 } from "fs";
@@ -24765,6 +24816,7 @@ function draftSummary(draft) {
24765
24816
 
24766
24817
  // src/local/projectPath.ts
24767
24818
  import path8 from "path";
24819
+ import { existsSync as existsSync7, realpathSync } from "fs";
24768
24820
  var defaultError = {
24769
24821
  code: "FILE_PATH_OUTSIDE_PROJECT",
24770
24822
  message: "\u8DEF\u5F84\u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
@@ -24776,10 +24828,29 @@ function normalizeProjectPath(cwd, rawPath, error2 = defaultError) {
24776
24828
  const root = path8.resolve(cwd);
24777
24829
  const resolved = path8.resolve(root, rawPath);
24778
24830
  const relative = path8.relative(root, resolved);
24779
- if (!relative || relative.startsWith("..") || path8.isAbsolute(relative)) {
24831
+ const normalized = relative.replaceAll("\\", "/");
24832
+ if (!relative || relative.startsWith("..") || path8.isAbsolute(relative) || normalized === ".freelog" || normalized.startsWith(".freelog/")) {
24833
+ throw new CliError(error2.message, error2.code);
24834
+ }
24835
+ return normalized;
24836
+ }
24837
+ function isInside(root, target) {
24838
+ const relative = path8.relative(root, target);
24839
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relative);
24840
+ }
24841
+ function resolveExistingProjectPath(cwd, filePath, error2 = defaultError) {
24842
+ const root = path8.resolve(cwd);
24843
+ const absolute = path8.resolve(root, filePath);
24844
+ if (!existsSync7(absolute)) {
24845
+ if (!isInside(root, absolute)) throw new CliError(error2.message, error2.code);
24846
+ return absolute;
24847
+ }
24848
+ const realRoot = realpathSync(root);
24849
+ const realTarget = realpathSync(absolute);
24850
+ if (!isInside(realRoot, realTarget)) {
24780
24851
  throw new CliError(error2.message, error2.code);
24781
24852
  }
24782
- return relative.replaceAll("\\", "/");
24853
+ return realTarget;
24783
24854
  }
24784
24855
 
24785
24856
  // src/local/resolve.ts
@@ -24799,25 +24870,24 @@ function assertNoDuplicate(cwd, identities, key) {
24799
24870
  function matches(cwd, identity, selector) {
24800
24871
  if (selector.startsWith("file:")) return `${identity.n}.json` === selector.slice(5);
24801
24872
  if (selector.startsWith("id:")) return identity.resourceId === selector.slice(3);
24802
- if (selector.startsWith("name:")) return identity.name === selector.slice(5) || identity.name === selector.slice(5).split("/").pop();
24803
- if (selector.startsWith("title:")) return identity.title === selector.slice(6);
24873
+ if (selector.startsWith("name:")) return identity.resourceName === selector.slice(5);
24804
24874
  if (selector.startsWith("artifact:")) {
24805
24875
  return normalizeProjectPath(cwd, identity.filePath) === normalizeProjectPath(
24806
24876
  cwd,
24807
24877
  selector.slice("artifact:".length)
24808
24878
  );
24809
24879
  }
24810
- return `${identity.n}.json` === selector || identity.resourceId === selector || identity.name === selector || identity.title === selector;
24880
+ return `${identity.n}.json` === selector || identity.resourceId === selector || identity.resourceName === selector;
24811
24881
  }
24812
24882
  var DRAFT_FILE_RE = /^([1-9]\d*)\.version\.json$/;
24813
24883
  function validateLocalState(cwd) {
24814
24884
  const identities = listIdentities(cwd);
24815
24885
  assertNoDuplicate(cwd, identities, "resourceId");
24816
- assertNoDuplicate(cwd, identities, "name");
24886
+ assertNoDuplicate(cwd, identities, "resourceName");
24817
24887
  assertNoDuplicate(cwd, identities, "filePath");
24818
24888
  const numbers = new Set(identities.map((identity) => identity.n));
24819
24889
  const dir = freelogDir(cwd);
24820
- if (!existsSync7(dir)) return identities;
24890
+ if (!existsSync8(dir)) return identities;
24821
24891
  for (const fileName of readdirSync2(dir)) {
24822
24892
  const match = DRAFT_FILE_RE.exec(fileName);
24823
24893
  if (!match) continue;
@@ -24914,6 +24984,15 @@ function unwrapOne(result) {
24914
24984
  const data = envelope.data ?? result;
24915
24985
  return data && typeof data === "object" && !Array.isArray(data) ? data : void 0;
24916
24986
  }
24987
+ function normalizeTypeInfo(node) {
24988
+ const support = node.resourceConfig?.supportOptionalConfig ?? node.supportOptionalConfig;
24989
+ if (support === void 0 || support === null || support === "") return node;
24990
+ const numeric = Number(support);
24991
+ return Number.isFinite(numeric) ? { ...node, supportOptionalConfig: numeric } : node;
24992
+ }
24993
+ function supportsOptionalConfig(node) {
24994
+ return Number(node.resourceConfig?.supportOptionalConfig ?? node.supportOptionalConfig) === 2;
24995
+ }
24917
24996
  function isEnabledResourceLeaf(node) {
24918
24997
  return node.isTerminate === true && node.status === 1 && supportsResourceSubject(node.subjectType);
24919
24998
  }
@@ -24943,6 +25022,28 @@ async function resourceTypeTree(apis) {
24943
25022
  const request2 = apis.resourceTypes ?? ((params) => service_API_default.Resource.resourceTypes(params));
24944
25023
  return unwrapList(await request2({ category: 1, status: 1, subjectType: 1 }));
24945
25024
  }
25025
+ function findTypePath(nodes2, code, ancestors = []) {
25026
+ for (const node of nodes2) {
25027
+ const path20 = [...ancestors, node];
25028
+ if (node.code === code) return path20;
25029
+ if (hasChildren(node)) {
25030
+ const found = findTypePath(node.children, code, path20);
25031
+ if (found) return found;
25032
+ }
25033
+ }
25034
+ return void 0;
25035
+ }
25036
+ async function getTypeHierarchy(code, apis = {}) {
25037
+ const normalized = code.trim();
25038
+ if (!normalized) throw new CliError("\u8BF7\u63D0\u4F9B\u8D44\u6E90\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
25039
+ const path20 = findTypePath(await resourceTypeTree(apis), normalized);
25040
+ if (!path20) throw new CliError(`\u5E73\u53F0\u7C7B\u578B\u6811\u4E0D\u5305\u542B\u5F53\u524D\u7C7B\u578B ${normalized}`, "TYPE_HIERARCHY_NOT_FOUND");
25041
+ const names = path20.map((node) => node.name.trim());
25042
+ if (names.some((name) => !name)) {
25043
+ throw new CliError("\u5E73\u53F0\u7C7B\u578B\u94FE\u5B58\u5728\u7F3A\u5931\u540D\u79F0\uFF0C\u65E0\u6CD5\u5C55\u793A\u5B8C\u6574\u5C42\u7EA7", "TYPE_HIERARCHY_INVALID");
25044
+ }
25045
+ return names;
25046
+ }
24946
25047
  async function listLeafTypes(apis = {}) {
24947
25048
  return flattenLeaves(await resourceTypeTree(apis));
24948
25049
  }
@@ -24963,7 +25064,8 @@ async function getTypeInfo(code, apis = {}) {
24963
25064
  }
24964
25065
  assertPlatformAllowed();
24965
25066
  const request2 = apis.getByCode ?? ((params) => service_API_default.Resource.getResourceTypeInfoByCode(params));
24966
- const info2 = unwrapOne(await request2({ code: normalized }));
25067
+ const rawInfo = unwrapOne(await request2({ code: normalized }));
25068
+ const info2 = rawInfo ? normalizeTypeInfo(rawInfo) : void 0;
24967
25069
  if (!info2?.code) {
24968
25070
  throw new CliError(`\u627E\u4E0D\u5230\u7C7B\u578B ${normalized}`, "TYPE_NOT_FOUND");
24969
25071
  }
@@ -25030,10 +25132,15 @@ async function chooseLeafType(apis = {}) {
25030
25132
  function formatTypeList(items) {
25031
25133
  return items.map((item) => `${item.code} ${item.nameChain ?? item.name}`).join("\n");
25032
25134
  }
25135
+ function formatTypeInfo(item) {
25136
+ const optionalConfig = supportsOptionalConfig(item) ? "\u652F\u6301" : "\u4E0D\u652F\u6301";
25137
+ return `${item.code} ${item.nameChain ?? item.name} \u53EF\u9009\u914D\u7F6E\uFF1A${optionalConfig}`;
25138
+ }
25033
25139
 
25034
25140
  // src/domain/version/zip.ts
25035
- import { createWriteStream, existsSync as existsSync8, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync5 } from "fs";
25141
+ import { createWriteStream, existsSync as existsSync9, lstatSync, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync5 } from "fs";
25036
25142
  import { tmpdir } from "os";
25143
+ import { randomUUID } from "crypto";
25037
25144
  import path9 from "path";
25038
25145
  import { finished } from "stream/promises";
25039
25146
  import archiver from "archiver";
@@ -25041,56 +25148,83 @@ function isThemeOrWidget(typeCode) {
25041
25148
  return typeCode === "RT001" || typeCode === "RT002";
25042
25149
  }
25043
25150
  function assertArtifactPath(typeCode, filePath) {
25044
- const stats = existsSync8(filePath) ? statSync(filePath) : void 0;
25151
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25045
25152
  const themeOrWidget = isThemeOrWidget(typeCode);
25046
25153
  if (stats?.isDirectory() && !themeOrWidget) {
25047
25154
  throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25048
25155
  }
25049
25156
  }
25050
- function assertArtifactAnchor(typeCode, filePath) {
25051
- if (!existsSync8(filePath)) {
25157
+ function assertArtifactAnchor(typeCode, filePath, cwd) {
25158
+ if (!existsSync9(filePath)) {
25052
25159
  throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25053
25160
  }
25161
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25054
25162
  const stats = statSync(filePath);
25055
25163
  assertArtifactPath(typeCode, filePath);
25056
25164
  if (!isThemeOrWidget(typeCode) && !stats.isFile()) {
25057
25165
  throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25058
25166
  }
25059
25167
  }
25060
- async function zipDirectoryContents(dir) {
25061
- if (!existsSync8(dir) || !statSync(dir).isDirectory()) {
25168
+ function assertUploadArtifactReady(typeCode, filePath, cwd) {
25169
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25170
+ assertArtifactPath(typeCode, filePath);
25171
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25172
+ if (!stats) {
25173
+ throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25174
+ }
25175
+ if (stats.isDirectory() && isThemeOrWidget(typeCode)) {
25176
+ assertZipDirectoryReady(filePath);
25177
+ return;
25178
+ }
25179
+ if (!stats.isFile()) {
25180
+ throw new CliError("\u4EA7\u7269\u5FC5\u987B\u662F\u6587\u4EF6\uFF0C\u6216\u4E3B\u9898/\u63D2\u4EF6\u7684\u975E\u7A7A\u6784\u5EFA\u76EE\u5F55\u3002", "FILE_ARTIFACT_UNSUPPORTED");
25181
+ }
25182
+ }
25183
+ function assertZipDirectoryReady(dir) {
25184
+ if (!existsSync9(dir) || !statSync(dir).isDirectory()) {
25062
25185
  throw new CliError(`\u6CA1\u6709\u6784\u5EFA\u4EA7\u7269 ${dir}\uFF0C\u8BF7\u5148\u6784\u5EFA`, "ZIP_MISSING");
25063
25186
  }
25064
- if (existsSync8(path9.join(dir, ".freelog"))) {
25187
+ if (existsSync9(path9.join(dir, ".freelog"))) {
25065
25188
  throw new CliError("\u4E0D\u8981\u628A\u5DE5\u7A0B\u6839\u5F53\u4EA7\u7269\u76EE\u5F55\uFF0C\u8BF7\u6307\u5B9A dist \u6216 build\u3002", "ZIP_PROJECT_ROOT");
25066
25189
  }
25067
- const entries = readdirSync3(dir);
25068
- if (entries.length === 0) {
25190
+ if (readdirSync3(dir).length === 0) {
25069
25191
  throw new CliError("\u6784\u5EFA\u4EA7\u7269\u662F\u7A7A\u7684\uFF0C\u8BF7\u5148\u6784\u5EFA", "ZIP_EMPTY");
25070
25192
  }
25193
+ }
25194
+ async function zipDirectoryContents(dir) {
25195
+ assertZipDirectoryReady(dir);
25071
25196
  const outPath = path9.join(
25072
25197
  tmpdir(),
25073
- `freelog-zip-${process.pid}-${Date.now()}.zip`
25198
+ `freelog-zip-${process.pid}-${randomUUID()}.zip`
25074
25199
  );
25075
25200
  const output2 = createWriteStream(outPath);
25076
25201
  const archive = archiver("zip", { zlib: { level: 9 } });
25077
25202
  try {
25078
25203
  archive.on("error", (error2) => output2.destroy(error2));
25079
25204
  archive.pipe(output2);
25080
- for (const entry2 of entries) {
25081
- const full = path9.join(dir, entry2);
25082
- const stats = statSync(full);
25083
- if (stats.isDirectory()) {
25084
- archive.directory(full, entry2);
25085
- } else {
25086
- archive.file(full, { name: entry2 });
25205
+ const appendEntries = (currentDir, prefix = "") => {
25206
+ for (const entry2 of readdirSync3(currentDir).sort((left, right) => left < right ? -1 : left > right ? 1 : 0)) {
25207
+ const full = path9.join(currentDir, entry2);
25208
+ const stats = lstatSync(full);
25209
+ const entryName = prefix ? `${prefix}/${entry2}` : entry2;
25210
+ if (stats.isSymbolicLink()) {
25211
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u4E0D\u80FD\u5305\u542B\u8F6F\u94FE\u63A5\uFF1A${entryName}`, "ZIP_SYMLINK_UNSUPPORTED");
25212
+ }
25213
+ if (stats.isDirectory()) {
25214
+ appendEntries(full, entryName);
25215
+ } else if (stats.isFile()) {
25216
+ archive.append(readFileSync6(full), { name: entryName });
25217
+ } else {
25218
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u5305\u542B\u4E0D\u652F\u6301\u7684\u6761\u76EE\uFF1A${entryName}`, "ZIP_ENTRY_UNSUPPORTED");
25219
+ }
25087
25220
  }
25088
- }
25221
+ };
25222
+ appendEntries(dir);
25089
25223
  await archive.finalize();
25090
25224
  await finished(output2);
25091
25225
  return outPath;
25092
25226
  } catch (error2) {
25093
- if (existsSync8(outPath)) {
25227
+ if (existsSync9(outPath)) {
25094
25228
  try {
25095
25229
  unlinkSync5(outPath);
25096
25230
  } catch {
@@ -25099,9 +25233,9 @@ async function zipDirectoryContents(dir) {
25099
25233
  throw error2;
25100
25234
  }
25101
25235
  }
25102
- async function prepareUploadPath(typeCode, filePath) {
25103
- assertArtifactPath(typeCode, filePath);
25104
- if (isThemeOrWidget(typeCode) && existsSync8(filePath) && statSync(filePath).isDirectory()) {
25236
+ async function prepareUploadPath(typeCode, filePath, cwd) {
25237
+ assertUploadArtifactReady(typeCode, filePath, cwd);
25238
+ if (isThemeOrWidget(typeCode) && existsSync9(filePath) && statSync(filePath).isDirectory()) {
25105
25239
  return zipDirectoryContents(filePath);
25106
25240
  }
25107
25241
  return filePath;
@@ -25164,6 +25298,9 @@ function validateCreateFlags(input2) {
25164
25298
  function resolveTargetIdentity(input2) {
25165
25299
  if (input2.selector) return resolveIdentity(input2.cwd, input2.selector);
25166
25300
  const unbound = input2.identities.filter((identity) => !identity.resourceId);
25301
+ if (input2.artifact !== void 0) {
25302
+ return unbound.find((identity) => identity.filePath === input2.artifact);
25303
+ }
25167
25304
  if (unbound.length === 1) return unbound[0];
25168
25305
  if (unbound.length > 1) throw new CliError("\u5F53\u524D\u5DE5\u7A0B\u6709\u591A\u4EFD\u672A\u7ED1\u5B9A\u8D44\u6E90\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 --resource \u6307\u5B9A\u8D44\u6E90", "IDENTITY_RESOURCE_REQUIRED");
25169
25306
  return void 0;
@@ -25177,7 +25314,8 @@ async function assertOwnShellAvailable(input2) {
25177
25314
  if (!existing.resourceId) {
25178
25315
  return;
25179
25316
  }
25180
- if (existing.userId !== input2.authUserId) {
25317
+ const ownerId = existing.userId ?? existing.ownerId ?? existing.creatorId;
25318
+ if (ownerId !== input2.authUserId) {
25181
25319
  throw new CliError(
25182
25320
  `\u8D44\u6E90\u6388\u6743\u6807\u8BC6 ${input2.name} \u5DF2\u88AB\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002`,
25183
25321
  "CREATE_NAME_TAKEN"
@@ -25193,6 +25331,7 @@ function writeCreatedIdentity(input2) {
25193
25331
  if (!filePath) throw new CliError("\u65B0\u8D44\u6E90\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "CREATE_ARTIFACT_REQUIRED");
25194
25332
  const patch = {
25195
25333
  resourceId: input2.resourceId,
25334
+ resourceName: input2.resourceName,
25196
25335
  name: input2.name,
25197
25336
  title: input2.title,
25198
25337
  typeCode: input2.typeCode,
@@ -25202,6 +25341,7 @@ function writeCreatedIdentity(input2) {
25202
25341
  return input2.target ? updateIdentity(input2.cwd, input2.target.n, patch) : createIdentity(input2.cwd, {
25203
25342
  subject: "resource",
25204
25343
  resourceId: input2.resourceId,
25344
+ resourceName: input2.resourceName,
25205
25345
  name: input2.name,
25206
25346
  title: input2.title,
25207
25347
  typeCode: input2.typeCode,
@@ -25212,7 +25352,6 @@ function writeCreatedIdentity(input2) {
25212
25352
  async function createResource(input2) {
25213
25353
  assertPlatformAllowed();
25214
25354
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25215
- validateLocalState(input2.cwd);
25216
25355
  const { title, name } = validateCreateFlags(input2);
25217
25356
  const file = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25218
25357
  code: "CREATE_FILE_OUTSIDE",
@@ -25223,6 +25362,7 @@ async function createResource(input2) {
25223
25362
  const target = resolveTargetIdentity({
25224
25363
  cwd: input2.cwd,
25225
25364
  selector: input2.selector,
25365
+ artifact: file,
25226
25366
  identities
25227
25367
  });
25228
25368
  const occupant = file ? identities.find((identity) => identity.filePath === file && identity.n !== target?.n) : void 0;
@@ -25248,7 +25388,7 @@ async function createResource(input2) {
25248
25388
  }
25249
25389
  const typeCode = resolveTypeCode(input2.type, target);
25250
25390
  await getTypeInfo(typeCode, input2.apis);
25251
- assertArtifactAnchor(typeCode, path10.resolve(input2.cwd, artifact));
25391
+ assertArtifactAnchor(typeCode, path10.resolve(input2.cwd, artifact), input2.cwd);
25252
25392
  await assertOwnShellAvailable({
25253
25393
  authLoginName: auth.loginName,
25254
25394
  authUserId: auth.userId,
@@ -25271,6 +25411,7 @@ async function createResource(input2) {
25271
25411
  cwd: input2.cwd,
25272
25412
  env: getEnv2(),
25273
25413
  name,
25414
+ resourceName: typeof created.resourceName === "string" && created.resourceName ? created.resourceName : `${auth.loginName}/${name}`,
25274
25415
  typeCode,
25275
25416
  resourceId,
25276
25417
  title,
@@ -25318,6 +25459,10 @@ function createCreateCommand() {
25318
25459
  // src/commands/listing/update.ts
25319
25460
  import { Command as Command5 } from "commander";
25320
25461
 
25462
+ // src/domain/listing/update.ts
25463
+ import { existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
25464
+ import path11 from "path";
25465
+
25321
25466
  // src/domain/version/gates.ts
25322
25467
  function evaluateGates(input2, intent) {
25323
25468
  const latest = input2.latestVersion;
@@ -25359,19 +25504,170 @@ function resolveBoundIdentity(cwd, file) {
25359
25504
  if (!identity.resourceId) {
25360
25505
  throw new CliError("\u8BF7\u5148 create \u6216 bind", "GATE_NO_RESOURCE");
25361
25506
  }
25507
+ const identityEnv = identity.env ?? "prod";
25508
+ const activeEnv2 = getEnv2();
25509
+ if (identityEnv !== activeEnv2) {
25510
+ throw new CliError(
25511
+ `\u8D44\u6E90 ${identity.n}.json \u5C5E\u4E8E ${identityEnv} \u73AF\u5883\uFF0C\u672C\u6B21\u662F ${activeEnv2}\uFF1B\u8BF7\u5207\u6362 --env \u6216\u9009\u62E9\u8BE5\u73AF\u5883\u7684\u8D44\u6E90\u72B6\u6001`,
25512
+ "RESOURCE_ENV_MISMATCH"
25513
+ );
25514
+ }
25362
25515
  return identity;
25363
25516
  }
25517
+ function assertRemoteResourceWritable(input2) {
25518
+ const codes = {
25519
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25520
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER",
25521
+ frozen: input2.codes?.frozen ?? "RESOURCE_FROZEN"
25522
+ };
25523
+ assertRemoteResourceOwned({
25524
+ info: input2.info,
25525
+ resourceId: input2.resourceId,
25526
+ authUserId: input2.authUserId,
25527
+ codes
25528
+ });
25529
+ if (input2.info.status === 2 || input2.info.isFrozen === true) {
25530
+ throw new CliError("\u8D44\u6E90\u5DF2\u88AB\u51BB\u7ED3\uFF0C\u4E0D\u80FD\u4FEE\u6539", codes.frozen);
25531
+ }
25532
+ }
25533
+ function assertRemoteResourceOwned(input2) {
25534
+ const codes = {
25535
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25536
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER"
25537
+ };
25538
+ if (input2.info.resourceId !== input2.resourceId) {
25539
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25540
+ }
25541
+ const ownerId = input2.info.userId ?? input2.info.ownerId ?? input2.info.creatorId;
25542
+ if (typeof ownerId !== "number" || !Number.isSafeInteger(ownerId)) {
25543
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8D44\u6E90 owner\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25544
+ }
25545
+ if (ownerId !== input2.authUserId) {
25546
+ throw new CliError("\u53EA\u80FD\u4FEE\u6539\u81EA\u5DF1\u7684\u8D44\u6E90", codes.notOwner);
25547
+ }
25548
+ }
25549
+
25550
+ // src/platform/unwrap.ts
25551
+ function unwrapData2(result) {
25552
+ const envelope = result;
25553
+ const data = envelope.data ?? result;
25554
+ return data ?? {};
25555
+ }
25556
+ function unwrapFirst(result) {
25557
+ const envelope = result;
25558
+ const data = envelope.data ?? result;
25559
+ if (Array.isArray(data)) {
25560
+ return data[0] ?? {};
25561
+ }
25562
+ return data ?? {};
25563
+ }
25564
+ function unwrapList2(result, fields = ["dataList", "list"]) {
25565
+ const envelope = result;
25566
+ const data = envelope.data ?? result;
25567
+ if (Array.isArray(data)) {
25568
+ return data;
25569
+ }
25570
+ if (data && typeof data === "object") {
25571
+ for (const field of fields) {
25572
+ const list2 = data[field];
25573
+ if (Array.isArray(list2)) {
25574
+ return list2;
25575
+ }
25576
+ }
25577
+ }
25578
+ return [];
25579
+ }
25364
25580
 
25365
25581
  // src/domain/listing/update.ts
25366
25582
  var MAX_TITLE = 100;
25367
25583
  var MAX_INTRO = 200;
25368
25584
  var MAX_TAGS = 20;
25369
25585
  var MAX_TAG_LENGTH = 20;
25586
+ var MAX_COVER_BYTES = 5 * 1024 * 1024;
25587
+ var COVER_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif"]);
25370
25588
  function assertTitle(title) {
25589
+ if (!title) {
25590
+ throw new CliError("\u8BF7\u8F93\u5165\u8D44\u6E90\u6807\u9898", "UPDATE_TITLE_REQUIRED");
25591
+ }
25371
25592
  if (title.length > MAX_TITLE) {
25372
25593
  throw new CliError("\u6807\u9898\u4E0D\u80FD\u8D85\u8FC7100\u4E2A\u5B57\u7B26", "UPDATE_TITLE_LONG");
25373
25594
  }
25374
25595
  }
25596
+ function isAnimatedGif(content) {
25597
+ if (content.subarray(0, 6).toString("ascii") !== "GIF87a" && content.subarray(0, 6).toString("ascii") !== "GIF89a" || content.length < 13) {
25598
+ return true;
25599
+ }
25600
+ let offset = 13;
25601
+ if ((content[10] & 128) !== 0) {
25602
+ offset += 3 * 2 ** ((content[10] & 7) + 1);
25603
+ }
25604
+ let frames = 0;
25605
+ while (offset < content.length) {
25606
+ const marker = content[offset++];
25607
+ if (marker === 59) return frames > 1;
25608
+ if (marker === 44) {
25609
+ frames += 1;
25610
+ if (frames > 1) return true;
25611
+ if (offset + 9 > content.length) return true;
25612
+ const packed = content[offset + 8];
25613
+ offset += 9;
25614
+ if ((packed & 128) !== 0) offset += 3 * 2 ** ((packed & 7) + 1);
25615
+ offset += 1;
25616
+ } else if (marker === 33) {
25617
+ offset += 1;
25618
+ } else {
25619
+ return true;
25620
+ }
25621
+ while (offset < content.length) {
25622
+ const size = content[offset++];
25623
+ if (size === 0) break;
25624
+ offset += size;
25625
+ }
25626
+ }
25627
+ return true;
25628
+ }
25629
+ function assertCoverContent(extension, content) {
25630
+ const isJpeg = content.length >= 3 && content[0] === 255 && content[1] === 216 && content[2] === 255;
25631
+ const isPng = content.length >= 8 && content.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
25632
+ const isGif = content.subarray(0, 6).toString("ascii") === "GIF87a" || content.subarray(0, 6).toString("ascii") === "GIF89a";
25633
+ if ((extension === ".jpg" || extension === ".jpeg") && isJpeg) return;
25634
+ if (extension === ".png" && isPng) return;
25635
+ if (extension === ".gif" && isGif && !isAnimatedGif(content)) return;
25636
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F JPG\u3001PNG \u6216\u9759\u6001 GIF \u56FE\u7247", "UPDATE_COVER_INVALID");
25637
+ }
25638
+ async function uploadCover(cwd, cover, apis) {
25639
+ const normalized = normalizeProjectPath(cwd, cover, {
25640
+ code: "UPDATE_COVER_OUTSIDE",
25641
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25642
+ });
25643
+ const localPath = resolveExistingProjectPath(cwd, path11.resolve(cwd, normalized), {
25644
+ code: "UPDATE_COVER_OUTSIDE",
25645
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25646
+ });
25647
+ if (!existsSync10(localPath)) {
25648
+ throw new CliError(`\u5C01\u9762\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${normalized}`, "UPDATE_COVER_MISSING");
25649
+ }
25650
+ const stats = statSync2(localPath);
25651
+ if (!stats.isFile()) {
25652
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F\u4E00\u4E2A\u56FE\u7247\u6587\u4EF6", "UPDATE_COVER_INVALID");
25653
+ }
25654
+ if (stats.size > MAX_COVER_BYTES) {
25655
+ throw new CliError("\u5C01\u9762\u4E0D\u80FD\u8D85\u8FC7 5MB", "UPDATE_COVER_TOO_LARGE");
25656
+ }
25657
+ const extension = path11.extname(normalized).toLowerCase();
25658
+ if (!COVER_EXTENSIONS.has(extension)) {
25659
+ throw new CliError("\u5C01\u9762\u53EA\u652F\u6301 JPG\u3001PNG \u6216\u9759\u6001 GIF", "UPDATE_COVER_INVALID");
25660
+ }
25661
+ const content = readFileSync7(localPath);
25662
+ assertCoverContent(extension, content);
25663
+ const upload = apis?.uploadImage ?? ((params) => service_API_default.Storage.uploadImage(params));
25664
+ const result = unwrapData2(await upload({ file: content }));
25665
+ const url2 = result.url ?? result.fileUrl ?? result.fileURL;
25666
+ if (typeof url2 !== "string" || !url2.trim()) {
25667
+ throw new CliError("\u5C01\u9762\u4E0A\u4F20\u6210\u529F\u4F46\u5E73\u53F0\u672A\u8FD4\u56DE\u56FE\u7247 URL", "UPDATE_COVER_UPLOAD_INVALID");
25668
+ }
25669
+ return url2;
25670
+ }
25375
25671
  function assertIntro(intro) {
25376
25672
  if (intro.length > MAX_INTRO) {
25377
25673
  throw new CliError("\u7B80\u4ECB\u4E0D\u80FD\u8D85\u8FC7200\u4E2A\u5B57\u7B26", "UPDATE_INTRO_LONG");
@@ -25385,9 +25681,6 @@ function assertTags(tags) {
25385
25681
  if (tag.length > MAX_TAG_LENGTH) {
25386
25682
  throw new CliError("\u5355\u4E2A\u6807\u7B7E\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC720\u4E2A\u5B57\u7B26", "UPDATE_TAG_LONG");
25387
25683
  }
25388
- if (tag.includes("#")) {
25389
- throw new CliError(`\u6807\u7B7E\u4E0D\u80FD\u5305\u542B #\uFF1A${tag}`, "UPDATE_TAG_INVALID");
25390
- }
25391
25684
  }
25392
25685
  if (tags.some((tag, i) => tags.indexOf(tag) !== i)) {
25393
25686
  throw new CliError("\u8BE5\u6807\u7B7E\u5DF2\u6DFB\u52A0", "UPDATE_TAG_DUPLICATE");
@@ -25395,34 +25688,47 @@ function assertTags(tags) {
25395
25688
  }
25396
25689
  async function updateListing(input2) {
25397
25690
  assertPlatformAllowed();
25398
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25399
- if (input2.yes && !input2.title && !input2.intro && !input2.cover && !input2.tags) {
25691
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25692
+ if (input2.yes && input2.title === void 0 && input2.intro === void 0 && input2.cover === void 0 && input2.tags === void 0) {
25400
25693
  throw new CliError("--yes \u4E14\u65E0\u4FEE\u6539\u9879\uFF0C\u62D2\u7EDD\u7A7A\u66F4\u65B0", "UPDATE_NO_FLAGS");
25401
25694
  }
25402
25695
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
25696
+ const info2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
25697
+ const remote = unwrapData2(await info2({ resourceIdOrName: identity.resourceId }));
25698
+ assertRemoteResourceWritable({ info: remote, resourceId: identity.resourceId, authUserId: auth.userId });
25403
25699
  const payload = {
25404
25700
  resourceId: identity.resourceId
25405
25701
  };
25406
- if (input2.title) {
25407
- assertTitle(input2.title);
25408
- payload.resourceTitle = input2.title;
25702
+ const title = input2.title?.trim();
25703
+ if (input2.title !== void 0) {
25704
+ assertTitle(title ?? "");
25705
+ payload.resourceTitle = title;
25409
25706
  }
25410
- if (input2.intro) {
25411
- assertIntro(input2.intro);
25412
- payload.intro = input2.intro;
25707
+ if (input2.intro !== void 0) {
25708
+ const intro = input2.intro.trim();
25709
+ assertIntro(intro);
25710
+ payload.intro = intro;
25413
25711
  }
25414
- if (input2.cover) payload.coverImages = [input2.cover];
25415
- if (input2.tags) {
25416
- const tags = input2.tags.split(",").map((item) => item.trim().replaceAll("#", "")).filter(Boolean);
25417
- assertTags(tags);
25418
- payload.tags = tags;
25712
+ if (input2.cover !== void 0) {
25713
+ if (!input2.cover.trim()) {
25714
+ throw new CliError("\u8BF7\u63D0\u4F9B\u5C01\u9762\u56FE\u7247\u8DEF\u5F84\uFF1B\u672C\u671F\u4E0D\u652F\u6301\u6E05\u7A7A\u5C01\u9762", "UPDATE_COVER_REQUIRED");
25715
+ }
25716
+ payload.coverImages = [await uploadCover(input2.cwd, input2.cover, input2.apis)];
25717
+ }
25718
+ if (input2.tags !== void 0) {
25719
+ const rawTags = input2.tags === "" ? [] : input2.tags.split(",").map((item) => item.trim().replaceAll("#", ""));
25720
+ if (rawTags.some((tag) => !tag)) {
25721
+ throw new CliError("\u6807\u7B7E\u4E0D\u80FD\u5305\u542B\u7A7A\u9879", "UPDATE_TAG_EMPTY");
25722
+ }
25723
+ assertTags(rawTags);
25724
+ payload.tags = rawTags;
25419
25725
  }
25420
25726
  if ("status" in payload) {
25421
25727
  delete payload.status;
25422
25728
  }
25423
25729
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
25424
25730
  await update2(payload);
25425
- if (input2.title) updateIdentity(input2.cwd, identity.n, { title: input2.title });
25731
+ if (title !== void 0) updateIdentity(input2.cwd, identity.n, { title });
25426
25732
  return payload;
25427
25733
  }
25428
25734
 
@@ -25448,27 +25754,13 @@ function createUpdateCommand() {
25448
25754
  }
25449
25755
 
25450
25756
  // src/commands/policy/apply.ts
25451
- import { readFileSync as readFileSync6 } from "fs";
25452
- import path11 from "path";
25757
+ import { readFileSync as readFileSync8 } from "fs";
25758
+ import path12 from "path";
25453
25759
  import { confirm, input } from "@inquirer/prompts";
25454
25760
  import { Command as Command6 } from "commander";
25455
25761
 
25456
- // src/platform/unwrap.ts
25457
- function unwrapData2(result) {
25458
- const envelope = result;
25459
- const data = envelope.data ?? result;
25460
- return data ?? {};
25461
- }
25462
- function unwrapFirst(result) {
25463
- const envelope = result;
25464
- const data = envelope.data ?? result;
25465
- if (Array.isArray(data)) {
25466
- return data[0] ?? {};
25467
- }
25468
- return data ?? {};
25469
- }
25470
-
25471
25762
  // src/domain/policy/list.ts
25763
+ var POLICY_LIST_PAGE_SIZE = 50;
25472
25764
  function infoApi(apis) {
25473
25765
  return apis?.info ?? ((params) => service_API_default.Resource.info(params));
25474
25766
  }
@@ -25497,14 +25789,17 @@ function decoded(value) {
25497
25789
  return value;
25498
25790
  }
25499
25791
  }
25500
- function assertEditable(info2, authUserId) {
25501
- if (info2.status === 2 || info2.isFrozen === true) {
25502
- throw new CliError("\u8D44\u6E90\u5DF2\u88AB\u51BB\u7ED3\uFF0C\u4E0D\u80FD\u4FEE\u6539\u6388\u6743\u7B56\u7565", "POLICY_RESOURCE_FROZEN");
25503
- }
25504
- const ownerId = info2.userId ?? info2.ownerId ?? info2.creatorId;
25505
- if (typeof ownerId === "number" && ownerId !== authUserId) {
25506
- throw new CliError("\u53EA\u80FD\u4FEE\u6539\u81EA\u5DF1\u7684\u8D44\u6E90\u6388\u6743\u7B56\u7565", "POLICY_NOT_OWNER");
25507
- }
25792
+ function assertEditable(info2, authUserId, resourceId) {
25793
+ assertRemoteResourceWritable({
25794
+ info: info2,
25795
+ resourceId,
25796
+ authUserId,
25797
+ codes: {
25798
+ invalid: "POLICY_INFO_INVALID",
25799
+ notOwner: "POLICY_NOT_OWNER",
25800
+ frozen: "POLICY_RESOURCE_FROZEN"
25801
+ }
25802
+ });
25508
25803
  }
25509
25804
  async function loadPolicyContext(input2) {
25510
25805
  assertPlatformAllowed();
@@ -25523,7 +25818,7 @@ async function loadPolicyContext(input2) {
25523
25818
  isLoadPolicyInfo: 1,
25524
25819
  isTranslate: 1
25525
25820
  }));
25526
- if (input2.editable) assertEditable(info2, auth.userId);
25821
+ if (input2.editable) assertEditable(info2, auth.userId, resourceId);
25527
25822
  return {
25528
25823
  resourceId,
25529
25824
  typeCode,
@@ -25534,30 +25829,56 @@ async function loadPolicyContext(input2) {
25534
25829
  function templateFrom(value) {
25535
25830
  if (!value || typeof value !== "object") return void 0;
25536
25831
  const raw = value;
25537
- const id = raw.id ?? raw.templateId ?? raw.policyTemplateId;
25538
- const name = raw.name ?? raw.templateName ?? raw.policyName;
25539
- const defaultValue = raw.defaultValue ?? raw.policyText ?? raw.value;
25832
+ const id = raw.id ?? raw.templateId ?? raw.policyTemplateId ?? raw._id;
25833
+ const name = raw.name ?? raw.templateName ?? raw.policyName ?? raw.title;
25834
+ const defaultValue = raw.defaultValue ?? raw.policyText ?? raw.value ?? raw.template;
25540
25835
  if (typeof id !== "string" || !id || typeof name !== "string" || !name || typeof defaultValue !== "string" || !defaultValue.trim()) {
25541
25836
  return void 0;
25542
25837
  }
25543
25838
  const summaryValue = raw.summary ?? raw.description ?? raw.eventSummary;
25839
+ const fillArgs = Array.isArray(raw.reportUiTemplate) ? raw.reportUiTemplate.flatMap((item) => {
25840
+ if (!item || typeof item !== "object") return [];
25841
+ const field = item;
25842
+ const name2 = field.id;
25843
+ const value2 = field.uiSectionDefaultValue;
25844
+ return typeof name2 === "string" && name2 && (typeof value2 === "string" || typeof value2 === "number") ? [{ name: name2, value: value2 }] : [];
25845
+ }) : [];
25544
25846
  return {
25545
25847
  id,
25546
25848
  name,
25547
25849
  defaultValue,
25850
+ fillArgs,
25548
25851
  ...typeof summaryValue === "string" && summaryValue.trim() ? { summary: summaryValue.trim() } : {}
25549
25852
  };
25550
25853
  }
25854
+ function normalizeCompiledTemplateDsl(contract2) {
25855
+ return contract2.replace(/\bfor\s+public\b/gi, "FOR PUBLIC").replace(/\binitial\b/gi, "Initial");
25856
+ }
25857
+ function compiledContract(result) {
25858
+ const envelope = result;
25859
+ const data = envelope.data ?? result;
25860
+ const contract2 = data && typeof data === "object" ? data.contractNew ?? data.contract : data;
25861
+ if (typeof contract2 !== "string" || !contract2.trim()) {
25862
+ throw new CliError("\u7B56\u7565\u6A21\u677F\u7F16\u8BD1\u7ED3\u679C\u7F3A\u5C11 contractNew", "POLICY_TEMPLATE_COMPILE_INVALID");
25863
+ }
25864
+ return normalizeCompiledTemplateDsl(contract2).trim();
25865
+ }
25551
25866
  async function getPolicyTemplates(input2) {
25552
- const context = await loadPolicyContext(input2);
25867
+ await loadPolicyContext(input2);
25553
25868
  const request2 = input2.apis?.policyTemplates ?? ((params) => service_API_default.Policy.policyTemplates(params));
25554
- const result = unwrapData2(await request2({ resourceTypeCodes4Resource: [context.typeCode] }));
25555
- const rawList = Array.isArray(result.list) ? result.list : Array.isArray(result.dataList) ? result.dataList : Array.isArray(result.templates) ? result.templates : [];
25869
+ const rawList = unwrapList2(await request2({}), ["list", "dataList", "templates"]);
25556
25870
  return rawList.flatMap((item) => {
25557
25871
  const template = templateFrom(item);
25558
25872
  return template ? [template] : [];
25559
25873
  });
25560
25874
  }
25875
+ async function applyPolicyTemplate(input2) {
25876
+ const template = (await getPolicyTemplates(input2)).find((item) => item.id === input2.templateId);
25877
+ if (!template) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25878
+ const request2 = input2.apis?.policyReCompile ?? ((params) => service_API_default.Policy.policyReCompile(params));
25879
+ const policyText = compiledContract(await request2({ _id: template.id, fillArgs: template.fillArgs }));
25880
+ await applyPolicy({ ...input2, policyText });
25881
+ }
25561
25882
  function positiveInteger(value, fallback, name, max) {
25562
25883
  const actual = value ?? fallback;
25563
25884
  if (!Number.isInteger(actual) || actual < 1 || actual > max) {
@@ -25565,10 +25886,39 @@ function positiveInteger(value, fallback, name, max) {
25565
25886
  }
25566
25887
  return actual;
25567
25888
  }
25568
- async function listPolicies(input2) {
25889
+ async function getPolicyList(input2) {
25569
25890
  const context = await loadPolicyContext(input2);
25570
- if (!context.policies.length) return "\u8FD8\u6CA1\u6709\u6388\u6743\u7B56\u7565";
25571
- return [...context.policies].sort((a, b) => b.status - a.status || a.policyName.localeCompare(b.policyName)).map((item) => `${item.policyId} ${item.policyName} ${item.status === 1 ? "on" : "off"}`).join("\n");
25891
+ const typeHierarchy = await getTypeHierarchy(context.typeCode, input2.apis);
25892
+ return {
25893
+ typeHierarchy,
25894
+ policies: [...context.policies].sort((a, b) => b.status - a.status || a.policyName.localeCompare(b.policyName))
25895
+ };
25896
+ }
25897
+ function policyListPage(list2, page) {
25898
+ const pageCount = Math.max(1, Math.ceil(list2.policies.length / POLICY_LIST_PAGE_SIZE));
25899
+ if (!Number.isInteger(page) || page < 1 || page > pageCount) {
25900
+ throw new CliError(`\u7B56\u7565\u9875\u7801\u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5171 ${pageCount} \u9875`, "POLICY_LIST_PAGE");
25901
+ }
25902
+ return {
25903
+ ...list2,
25904
+ page,
25905
+ pageCount,
25906
+ total: list2.policies.length,
25907
+ hasPrevious: page > 1,
25908
+ hasNext: page < pageCount,
25909
+ items: list2.policies.slice((page - 1) * POLICY_LIST_PAGE_SIZE, page * POLICY_LIST_PAGE_SIZE)
25910
+ };
25911
+ }
25912
+ function formatPolicyListPage(page) {
25913
+ const header = [
25914
+ `\u8D44\u6E90\u7C7B\u578B\uFF1A${page.typeHierarchy.join(" / ")}`,
25915
+ `\u7B2C ${page.page}/${page.pageCount} \u9875\uFF0C\u5171 ${page.total} \u6761`
25916
+ ];
25917
+ if (page.items.length === 0) return [...header, "\u8FD8\u6CA1\u6709\u6388\u6743\u7B56\u7565"].join("\n");
25918
+ return [
25919
+ ...header,
25920
+ ...page.items.map((item) => `${item.policyId} ${item.policyName} ${item.status === 1 ? "on" : "off"}`)
25921
+ ].join("\n");
25572
25922
  }
25573
25923
  async function listPolicyTemplates(input2) {
25574
25924
  const page = positiveInteger(input2.page, 1, "--page", Number.MAX_SAFE_INTEGER);
@@ -25627,8 +25977,8 @@ async function setPolicy(input2) {
25627
25977
 
25628
25978
  // src/commands/policy/apply.ts
25629
25979
  function parsePolicy(file) {
25630
- const text = readFileSync6(file, "utf8");
25631
- if (path11.extname(file).toLowerCase() !== ".json") return { policyText: text };
25980
+ const text = readFileSync8(file, "utf8");
25981
+ if (path12.extname(file).toLowerCase() !== ".json") return { policyText: text };
25632
25982
  let parsed;
25633
25983
  try {
25634
25984
  parsed = JSON.parse(text);
@@ -25663,17 +26013,39 @@ function createPolicyApplyCommand() {
25663
26013
 
25664
26014
  // src/commands/policy/list.ts
25665
26015
  import { Command as Command7 } from "commander";
26016
+ async function showPolicyPages(pageFor) {
26017
+ let page = 1;
26018
+ while (true) {
26019
+ const current = pageFor(page);
26020
+ console.log(formatPolicyListPage(current));
26021
+ if (!current.hasPrevious && !current.hasNext) return;
26022
+ if (!isInteractive()) {
26023
+ if (current.hasNext) {
26024
+ console.log(`\u8FD8\u6709 ${current.total - current.page * POLICY_LIST_PAGE_SIZE} \u6761\u7B56\u7565\uFF1B\u8BF7\u5728\u4EA4\u4E92\u7EC8\u7AEF\u8FD0\u884C policy list \u67E5\u770B\u540E\u7EED\u9875`);
26025
+ }
26026
+ return;
26027
+ }
26028
+ const action = await selectQuestion("\u6388\u6743\u7B56\u7565\u5217\u8868", [
26029
+ ...current.hasPrevious ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
26030
+ ...current.hasNext ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
26031
+ { name: "\u9000\u51FA", value: "__exit__" }
26032
+ ]);
26033
+ if (action === "__exit__") return;
26034
+ page += action === "__next__" ? 1 : -1;
26035
+ }
26036
+ }
25666
26037
  function createPolicyListCommand() {
25667
26038
  const command = addSharedOptions(new Command7("list"));
25668
26039
  command.description(
25669
26040
  // i18n: cli.command.policy.list.description
25670
- "\u770B\u5DF2\u6709\u7B56\u7565"
26041
+ "\u770B\u5DF2\u6709\u7B56\u7565\u53CA\u8D44\u6E90\u7C7B\u578B\u5C42\u7EA7"
25671
26042
  ).action(async function() {
25672
26043
  const shared = readSharedOptions(this);
25673
- console.log(await listPolicies({
26044
+ const list2 = await getPolicyList({
25674
26045
  cwd: resolveCwd(shared.cwd),
25675
26046
  file: shared.file
25676
- }));
26047
+ });
26048
+ await showPolicyPages((page) => policyListPage(list2, page));
25677
26049
  });
25678
26050
  return command;
25679
26051
  }
@@ -25725,7 +26097,7 @@ async function chooseTemplate(templates) {
25725
26097
  }
25726
26098
  function createPolicyTemplateCommand() {
25727
26099
  const template = addSharedOptions(new Command9("template")).description("\u6388\u6743\u7B56\u7565\u6A21\u677F");
25728
- addSharedOptions(template.command("list")).description("\u6309\u5F53\u524D\u8D44\u6E90\u7C7B\u578B\u5217\u5168\u90E8\u6A21\u677F").option("--page <n>", "\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB", Number).option("--page-size <n>", "\u6BCF\u9875 1\u2013100 \u6761\uFF0C\u9ED8\u8BA4 20", Number).action(async function(options) {
26100
+ addSharedOptions(template.command("list")).description("\u5217\u5F53\u524D\u6A21\u677F\u5217\u8868").option("--page <n>", "\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB", Number).option("--page-size <n>", "\u6BCF\u9875 1\u2013100 \u6761\uFF0C\u9ED8\u8BA4 20", Number).action(async function(options) {
25729
26101
  const shared = readSharedOptions(this);
25730
26102
  console.log(await listPolicyTemplates({ cwd: resolveCwd(shared.cwd), file: shared.file, page: options.page, pageSize: options.pageSize }));
25731
26103
  });
@@ -25735,7 +26107,7 @@ function createPolicyTemplateCommand() {
25735
26107
  const yes = shared.yes === true;
25736
26108
  const templates = await getPolicyTemplates({ cwd, file: shared.file });
25737
26109
  let item = templateId ? templates.find((candidate) => candidate.id === templateId) : void 0;
25738
- if (!item && templateId) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5C5E\u4E8E\u5F53\u524D\u8D44\u6E90\u7C7B\u578B", "POLICY_TEMPLATE_INVALID");
26110
+ if (!item && templateId) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25739
26111
  if (!item) {
25740
26112
  if (yes || !process.stdin.isTTY) throw new CliError("--yes \u6216\u975E\u4EA4\u4E92\u6A21\u5F0F\u5FC5\u987B\u63D0\u4F9B templateId", "POLICY_TEMPLATE_ID");
25741
26113
  item = await chooseTemplate(templates);
@@ -25743,7 +26115,7 @@ function createPolicyTemplateCommand() {
25743
26115
  }
25744
26116
  const policyName = options.name ?? item.name;
25745
26117
  if (!yes && !await confirm3({ message: `\u6DFB\u52A0\u5E76\u542F\u7528\u7B56\u7565\u300C${policyName}\u300D\uFF1F`, default: true })) return;
25746
- await applyPolicy({ cwd, file: shared.file, policyName, policyText: item.defaultValue });
26118
+ await applyPolicyTemplate({ cwd, file: shared.file, templateId: item.id, policyName });
25747
26119
  console.log("\u5DF2\u6DFB\u52A0\u5E76\u542F\u7528\u6388\u6743\u7B56\u7565");
25748
26120
  });
25749
26121
  return template;
@@ -25753,28 +26125,18 @@ function createPolicyTemplateCommand() {
25753
26125
  import { Command as Command10 } from "commander";
25754
26126
 
25755
26127
  // src/domain/bind/bind.ts
25756
- import { existsSync as existsSync10 } from "fs";
25757
-
25758
- // src/local/indexFile.ts
25759
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
25760
- import path12 from "path";
25761
- function normalizeFileKey(filePath) {
25762
- return path12.posix.normalize(filePath.replaceAll("\\", "/")).replace(/^\.\//, "");
25763
- }
25764
-
25765
- // src/domain/bind/bind.ts
26128
+ import { existsSync as existsSync11 } from "fs";
25766
26129
  import path13 from "path";
25767
26130
  async function bindResource(input2) {
25768
26131
  assertPlatformAllowed();
25769
26132
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25770
- validateLocalState(input2.cwd);
25771
26133
  const filePath = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25772
26134
  code: "BIND_FILE_OUTSIDE",
25773
26135
  message: "--artifact \u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
25774
26136
  }) : void 0;
25775
26137
  if (filePath) {
25776
26138
  const absolute = path13.resolve(input2.cwd, filePath);
25777
- if (!existsSync10(absolute)) {
26139
+ if (!existsSync11(absolute)) {
25778
26140
  throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${absolute}`, "ARTIFACT_ANCHOR_MISSING");
25779
26141
  }
25780
26142
  }
@@ -25791,23 +26153,34 @@ async function bindResource(input2) {
25791
26153
  if (!isResourceSubject(info2.subjectType)) {
25792
26154
  throw new CliError("\u53EA\u80FD bind \u5355\u8D44\u6E90", "BIND_SUBJECT_INVALID");
25793
26155
  }
25794
- if (info2.userId !== auth.userId) {
25795
- throw new CliError("\u53EA\u80FD bind \u81EA\u5DF1\u7684\u8D44\u6E90", "BIND_NOT_OWNER");
25796
- }
25797
26156
  const resourceId = String(info2.resourceId ?? "");
25798
- const name = String(info2.resourceName ?? info2.name ?? "").split("/").pop() ?? "";
26157
+ if (!resourceId) {
26158
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
26159
+ }
26160
+ assertRemoteResourceOwned({
26161
+ info: info2,
26162
+ resourceId,
26163
+ authUserId: auth.userId,
26164
+ codes: {
26165
+ invalid: "BIND_INFO_INVALID",
26166
+ notOwner: "BIND_NOT_OWNER"
26167
+ }
26168
+ });
26169
+ const resourceName = String(info2.resourceName ?? "");
26170
+ const name = (resourceName || String(info2.name ?? "")).split("/").pop() ?? "";
25799
26171
  const title = String(info2.resourceTitle ?? info2.title ?? name);
25800
26172
  const resourceType = info2.resourceType;
25801
26173
  const typeFromArray = Array.isArray(resourceType) ? String(resourceType[0] ?? "") : "";
25802
26174
  const typeCode = String(info2.resourceTypeCode ?? typeFromArray);
25803
- if (!resourceId || !name || !typeCode || !title) {
26175
+ if (!name || !typeCode || !title) {
25804
26176
  throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
25805
26177
  }
26178
+ const stableResourceName = resourceName || `${auth.loginName}/${name}`;
25806
26179
  return withProjectLock(input2.cwd, () => {
25807
26180
  const identities = validateLocalState(input2.cwd);
25808
26181
  const byId = identities.find((item) => item.resourceId === resourceId);
25809
26182
  const byFile = filePath ? identities.find(
25810
- (item) => item.filePath !== void 0 && normalizeFileKey(item.filePath) === filePath
26183
+ (item) => normalizeProjectPath(input2.cwd, item.filePath) === filePath
25811
26184
  ) : void 0;
25812
26185
  const unbound = identities.filter((item) => !item.resourceId);
25813
26186
  if (byFile && byId && byFile.n !== byId.n) {
@@ -25817,7 +26190,10 @@ async function bindResource(input2) {
25817
26190
  if (input2.selector && byId && target && target.n !== byId.n) {
25818
26191
  throw new CliError(`\u8D44\u6E90\u5DF2\u7ED1\u5B9A\u5230 ${byId.n}.json`, "BIND_ID_TAKEN");
25819
26192
  }
25820
- if (!target && unbound.length === 1) {
26193
+ if (!target && filePath) {
26194
+ target = unbound.find((item) => item.filePath === filePath);
26195
+ }
26196
+ if (!target && filePath === void 0 && unbound.length === 1) {
25821
26197
  target = unbound[0];
25822
26198
  }
25823
26199
  if (!target && unbound.length > 1) throw new CliError("\u5F53\u524D\u5DE5\u7A0B\u6709\u591A\u4EFD\u672A\u7ED1\u5B9A\u8D44\u6E90\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 --resource \u6307\u5B9A\u8D44\u6E90", "IDENTITY_RESOURCE_REQUIRED");
@@ -25828,9 +26204,15 @@ async function bindResource(input2) {
25828
26204
  if (!resolvedFilePath) {
25829
26205
  throw new CliError("\u65B0\u589E\u672C\u5730\u72B6\u6001\u65F6\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "BIND_ARTIFACT_REQUIRED");
25830
26206
  }
25831
- assertArtifactAnchor(typeCode, path13.resolve(input2.cwd, resolvedFilePath));
26207
+ assertArtifactAnchor(typeCode, path13.resolve(input2.cwd, resolvedFilePath), input2.cwd);
25832
26208
  const env = getEnv2();
25833
26209
  if (target) {
26210
+ if (target.resourceId && (target.env ?? "prod") !== env) {
26211
+ throw new CliError(
26212
+ `\u4E0D\u80FD\u628A ${target.n}.json\uFF08${target.env ?? "prod"}\uFF09\u6539\u7ED1\u5230 ${env} \u73AF\u5883\uFF1B\u8BF7\u65B0\u5EFA\u6216\u9009\u62E9\u8BE5\u73AF\u5883\u7684\u672C\u5730\u8D44\u6E90\u72B6\u6001`,
26213
+ "BIND_ENV_MISMATCH"
26214
+ );
26215
+ }
25834
26216
  const resourceChanged = target.resourceId !== resourceId;
25835
26217
  const typeChanged = target.typeCode !== typeCode;
25836
26218
  if (target.resourceId && resourceChanged) {
@@ -25841,6 +26223,7 @@ async function bindResource(input2) {
25841
26223
  const discardDraft = target.resourceId && resourceChanged || typeChanged;
25842
26224
  const updated = prepareIdentityUpdate(input2.cwd, target.n, {
25843
26225
  resourceId,
26226
+ resourceName: stableResourceName,
25844
26227
  name,
25845
26228
  title,
25846
26229
  typeCode,
@@ -25856,6 +26239,7 @@ async function bindResource(input2) {
25856
26239
  const created = prepareIdentityCreate(input2.cwd, {
25857
26240
  subject: "resource",
25858
26241
  resourceId,
26242
+ resourceName: stableResourceName,
25859
26243
  name,
25860
26244
  title,
25861
26245
  typeCode,
@@ -25863,7 +26247,8 @@ async function bindResource(input2) {
25863
26247
  env
25864
26248
  });
25865
26249
  commitLocalTransaction(input2.cwd, [
25866
- { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) }
26250
+ { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) },
26251
+ { path: identitySequenceFilePath(input2.cwd), content: serializeIdentitySequence(created.n) }
25867
26252
  ]);
25868
26253
  return created;
25869
26254
  });
@@ -25911,7 +26296,7 @@ import { Command as Command11 } from "commander";
25911
26296
 
25912
26297
  // src/domain/init/scaffold.ts
25913
26298
  import { execFile as execFileCallback } from "child_process";
25914
- import { cpSync, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync2, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync8, renameSync, rmSync } from "fs";
26299
+ import { cpSync, existsSync as existsSync12, lstatSync as lstatSync2, mkdirSync as mkdirSync2, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync9, renameSync, rmSync } from "fs";
25915
26300
  import os4 from "os";
25916
26301
  import path14 from "path";
25917
26302
  import { promisify } from "util";
@@ -25943,7 +26328,7 @@ function resolveTargetDir(input2) {
25943
26328
  return input2.dir ? path14.isAbsolute(input2.dir) ? input2.dir : path14.resolve(input2.cwd, input2.dir) : path14.resolve(input2.cwd);
25944
26329
  }
25945
26330
  function targetHasOnlyAuthSelector(targetDir) {
25946
- if (!existsSync11(targetDir)) return false;
26331
+ if (!existsSync12(targetDir)) return false;
25947
26332
  let entries;
25948
26333
  try {
25949
26334
  entries = readdirSync4(targetDir);
@@ -25956,20 +26341,20 @@ function targetHasOnlyAuthSelector(targetDir) {
25956
26341
  return stateDirHasOnlyAuthSelector(stateDir);
25957
26342
  }
25958
26343
  function stateDirHasOnlyAuthSelector(stateDir) {
25959
- if (!existsSync11(stateDir) || !lstatSync(stateDir).isDirectory() || lstatSync(stateDir).isSymbolicLink()) return false;
26344
+ if (!existsSync12(stateDir) || !lstatSync2(stateDir).isDirectory() || lstatSync2(stateDir).isSymbolicLink()) return false;
25960
26345
  const stateEntries = readdirSync4(stateDir);
25961
26346
  if (stateEntries.length !== 1 || stateEntries[0] !== "auth") return false;
25962
- const auth = lstatSync(path14.join(stateDir, "auth"));
26347
+ const auth = lstatSync2(path14.join(stateDir, "auth"));
25963
26348
  return auth.isFile() && !auth.isSymbolicLink();
25964
26349
  }
25965
26350
  function assertInitialTarget(targetDir, adoptExistingProject) {
25966
- if (!existsSync11(targetDir)) return { targetExisted: false, preserveAuth: false, adoptExistingProject };
26351
+ if (!existsSync12(targetDir)) return { targetExisted: false, preserveAuth: false, adoptExistingProject };
25967
26352
  const entries = readdirSync4(targetDir);
25968
26353
  if (entries.length === 0) return { targetExisted: true, preserveAuth: false, adoptExistingProject };
25969
26354
  if (targetHasOnlyAuthSelector(targetDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject };
25970
26355
  if (adoptExistingProject) {
25971
26356
  const stateDir = path14.join(targetDir, ".freelog");
25972
- if (!existsSync11(stateDir)) return { targetExisted: true, preserveAuth: false, adoptExistingProject: true };
26357
+ if (!existsSync12(stateDir)) return { targetExisted: true, preserveAuth: false, adoptExistingProject: true };
25973
26358
  if (stateDirHasOnlyAuthSelector(stateDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject: true };
25974
26359
  }
25975
26360
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u4E0D\u662F\u7A7A\u76EE\u5F55\uFF0C\u62D2\u7EDD\u8986\u76D6", "INIT_TARGET_NOT_EMPTY");
@@ -25997,12 +26382,12 @@ function validateRemoteTemplate(packageDir, template) {
25997
26382
  const templateDir = path14.join(packageDir, "template");
25998
26383
  let manifest;
25999
26384
  try {
26000
- manifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
26385
+ manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
26001
26386
  } catch {
26002
26387
  throw new CliError("\u6A21\u677F\u6821\u9A8C\u5931\u8D25\uFF1A\u7F3A\u5C11\u6216\u65E0\u6CD5\u8BFB\u53D6 template.manifest.json", "TEMPLATE_INVALID");
26003
26388
  }
26004
26389
  const tags = Array.isArray(manifest.tags) ? manifest.tags : [];
26005
- if (manifest.id !== template.id || manifest.npmName !== template.npmName || manifest.version !== template.version || !tags.includes("runtime") || !template.targets.every((target) => tags.includes(target)) || !existsSync11(templateDir)) {
26390
+ if (manifest.id !== template.id || manifest.npmName !== template.npmName || manifest.version !== template.version || !tags.includes("runtime") || !template.targets.every((target) => tags.includes(target)) || !existsSync12(templateDir)) {
26006
26391
  throw new CliError("\u6A21\u677F\u6821\u9A8C\u5931\u8D25\uFF1A\u7EBF\u4E0A\u6A21\u677F\u4E0E\u53D7\u63A7\u6E05\u5355\u4E0D\u4E00\u81F4", "TEMPLATE_INVALID");
26007
26392
  }
26008
26393
  return templateDir;
@@ -26040,7 +26425,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26040
26425
  if (preserveAuth) {
26041
26426
  assertOnlyAuthSelectorStillThere(targetDir);
26042
26427
  } else if (adoptExistingProject) {
26043
- if (existsSync11(path14.join(targetDir, ".freelog"))) {
26428
+ if (existsSync12(path14.join(targetDir, ".freelog"))) {
26044
26429
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
26045
26430
  }
26046
26431
  renameSync(path14.join(stagingDir, ".freelog"), path14.join(targetDir, ".freelog"));
@@ -26054,7 +26439,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26054
26439
  if (preserveAuth && entry2 === ".freelog") {
26055
26440
  for (const stateEntry of readdirSync4(path14.join(stagingDir, entry2))) {
26056
26441
  const stateDestination = path14.join(destination, stateEntry);
26057
- if (existsSync11(stateDestination)) {
26442
+ if (existsSync12(stateDestination)) {
26058
26443
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
26059
26444
  }
26060
26445
  renameSync(path14.join(stagingDir, entry2, stateEntry), stateDestination);
@@ -26062,7 +26447,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26062
26447
  rmSync(path14.join(stagingDir, entry2), { recursive: true, force: true });
26063
26448
  continue;
26064
26449
  }
26065
- if (existsSync11(destination)) {
26450
+ if (existsSync12(destination)) {
26066
26451
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
26067
26452
  }
26068
26453
  renameSync(path14.join(stagingDir, entry2), destination);
@@ -26086,7 +26471,7 @@ async function initProject(input2) {
26086
26471
  }
26087
26472
  const typeCode = shortcut === "theme" ? "RT001" : shortcut === "widget" ? "RT002" : (await (input2.typeValidator ?? ((code) => getTypeInfo(code, input2.typeApis)))(input2.typeCode)).code;
26088
26473
  const artifact = shortcut ? "dist" : normalizeProjectPath(targetDir, input2.artifact);
26089
- if (!shortcut) assertArtifactAnchor(typeCode, path14.resolve(targetDir, artifact));
26474
+ if (!shortcut) assertArtifactAnchor(typeCode, path14.resolve(targetDir, artifact), targetDir);
26090
26475
  const template = shortcut && input2.template ? getTemplate(input2.template, shortcut) : void 0;
26091
26476
  if (shortcut && !template) throw new CliError("\u8BF7\u9009\u62E9\u6A21\u677F", "INIT_TEMPLATE_REQUIRED");
26092
26477
  const parent = path14.dirname(targetDir);
@@ -26188,6 +26573,7 @@ async function statusProject(input2) {
26188
26573
  if (local?.resourceId) {
26189
26574
  assertPlatformAllowed();
26190
26575
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26576
+ resolveBoundIdentity(input2.cwd, input2.file);
26191
26577
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26192
26578
  const result = await infoApi2({
26193
26579
  resourceIdOrName: local.resourceId,
@@ -26298,7 +26684,7 @@ function createTypeCommand() {
26298
26684
  return;
26299
26685
  }
26300
26686
  const info2 = await getTypeInfo(options.type);
26301
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26687
+ console.log(formatTypeInfo(info2));
26302
26688
  });
26303
26689
  addSharedOptions(type.command("info")).description(
26304
26690
  // i18n: cli.command.type.info.description
@@ -26313,7 +26699,7 @@ function createTypeCommand() {
26313
26699
  throw new CliError("\u8BF7\u63D0\u4F9B\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
26314
26700
  }
26315
26701
  const info2 = await getTypeInfo(code);
26316
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26702
+ console.log(formatTypeInfo(info2));
26317
26703
  });
26318
26704
  return type;
26319
26705
  }
@@ -26340,6 +26726,9 @@ async function syncResourceTitles(input2) {
26340
26726
  if (!identity.resourceId) return { identity, error: "\u8D44\u6E90\u5C1A\u672A\u7ED1\u5B9A" };
26341
26727
  try {
26342
26728
  const data = unwrapData2(await info2({ resourceIdOrName: identity.resourceId, isLoadLatestVersionInfo: 1 }));
26729
+ if (data.resourceId !== identity.resourceId) {
26730
+ return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId" };
26731
+ }
26343
26732
  const title = String(data.resourceTitle ?? data.title ?? "");
26344
26733
  if (!title) return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6807\u9898" };
26345
26734
  return { identity, title };
@@ -26363,76 +26752,367 @@ async function syncResourceTitles(input2) {
26363
26752
  }, "sync-resource-titles");
26364
26753
  }
26365
26754
 
26366
- // src/commands/project/resource.ts
26367
- function createResourceCommand() {
26368
- const resource = addSharedOptions(new Command15("resource")).description("\u8D44\u6E90\u672C\u5730\u72B6\u6001\u7EF4\u62A4");
26369
- resource.command("sync").description("\u4ECE\u5E73\u53F0\u540C\u6B65\u672C\u5730\u8D44\u6E90\u6807\u9898").action(async function() {
26370
- const shared = readSharedOptions(this);
26371
- console.log(await syncResourceTitles({ cwd: resolveCwd(shared.cwd), selector: shared.file }));
26372
- });
26373
- return resource;
26755
+ // src/local/pendingOperation.ts
26756
+ import { randomUUID as randomUUID2 } from "crypto";
26757
+ import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync as unlinkSync6 } from "fs";
26758
+ import path15 from "path";
26759
+ import { z as z4 } from "zod";
26760
+ var pendingVersionSubmitV1Schema = z4.object({
26761
+ schemaVersion: z4.literal(1),
26762
+ operationId: z4.string().uuid(),
26763
+ kind: z4.literal("version-submit"),
26764
+ resourceN: z4.number().int().positive(),
26765
+ resourceId: z4.string().min(1),
26766
+ env: z4.enum(["prod", "test", "dev"]),
26767
+ version: z4.string().min(1),
26768
+ fileSha1: z4.string().min(1),
26769
+ createdAt: z4.string().min(1)
26770
+ }).strict();
26771
+ var pendingVersionSubmitV2Schema = pendingVersionSubmitV1Schema.omit({ schemaVersion: true }).extend({
26772
+ schemaVersion: z4.literal(2),
26773
+ state: z4.enum(["prepared", "sending"])
26774
+ }).strict();
26775
+ function pendingOperationFilePath(cwd) {
26776
+ return path15.join(freelogDir(cwd), ".pending-operation.json");
26374
26777
  }
26375
-
26376
- // src/commands/shelf/offline.ts
26377
- import { Command as Command16 } from "commander";
26378
-
26379
- // src/domain/online/online.ts
26380
- async function loadInfo(resourceId, apis) {
26381
- const infoApi2 = apis?.info ?? ((params) => service_API_default.Resource.info(params));
26382
- return unwrapData2(
26383
- await infoApi2({
26384
- resourceIdOrName: resourceId,
26385
- isLoadLatestVersionInfo: 1,
26386
- isLoadPolicyInfo: 1
26387
- })
26388
- );
26778
+ function invalidPending() {
26779
+ throw new CliError("\u672A\u51B3\u8FDC\u7AEF\u64CD\u4F5C\u8BB0\u5F55\u65E0\u6548\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "PENDING_OPERATION_INVALID");
26389
26780
  }
26390
- function validateForOnline(info2) {
26391
- if (!info2.latestVersion) {
26392
- throw new CliError("\u4E0A\u67B6\u987B\u5DF2\u6709\u7248\u672C", "ONLINE_NO_VERSION");
26781
+ function readPendingOperation(cwd) {
26782
+ const filePath = pendingOperationFilePath(cwd);
26783
+ if (!existsSync13(filePath)) return void 0;
26784
+ let raw;
26785
+ try {
26786
+ raw = JSON.parse(readFileSync10(filePath, "utf8"));
26787
+ } catch {
26788
+ return invalidPending();
26393
26789
  }
26394
- const policies2 = info2.policies ?? [];
26395
- if (!policies2.some((item) => item.status === 1)) {
26396
- throw new CliError("\u4E0A\u67B6\u987B\u81F3\u5C11\u4E00\u6761\u542F\u7528\u7B56\u7565", "ONLINE_NO_POLICY");
26790
+ const v2 = pendingVersionSubmitV2Schema.safeParse(raw);
26791
+ if (v2.success) return v2.data;
26792
+ const v1 = pendingVersionSubmitV1Schema.safeParse(raw);
26793
+ if (v1.success) {
26794
+ return { ...v1.data, state: "sending" };
26397
26795
  }
26796
+ return invalidPending();
26398
26797
  }
26399
- async function onlineResource(input2) {
26400
- assertPlatformAllowed();
26401
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26402
- const identity = resolveBoundIdentity(input2.cwd, input2.file);
26403
- const info2 = await loadInfo(identity.resourceId, input2.apis);
26404
- validateForOnline(info2);
26405
- const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
26406
- await update2({
26407
- resourceId: identity.resourceId,
26408
- status: 1
26409
- });
26798
+ function createPendingVersionSubmit(input2) {
26799
+ if (readPendingOperation(input2.cwd)) {
26800
+ throw new CliError("\u5B58\u5728\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C\uFF1B\u8BF7\u5148 resource recover", "PENDING_OPERATION_EXISTS");
26801
+ }
26802
+ const pending = {
26803
+ schemaVersion: 2,
26804
+ operationId: randomUUID2(),
26805
+ kind: "version-submit",
26806
+ resourceN: input2.resourceN,
26807
+ resourceId: input2.resourceId,
26808
+ env: input2.env,
26809
+ version: input2.version,
26810
+ fileSha1: input2.fileSha1,
26811
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
26812
+ state: "prepared"
26813
+ };
26814
+ atomicWriteFile(pendingOperationFilePath(input2.cwd), `${JSON.stringify(pending, null, 2)}
26815
+ `);
26816
+ return pending;
26410
26817
  }
26411
- async function offlineResource(input2) {
26412
- assertPlatformAllowed();
26413
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26414
- const identity = resolveBoundIdentity(input2.cwd, input2.file);
26415
- const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
26416
- await update2({
26417
- resourceId: identity.resourceId,
26418
- status: 4
26419
- });
26818
+ function markPendingVersionSubmitSending(cwd) {
26819
+ const pending = readPendingOperation(cwd);
26820
+ if (!pending || pending.state !== "prepared") {
26821
+ throw new CliError("\u672A\u51B3\u7248\u672C\u63D0\u4EA4\u4E0D\u5904\u4E8E\u53EF\u53D1\u9001\u72B6\u6001\uFF0C\u62D2\u7EDD\u8C03\u7528\u5E73\u53F0", "PENDING_OPERATION_STATE_INVALID");
26822
+ }
26823
+ const sending = {
26824
+ ...pending,
26825
+ schemaVersion: 2,
26826
+ state: "sending"
26827
+ };
26828
+ atomicWriteFile(pendingOperationFilePath(cwd), `${JSON.stringify(sending, null, 2)}
26829
+ `);
26830
+ return sending;
26420
26831
  }
26421
-
26422
- // src/commands/shelf/offline.ts
26423
- function createOfflineCommand() {
26424
- const command = addSharedOptions(new Command16("offline"));
26425
- command.description(
26426
- // i18n: cli.command.offline.description
26427
- "\u4E0B\u67B6"
26428
- ).action(async function() {
26429
- const shared = readSharedOptions(this);
26430
- await offlineResource({
26431
- cwd: resolveCwd(shared.cwd),
26432
- file: shared.file
26433
- });
26434
- });
26435
- return command;
26832
+ function clearPendingOperation(cwd) {
26833
+ const filePath = pendingOperationFilePath(cwd);
26834
+ if (!existsSync13(filePath)) return false;
26835
+ unlinkSync6(filePath);
26836
+ return true;
26837
+ }
26838
+ function assertNoPendingOperation(cwd) {
26839
+ const pending = readPendingOperation(cwd);
26840
+ if (pending) {
26841
+ throw new CliError(
26842
+ `\u5B58\u5728\u672A\u5B8C\u6210\u6536\u5C3E\u7684\u7248\u672C\u63D0\u4EA4\uFF08${pending.resourceId} ${pending.version}\uFF0C${pending.state}\uFF09\uFF1B\u8BF7\u5148 resource recover`,
26843
+ "PENDING_OPERATION_EXISTS"
26844
+ );
26845
+ }
26846
+ }
26847
+
26848
+ // src/domain/resource/recover.ts
26849
+ async function recoverPendingOperation(input2) {
26850
+ return withProjectLock(input2.cwd, () => recoverPendingOperationLocked(input2), "resource-recover");
26851
+ }
26852
+ async function recoverPendingOperationLocked(input2) {
26853
+ assertPlatformAllowed();
26854
+ const pending = readPendingOperation(input2.cwd);
26855
+ if (!pending) return "\u6CA1\u6709\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C";
26856
+ const activeEnv2 = getEnv2();
26857
+ if (pending.env !== activeEnv2) {
26858
+ throw new CliError(
26859
+ `\u672A\u51B3\u64CD\u4F5C\u5C5E\u4E8E ${pending.env} \u73AF\u5883\uFF0C\u672C\u6B21\u662F ${activeEnv2}\uFF1B\u8BF7\u5207\u6362 --env \u540E\u6062\u590D`,
26860
+ "PENDING_OPERATION_ENV_MISMATCH"
26861
+ );
26862
+ }
26863
+ const identity = readIdentity(input2.cwd, pending.resourceN);
26864
+ if (identity.resourceId !== pending.resourceId || (identity.env ?? "prod") !== pending.env) {
26865
+ throw new CliError("\u672A\u51B3\u64CD\u4F5C\u4E0E\u5F53\u524D N.json \u8EAB\u4EFD\u6216\u73AF\u5883\u4E0D\u4E00\u81F4\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6E05\u7406", "PENDING_OPERATION_LOCAL_MISMATCH");
26866
+ }
26867
+ const draft = readDraft(input2.cwd, identity.n);
26868
+ if (draft?.resourceId && draft.resourceId !== pending.resourceId) {
26869
+ throw new CliError("\u672A\u51B3\u64CD\u4F5C\u4E0E\u5F53\u524D\u5DE5\u4F5C\u7A3F\u4E0D\u4E00\u81F4\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6E05\u7406", "PENDING_OPERATION_LOCAL_MISMATCH");
26870
+ }
26871
+ if (pending.state === "prepared") {
26872
+ if (!input2.apply) {
26873
+ return `\u5C1A\u672A\u53D1\u9001\uFF1A${pending.resourceId} ${pending.version} \u7684\u63D0\u4EA4\u8BF7\u6C42\u5C1A\u672A\u5141\u8BB8\u53D1\u51FA\uFF1B\u5DE5\u4F5C\u7A3F\u5DF2\u4FDD\u7559\u3002\u8FD0\u884C resource recover --apply --yes \u4EC5\u6E05\u7406\u672A\u51B3\u8BB0\u5F55\u540E\u53EF\u91CD\u65B0\u63D0\u4EA4`;
26874
+ }
26875
+ if (!input2.yes) {
26876
+ throw new CliError("\u5E94\u7528\u6062\u590D\u4F1A\u6E05\u7406\u672A\u51B3\u8BB0\u5F55\uFF1B\u8BF7\u4F7F\u7528 resource recover --apply --yes", "RECOVER_APPLY_NEED_YES");
26877
+ }
26878
+ commitLocalTransaction(input2.cwd, [
26879
+ { path: pendingOperationFilePath(input2.cwd), content: null }
26880
+ ]);
26881
+ return `\u5DF2\u5B8C\u6210\u6062\u590D\uFF1A${pending.resourceId} ${pending.version} \u7684\u672A\u51B3\u8BB0\u5F55\u5DF2\u6E05\u7406\uFF0C\u5DE5\u4F5C\u7A3F\u5DF2\u4FDD\u7559`;
26882
+ }
26883
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26884
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26885
+ const info2 = unwrapData2(await infoApi2({ resourceIdOrName: pending.resourceId }));
26886
+ assertRemoteResourceOwned({
26887
+ info: info2,
26888
+ resourceId: pending.resourceId,
26889
+ authUserId: auth.userId,
26890
+ codes: {
26891
+ invalid: "RECOVER_RESOURCE_INFO_INVALID",
26892
+ notOwner: "RECOVER_NOT_OWNER"
26893
+ }
26894
+ });
26895
+ const versionInfoApi = input2.apis?.resourceVersionInfo1 ?? ((params) => service_API_default.Resource.resourceVersionInfo1(params));
26896
+ const versionInfo = unwrapData2(await versionInfoApi({
26897
+ resourceId: pending.resourceId,
26898
+ version: pending.version
26899
+ }));
26900
+ const remoteSha = typeof versionInfo.fileSha1 === "string" ? versionInfo.fileSha1 : "";
26901
+ if (!remoteSha) {
26902
+ if (input2.apply) {
26903
+ throw new CliError("\u7EBF\u4E0A\u5C1A\u672A\u8BC1\u660E\u76EE\u6807\u7248\u672C\u5B58\u5728\uFF0C\u4E0D\u80FD\u5E94\u7528\u6062\u590D", "RECOVER_NOT_CONFIRMED");
26904
+ }
26905
+ return `\u672A\u786E\u8BA4\uFF1A\u7EBF\u4E0A\u4E0D\u5B58\u5728 ${pending.resourceId} \u7684\u7248\u672C ${pending.version}\uFF1B\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4`;
26906
+ }
26907
+ if (remoteSha !== pending.fileSha1) {
26908
+ if (input2.apply) {
26909
+ throw new CliError("\u7EBF\u4E0A\u76EE\u6807\u7248\u672C\u7684\u6587\u4EF6 SHA \u4E0D\u5339\u914D\uFF0C\u4E0D\u80FD\u5E94\u7528\u6062\u590D", "RECOVER_NOT_CONFIRMED");
26910
+ }
26911
+ return `\u51B2\u7A81\uFF1A\u7EBF\u4E0A ${pending.version} \u7684\u6587\u4EF6 SHA \u4E0E\u672A\u51B3\u8BB0\u5F55\u4E0D\u540C\uFF1B\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4`;
26912
+ }
26913
+ if (!input2.apply) {
26914
+ return `\u5DF2\u786E\u8BA4\uFF1A${pending.resourceId} ${pending.version} \u5DF2\u4EE5\u76F8\u540C SHA \u63D0\u4EA4\u3002\u8FD0\u884C resource recover --apply --yes \u6E05\u7406\u672C\u5730\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55`;
26915
+ }
26916
+ if (!input2.yes) {
26917
+ throw new CliError("\u5E94\u7528\u6062\u590D\u4F1A\u5220\u9664\u672C\u5730\u5DE5\u4F5C\u7A3F\uFF1B\u8BF7\u4F7F\u7528 resource recover --apply --yes", "RECOVER_APPLY_NEED_YES");
26918
+ }
26919
+ commitLocalTransaction(input2.cwd, [
26920
+ { path: pendingOperationFilePath(input2.cwd), content: null },
26921
+ { path: draftFilePath(input2.cwd, identity.n), content: null }
26922
+ ]);
26923
+ return `\u5DF2\u5B8C\u6210\u6062\u590D\uFF1A${pending.resourceId} ${pending.version} \u7684\u672C\u5730\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\u5DF2\u6E05\u7406`;
26924
+ }
26925
+
26926
+ // src/local/diagnostics.ts
26927
+ import { existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
26928
+ var IDENTITY_FILE_RE2 = /^([1-9]\d*)\.json$/;
26929
+ var DRAFT_FILE_RE2 = /^([1-9]\d*)\.version\.json$/;
26930
+ function errorMessage(error2) {
26931
+ return error2 instanceof Error ? error2.message : "\u672A\u77E5\u9519\u8BEF";
26932
+ }
26933
+ function duplicateDiagnostics(cwd, identities) {
26934
+ const result = [];
26935
+ for (const key of ["resourceId", "resourceName", "filePath"]) {
26936
+ const seen = /* @__PURE__ */ new Map();
26937
+ for (const identity of identities) {
26938
+ const raw = identity[key];
26939
+ if (!raw) continue;
26940
+ let value;
26941
+ try {
26942
+ value = key === "filePath" ? normalizeProjectPath(cwd, raw) : raw;
26943
+ } catch (error2) {
26944
+ result.push({ file: `${identity.n}.json`, code: "IDENTITY_PATH_INVALID", message: errorMessage(error2) });
26945
+ continue;
26946
+ }
26947
+ const previous = seen.get(value);
26948
+ if (previous !== void 0) {
26949
+ result.push({
26950
+ file: `${identity.n}.json`,
26951
+ code: "IDENTITY_CONFLICT",
26952
+ message: `\u4E0E ${previous}.json \u4F7F\u7528\u76F8\u540C ${key}`
26953
+ });
26954
+ } else {
26955
+ seen.set(value, identity.n);
26956
+ }
26957
+ }
26958
+ }
26959
+ return result;
26960
+ }
26961
+ function diagnoseLocalState(cwd) {
26962
+ const dir = freelogDir(cwd);
26963
+ if (!existsSync14(dir)) return "\u6CA1\u6709 .freelog \u672C\u5730\u72B6\u6001";
26964
+ let entries;
26965
+ try {
26966
+ entries = readdirSync5(dir).sort((left, right) => left.localeCompare(right, "en"));
26967
+ } catch (error2) {
26968
+ return `\u65E0\u6CD5\u8BFB\u53D6 .freelog\uFF1A${errorMessage(error2)}`;
26969
+ }
26970
+ const lines = [];
26971
+ const diagnostics = [];
26972
+ const identityNumbers = /* @__PURE__ */ new Set();
26973
+ const validIdentities = [];
26974
+ for (const file of entries) {
26975
+ const match = IDENTITY_FILE_RE2.exec(file);
26976
+ if (!match) continue;
26977
+ const n = Number(match[1]);
26978
+ identityNumbers.add(n);
26979
+ try {
26980
+ const identity = readIdentity(cwd, n);
26981
+ validIdentities.push(identity);
26982
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}\uFF0C${identity.typeCode}\uFF0C${identity.filePath}\uFF09`);
26983
+ } catch (error2) {
26984
+ diagnostics.push({ file, code: "IDENTITY_INVALID", message: errorMessage(error2) });
26985
+ }
26986
+ }
26987
+ for (const file of entries) {
26988
+ const match = DRAFT_FILE_RE2.exec(file);
26989
+ if (!match) continue;
26990
+ const n = Number(match[1]);
26991
+ if (!identityNumbers.has(n)) {
26992
+ diagnostics.push({ file, code: "DRAFT_ORPHAN", message: `\u6CA1\u6709\u540C\u53F7 ${n}.json` });
26993
+ continue;
26994
+ }
26995
+ if (!validIdentities.some((identity) => identity.n === n)) {
26996
+ diagnostics.push({ file, code: "DRAFT_IDENTITY_INVALID", message: `\u540C\u53F7 ${n}.json \u5DF2\u635F\u574F\uFF0C\u65E0\u6CD5\u6838\u9A8C\u5DE5\u4F5C\u7A3F` });
26997
+ continue;
26998
+ }
26999
+ try {
27000
+ const draft = readDraft(cwd, n);
27001
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${draft?.draftKind ?? "\u65E0"} \u5DE5\u4F5C\u7A3F\uFF09`);
27002
+ } catch (error2) {
27003
+ diagnostics.push({ file, code: "DRAFT_INVALID", message: errorMessage(error2) });
27004
+ }
27005
+ }
27006
+ diagnostics.push(...duplicateDiagnostics(cwd, validIdentities));
27007
+ if (entries.includes("index.json")) {
27008
+ lines.push("index.json\uFF1A\u5386\u53F2\u9057\u7559\u6587\u4EF6\uFF0C\u5F53\u524D CLI \u4E0D\u8BFB\u53D6\u3001\u4E0D\u91CD\u5EFA\u4E5F\u4E0D\u81EA\u52A8\u5220\u9664");
27009
+ }
27010
+ try {
27011
+ const pending = readPendingOperation(cwd);
27012
+ if (pending) lines.push(`.pending-operation.json\uFF1A\u672A\u51B3 ${pending.kind}\uFF08${pending.resourceId} ${pending.version}\uFF09`);
27013
+ } catch (error2) {
27014
+ diagnostics.push({ file: ".pending-operation.json", code: "PENDING_OPERATION_INVALID", message: errorMessage(error2) });
27015
+ }
27016
+ if (entries.includes(".txn.json")) {
27017
+ lines.push(".txn.json\uFF1A\u5B58\u5728\uFF1B\u4E0B\u4E00\u6B21\u6301\u9501\u4E1A\u52A1\u547D\u4EE4\u4F1A\u5C1D\u8BD5\u6062\u590D\uFF0C\u8BCA\u65AD\u547D\u4EE4\u4E0D\u4F1A\u56DE\u653E");
27018
+ }
27019
+ if (lines.length === 0) lines.push("\u6CA1\u6709\u53EF\u8BC6\u522B\u7684\u8D44\u6E90\u8EAB\u4EFD\u6216\u5DE5\u4F5C\u7A3F");
27020
+ if (diagnostics.length === 0) return `\u672C\u5730\u72B6\u6001\u6B63\u5E38\uFF1A
27021
+ ${lines.map((line) => `- ${line}`).join("\n")}`;
27022
+ return [
27023
+ "\u672C\u5730\u72B6\u6001\u5B58\u5728\u95EE\u9898\uFF08\u672A\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\uFF09\uFF1A",
27024
+ ...lines.map((line) => `- ${line}`),
27025
+ ...diagnostics.map((item) => `- ${item.file} [${item.code}]\uFF1A${item.message}`),
27026
+ "\u8BF7\u5148\u5907\u4EFD .freelog\uFF1B\u4FEE\u590D\u6216\u5220\u9664\u660E\u786E\u65E0\u7528\u7684\u5B8C\u6574\u72B6\u6001\u5355\u5143\u540E\u518D\u6267\u884C\u4E1A\u52A1\u547D\u4EE4\u3002"
27027
+ ].join("\n");
27028
+ }
27029
+
27030
+ // src/commands/project/resource.ts
27031
+ function createResourceCommand() {
27032
+ const resource = addSharedOptions(new Command15("resource")).description("\u8D44\u6E90\u672C\u5730\u72B6\u6001\u7EF4\u62A4");
27033
+ resource.command("list").description("\u53EA\u8BFB\u8BCA\u65AD\u672C\u5730\u8D44\u6E90\u72B6\u6001\uFF08\u635F\u574F\u65F6\u4E5F\u53EF\u8FD0\u884C\uFF09").action(function() {
27034
+ const shared = readSharedOptions(this);
27035
+ console.log(diagnoseLocalState(resolveCwd(shared.cwd)));
27036
+ });
27037
+ resource.command("sync").description("\u4ECE\u5E73\u53F0\u540C\u6B65\u672C\u5730\u8D44\u6E90\u6807\u9898").action(async function() {
27038
+ const shared = readSharedOptions(this);
27039
+ console.log(await syncResourceTitles({ cwd: resolveCwd(shared.cwd), selector: shared.file }));
27040
+ });
27041
+ resource.command("recover").description("\u5904\u7406\u672A\u51B3\u7248\u672C\u63D0\u4EA4\uFF0C\u7EDD\u4E0D\u91CD\u53D1").option("--apply", "prepared \u4EC5\u6E05 marker\uFF1Bsending \u987B\u8FDC\u7AEF\u7248\u672C\u548C SHA \u5DF2\u8BC1\u660E\u6210\u529F\u624D\u6E05\u7406\u7A3F").action(async function() {
27042
+ const shared = readSharedOptions(this);
27043
+ const options = this.opts();
27044
+ console.log(await recoverPendingOperation({
27045
+ cwd: resolveCwd(shared.cwd),
27046
+ apply: options.apply === true,
27047
+ yes: shared.yes
27048
+ }));
27049
+ });
27050
+ return resource;
27051
+ }
27052
+
27053
+ // src/commands/shelf/offline.ts
27054
+ import { Command as Command16 } from "commander";
27055
+
27056
+ // src/domain/online/online.ts
27057
+ async function loadInfo(resourceId, apis) {
27058
+ const infoApi2 = apis?.info ?? ((params) => service_API_default.Resource.info(params));
27059
+ return unwrapData2(
27060
+ await infoApi2({
27061
+ resourceIdOrName: resourceId,
27062
+ isLoadLatestVersionInfo: 1,
27063
+ isLoadPolicyInfo: 1
27064
+ })
27065
+ );
27066
+ }
27067
+ function validateForOnline(info2) {
27068
+ if (!info2.latestVersion) {
27069
+ throw new CliError("\u4E0A\u67B6\u987B\u5DF2\u6709\u7248\u672C", "ONLINE_NO_VERSION");
27070
+ }
27071
+ const policies2 = info2.policies ?? [];
27072
+ if (!policies2.some((item) => item.status === 1)) {
27073
+ throw new CliError("\u4E0A\u67B6\u987B\u81F3\u5C11\u4E00\u6761\u542F\u7528\u7B56\u7565", "ONLINE_NO_POLICY");
27074
+ }
27075
+ }
27076
+ async function onlineResource(input2) {
27077
+ assertPlatformAllowed();
27078
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27079
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27080
+ const info2 = await loadInfo(identity.resourceId, input2.apis);
27081
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27082
+ validateForOnline(info2);
27083
+ const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
27084
+ await update2({
27085
+ resourceId: identity.resourceId,
27086
+ status: 1
27087
+ });
27088
+ }
27089
+ async function offlineResource(input2) {
27090
+ assertPlatformAllowed();
27091
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27092
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27093
+ const info2 = await loadInfo(identity.resourceId, input2.apis);
27094
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27095
+ const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
27096
+ await update2({
27097
+ resourceId: identity.resourceId,
27098
+ status: 4
27099
+ });
27100
+ }
27101
+
27102
+ // src/commands/shelf/offline.ts
27103
+ function createOfflineCommand() {
27104
+ const command = addSharedOptions(new Command16("offline"));
27105
+ command.description(
27106
+ // i18n: cli.command.offline.description
27107
+ "\u4E0B\u67B6"
27108
+ ).action(async function() {
27109
+ const shared = readSharedOptions(this);
27110
+ await offlineResource({
27111
+ cwd: resolveCwd(shared.cwd),
27112
+ file: shared.file
27113
+ });
27114
+ });
27115
+ return command;
26436
27116
  }
26437
27117
 
26438
27118
  // src/commands/shelf/online.ts
@@ -26461,7 +27141,7 @@ async function validateOnline(input2) {
26461
27141
  throw new CliError("\u76EE\u524D\u53EA\u652F\u6301 --for online", "VALIDATE_FOR");
26462
27142
  }
26463
27143
  assertPlatformAllowed();
26464
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27144
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26465
27145
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
26466
27146
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26467
27147
  const result = await infoApi2({
@@ -26470,6 +27150,11 @@ async function validateOnline(input2) {
26470
27150
  isLoadPolicyInfo: 1
26471
27151
  });
26472
27152
  const data = result.data ?? result;
27153
+ assertRemoteResourceWritable({
27154
+ info: data,
27155
+ resourceId: identity.resourceId,
27156
+ authUserId: auth.userId
27157
+ });
26473
27158
  validateForOnline(data);
26474
27159
  return "\u53EF\u4EE5\u4E0A\u67B6";
26475
27160
  }
@@ -26753,6 +27438,42 @@ function attrList(cwd, file) {
26753
27438
  );
26754
27439
  return list2.map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
26755
27440
  }
27441
+ function attrReview(cwd, file) {
27442
+ const identity = resolveIdentity(cwd, file);
27443
+ const draft = readDraft(cwd, identity.n);
27444
+ if (!draft) {
27445
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27446
+ }
27447
+ const items = draft.orphanedInputAttrs ?? [];
27448
+ if (items.length === 0) return "\u6CA1\u6709\u5F85\u590D\u6838\u7684\u9644\u52A0\u5C5E\u6027";
27449
+ return [
27450
+ "\u4EE5\u4E0B\u9644\u52A0\u5C5E\u6027\u5DF2\u4E0D\u5728\u5F53\u524D\u6587\u4EF6\u5206\u6790\u7ED3\u679C\u4E2D\uFF0C\u4E0D\u80FD\u63D0\u4EA4\uFF1A",
27451
+ ...items.map((item) => ` ${String(item.key ?? "\uFF08\u65E0\u952E\uFF09")}=${String(item.value ?? "")}`),
27452
+ "\u91CD\u65B0\u4E0A\u4F20\u5305\u542B\u8BE5 key \u7684\u6587\u4EF6\u4F1A\u81EA\u52A8\u6062\u590D\uFF1B\u786E\u8BA4\u4E0D\u518D\u9700\u8981\u53EF\u6267\u884C version attr review discard <key>\u3002"
27453
+ ].join("\n");
27454
+ }
27455
+ async function attrReviewDiscard(cwd, input2) {
27456
+ return withProjectLock(cwd, () => attrReviewDiscardLocked(cwd, input2), "version-attr-review-discard");
27457
+ }
27458
+ async function attrReviewDiscardLocked(cwd, input2) {
27459
+ const identity = resolveIdentity(cwd, input2.file);
27460
+ const draft = readDraft(cwd, identity.n);
27461
+ if (!draft) {
27462
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27463
+ }
27464
+ const key = input2.key.trim();
27465
+ const items = draft.orphanedInputAttrs ?? [];
27466
+ if (!key || !items.some((item) => item.key === key)) {
27467
+ throw new CliError("\u627E\u4E0D\u5230\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", "ATTR_REVIEW_NOT_FOUND");
27468
+ }
27469
+ const preview = `\u5373\u5C06\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027\uFF1A${key}`;
27470
+ if (!await confirmDestructive(preview, "\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", input2.yes)) {
27471
+ return "\u5DF2\u53D6\u6D88";
27472
+ }
27473
+ draft.orphanedInputAttrs = items.filter((item) => item.key !== key);
27474
+ writeDraft(cwd, identity.n, draft);
27475
+ return preview;
27476
+ }
26756
27477
 
26757
27478
  // src/commands/version/attr.ts
26758
27479
  function createVersionAttrCommand() {
@@ -26774,12 +27495,232 @@ function createVersionAttrCommand() {
26774
27495
  const shared = readSharedOptions(this);
26775
27496
  console.log(attrList(resolveCwd(shared.cwd), shared.file));
26776
27497
  });
27498
+ const review = addSharedOptions(attr.command("review")).description("\u67E5\u770B\u6216\u5904\u7406\u6587\u4EF6\u5206\u6790\u53D8\u5316\u540E\u7684\u9644\u52A0\u5C5E\u6027");
27499
+ review.action(function() {
27500
+ const shared = readSharedOptions(this);
27501
+ console.log(attrReview(resolveCwd(shared.cwd), shared.file));
27502
+ });
27503
+ addSharedOptions(review.command("discard")).description("\u786E\u8BA4\u4E22\u5F03\u4E00\u9879\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027").argument("<key>", "\u9644\u52A0\u5C5E\u6027\u952E").action(async function(key) {
27504
+ const shared = readSharedOptions(this);
27505
+ console.log(await attrReviewDiscard(resolveCwd(shared.cwd), {
27506
+ key,
27507
+ file: shared.file,
27508
+ yes: shared.yes
27509
+ }));
27510
+ });
26777
27511
  return attr;
26778
27512
  }
26779
27513
 
26780
27514
  // src/commands/version/create-version.ts
26781
27515
  import { Command as Command20 } from "commander";
26782
27516
 
27517
+ // src/domain/version/form/option.ts
27518
+ var MAX_OPTION = 30;
27519
+ var MAX_OPTION_ITEM = 30;
27520
+ var MAX_TEXT_DEFAULT = 140;
27521
+ var MAX_NAME2 = 50;
27522
+ var MAX_REMARK2 = 50;
27523
+ function assertValidName2(name) {
27524
+ if (name.length > MAX_NAME2) {
27525
+ throw new CliError("\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26", "OPTION_NAME_LONG");
27526
+ }
27527
+ }
27528
+ function assertValidRemark2(remark) {
27529
+ if (remark.length > MAX_REMARK2) {
27530
+ throw new CliError("\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26\u3002", "OPTION_REMARK_LONG");
27531
+ }
27532
+ }
27533
+ function assertOptionValueLength(value) {
27534
+ if (value.length > MAX_TEXT_DEFAULT) {
27535
+ throw new CliError("\u4E0D\u8D85\u8FC7140\u4E2A\u5B57\u7B26", "OPTION_VALUE_LONG");
27536
+ }
27537
+ }
27538
+ function isOption(item) {
27539
+ return item.type === "editableText" || item.type === "select";
27540
+ }
27541
+ async function assertTypeAllowsOption(input2) {
27542
+ if (input2.supportOptionalConfig === true) return;
27543
+ if (input2.supportOptionalConfig === false) {
27544
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27545
+ }
27546
+ requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27547
+ const type = await getTypeInfo(input2.typeCode, input2.apis);
27548
+ if (type.supportOptionalConfig !== 2) {
27549
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27550
+ }
27551
+ }
27552
+ async function assertDraftOptionsAllowed(input2) {
27553
+ if (!(input2.customPropertyDescriptors ?? []).some((item) => isOption(item))) return;
27554
+ await assertTypeAllowsOption(input2);
27555
+ }
27556
+ function optionListFromDraft(draft) {
27557
+ return (draft.customPropertyDescriptors ?? []).filter((item) => isOption(item));
27558
+ }
27559
+ function normalizeMode(mode) {
27560
+ if (mode === void 0) return void 0;
27561
+ if (mode === "\u4E0B\u62C9" || mode === "\u4E0B\u62C9\u5217\u8868" || mode === "select") return "select";
27562
+ if (mode === "\u6587\u672C" || mode === "\u6587\u672C\u8F93\u5165\u6846" || mode === "input") return "editableText";
27563
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27564
+ }
27565
+ function parseCandidates(raw) {
27566
+ const items = (raw ?? "").split("|").map((item) => item.trim()).filter(Boolean);
27567
+ if (items.length === 0) throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27568
+ if (items.length > MAX_OPTION_ITEM) throw new CliError("\u9009\u9879\u4E2A\u6570\u4E0D\u80FD\u8D85\u8FC730\u9879", "OPTION_OPTIONS");
27569
+ for (const item of items) assertOptionValueLength(item);
27570
+ if (items.some((item, index) => items.indexOf(item) !== index)) {
27571
+ throw new CliError("\u8BE5\u9009\u9879\u5DF2\u5B58\u5728", "OPTION_DUPLICATE_OPTION");
27572
+ }
27573
+ return items;
27574
+ }
27575
+ async function optionAdd(cwd, input2) {
27576
+ return withProjectLock(cwd, () => optionAddLocked(cwd, input2), "version-option-add");
27577
+ }
27578
+ async function optionAddLocked(cwd, input2) {
27579
+ const identity = resolveIdentity(cwd, input2.file);
27580
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27581
+ const draft = readDraft(cwd, identity.n) ?? {
27582
+ baseUpcastResources: [],
27583
+ authExcludedItems: []
27584
+ };
27585
+ const parsed = parseLine(input2.line ?? "");
27586
+ if (!parsed.name || !parsed.key) {
27587
+ throw new CliError("\u53EF\u9009\u914D\u7F6E\u9700\u8981\u540D\u79F0\u548C\u952E", "OPTION_FIELDS");
27588
+ }
27589
+ assertValidKey(parsed.key);
27590
+ const list2 = draft.customPropertyDescriptors ?? [];
27591
+ if (optionListFromDraft(draft).length >= MAX_OPTION) {
27592
+ throw new CliError("\u6700\u591A\u53EF\u6DFB\u52A030\u4E2A\u53EF\u9009\u914D\u7F6E", "OPTION_FULL");
27593
+ }
27594
+ if (list2.some((item) => item.key === parsed.key)) {
27595
+ throw new CliError(`\u952E ${parsed.key} \u5DF2\u5B58\u5728`, "OPTION_DUPLICATE");
27596
+ }
27597
+ if (list2.some((item) => item.name === parsed.name)) {
27598
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27599
+ }
27600
+ const normalizedMode = normalizeMode(parsed.mode);
27601
+ if (!normalizedMode) {
27602
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27603
+ }
27604
+ const isSelect = normalizedMode === "select";
27605
+ const isText = normalizedMode === "editableText";
27606
+ if (isSelect && parsed.defaultValue) {
27607
+ throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
27608
+ }
27609
+ if (isText && parsed.options) {
27610
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27611
+ }
27612
+ if (!isSelect && !isText) {
27613
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27614
+ }
27615
+ let type = "editableText";
27616
+ let defaultValue = parsed.defaultValue ?? parsed.value ?? "";
27617
+ let candidateItems;
27618
+ assertValidName2(parsed.name);
27619
+ assertValidRemark2(parsed.remark ?? "");
27620
+ if (isSelect) {
27621
+ const options = parseCandidates(parsed.options);
27622
+ type = "select";
27623
+ defaultValue = options[0] ?? "";
27624
+ candidateItems = options;
27625
+ } else {
27626
+ assertOptionValueLength(defaultValue);
27627
+ }
27628
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27629
+ list2.push({
27630
+ name: parsed.name,
27631
+ key: parsed.key,
27632
+ remark: parsed.remark ?? "",
27633
+ defaultValue,
27634
+ type,
27635
+ ...candidateItems ? { candidateItems } : {}
27636
+ });
27637
+ draft.customPropertyDescriptors = list2;
27638
+ writeDraft(cwd, identity.n, draft);
27639
+ return preview;
27640
+ }
27641
+ async function optionSet(cwd, input2) {
27642
+ return withProjectLock(cwd, () => optionSetLocked(cwd, input2), "version-option-set");
27643
+ }
27644
+ async function optionSetLocked(cwd, input2) {
27645
+ const identity = resolveIdentity(cwd, input2.file);
27646
+ const draft = readDraft(cwd, identity.n);
27647
+ if (!draft) {
27648
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27649
+ }
27650
+ const parsed = parseLine(input2.line ?? "");
27651
+ const found = (draft.customPropertyDescriptors ?? []).find(
27652
+ (item) => isOption(item) && (item.key === parsed.key || item.name === parsed.name)
27653
+ );
27654
+ if (!found) {
27655
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27656
+ }
27657
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27658
+ assertKeyUnchanged(String(found.key), parsed.key);
27659
+ assertValidName2(parsed.name ?? String(found.name ?? ""));
27660
+ assertValidRemark2(parsed.remark ?? String(found.remark ?? ""));
27661
+ const nextType = normalizeMode(parsed.mode) ?? (String(found.type) === "select" ? "select" : "editableText");
27662
+ const nextDefault = parsed.defaultValue ?? parsed.value;
27663
+ if (nextType === "select" && nextDefault !== void 0) {
27664
+ throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
27665
+ }
27666
+ if (nextType === "editableText") {
27667
+ if (parsed.options !== void 0) {
27668
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27669
+ }
27670
+ if (nextDefault !== void 0) assertOptionValueLength(nextDefault);
27671
+ }
27672
+ if (parsed.name && (draft.customPropertyDescriptors ?? []).some((item) => item.name === parsed.name && item.key !== found.key)) {
27673
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27674
+ }
27675
+ if (parsed.name) {
27676
+ found.name = parsed.name;
27677
+ }
27678
+ if (parsed.remark !== void 0) {
27679
+ found.remark = parsed.remark;
27680
+ }
27681
+ if (nextType === "select") {
27682
+ const candidates = parsed.options !== void 0 ? parseCandidates(parsed.options) : Array.isArray(found.candidateItems) ? found.candidateItems.map(String) : [];
27683
+ if (candidates.length === 0) {
27684
+ throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27685
+ }
27686
+ found.type = "select";
27687
+ found.candidateItems = candidates;
27688
+ found.defaultValue = candidates[0];
27689
+ } else {
27690
+ found.type = "editableText";
27691
+ delete found.candidateItems;
27692
+ if (nextDefault !== void 0) {
27693
+ found.defaultValue = nextDefault;
27694
+ }
27695
+ }
27696
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27697
+ writeDraft(cwd, identity.n, draft);
27698
+ return preview;
27699
+ }
27700
+ function optionRm(cwd, key, file) {
27701
+ return withProjectLock(cwd, () => optionRmLocked(cwd, key, file), "version-option-rm");
27702
+ }
27703
+ function optionRmLocked(cwd, key, file) {
27704
+ const identity = resolveIdentity(cwd, file);
27705
+ const draft = readDraft(cwd, identity.n);
27706
+ if (!draft) {
27707
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27708
+ }
27709
+ const list2 = draft.customPropertyDescriptors ?? [];
27710
+ const next = list2.filter((item) => !(isOption(item) && item.key === key));
27711
+ if (next.length === list2.length) {
27712
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27713
+ }
27714
+ draft.customPropertyDescriptors = next;
27715
+ writeDraft(cwd, identity.n, draft);
27716
+ return key;
27717
+ }
27718
+ function optionList(cwd, file) {
27719
+ const identity = resolveIdentity(cwd, file);
27720
+ const draft = readDraft(cwd, identity.n);
27721
+ return optionListFromDraft(draft ?? {}).map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
27722
+ }
27723
+
26783
27724
  // src/domain/version/submit.ts
26784
27725
  function fieldName(error2) {
26785
27726
  const rec = error2;
@@ -26804,6 +27745,9 @@ function buildVersionPayload(input2) {
26804
27745
  };
26805
27746
  }
26806
27747
  async function submitVersion(input2) {
27748
+ return withProjectLock(input2.cwd, () => submitVersionLocked(input2), "submit-version");
27749
+ }
27750
+ async function submitVersionLocked(input2) {
26807
27751
  assertPlatformAllowed();
26808
27752
  if (!input2.identity.resourceId) {
26809
27753
  throw new CliError("\u8BF7\u5148 create \u6216 bind", "SUBMIT_NO_RESOURCE");
@@ -26818,15 +27762,39 @@ async function submitVersion(input2) {
26818
27762
  if ((draft.orphanedInputAttrs ?? []).length > 0) {
26819
27763
  throw new CliError("\u6587\u4EF6\u5206\u6790\u53D8\u5316\u5F85\u5904\u7406\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u590D\u6838\u9644\u52A0\u5C5E\u6027", "SUBMIT_ORPHANED_INPUT_ATTRS");
26820
27764
  }
27765
+ await assertDraftOptionsAllowed({
27766
+ cwd: input2.cwd,
27767
+ typeCode: input2.identity.typeCode,
27768
+ customPropertyDescriptors: draft.customPropertyDescriptors,
27769
+ homeDir: input2.homeDir,
27770
+ apis: input2.apis
27771
+ });
26821
27772
  const payload = buildVersionPayload({
26822
27773
  resourceId: input2.identity.resourceId,
26823
27774
  version: input2.version,
26824
27775
  draft
26825
27776
  });
26826
27777
  const createVersion2 = input2.apis?.createVersion ?? ((params) => service_API_default.Resource.createVersion(params));
27778
+ assertNoPendingOperation(input2.cwd);
27779
+ createPendingVersionSubmit({
27780
+ cwd: input2.cwd,
27781
+ resourceN: input2.identity.n,
27782
+ resourceId: input2.identity.resourceId,
27783
+ env: getEnv2(),
27784
+ version: input2.version,
27785
+ fileSha1: draft.fileSha1
27786
+ });
27787
+ markPendingVersionSubmitSending(input2.cwd);
26827
27788
  try {
26828
27789
  await createVersion2(payload);
26829
27790
  } catch (error2) {
27791
+ if (!isDefinitivePlatformRejection(error2)) {
27792
+ throw new CliError(
27793
+ "\u7248\u672C\u63D0\u4EA4\u8BF7\u6C42\u7ED3\u679C\u672A\u77E5\uFF1B\u8BF7\u8FD0\u884C resource recover \u6838\u9A8C\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4\u6216\u91CD\u65B0 bump",
27794
+ "SUBMIT_RESULT_UNKNOWN"
27795
+ );
27796
+ }
27797
+ clearPendingOperation(input2.cwd);
26830
27798
  const field = fieldName(error2);
26831
27799
  const detail = errorDetail(error2);
26832
27800
  throw new CliError(
@@ -26834,21 +27802,30 @@ async function submitVersion(input2) {
26834
27802
  "SUBMIT_FAILED"
26835
27803
  );
26836
27804
  }
26837
- deleteDraft(input2.cwd, input2.identity.n);
27805
+ commitLocalTransaction(input2.cwd, [
27806
+ { path: draftFilePath(input2.cwd, input2.identity.n), content: null },
27807
+ { path: pendingOperationFilePath(input2.cwd), content: null }
27808
+ ]);
27809
+ }
27810
+ function isDefinitivePlatformRejection(error2) {
27811
+ if (error2 instanceof CliError) return true;
27812
+ const rec = error2;
27813
+ if (typeof rec.field === "string" || typeof rec.msg === "string" || typeof rec.result?.msg === "string") {
27814
+ return true;
27815
+ }
27816
+ const status = rec.status ?? rec.statusCode ?? rec.response?.status;
27817
+ return typeof status === "number" && status >= 400 && status < 500;
26838
27818
  }
26839
27819
 
26840
27820
  // src/domain/version/file.ts
26841
27821
  import { createHash as createHash3 } from "crypto";
26842
- import { existsSync as existsSync12, readFileSync as readFileSync9, statSync as statSync2, unlinkSync as unlinkSync6 } from "fs";
26843
- import path15 from "path";
27822
+ import { createReadStream, existsSync as existsSync15, openAsBlob, statSync as statSync3, unlinkSync as unlinkSync7 } from "fs";
27823
+ import path16 from "path";
26844
27824
  var ANALYZE_TIMEOUT_MS = 12e4;
26845
27825
  var ANALYZE_POLL_INTERVAL_MS = 200;
26846
27826
  function resolveExistingPath(cwd, raw) {
26847
- if (existsSync12(raw)) {
26848
- return path15.resolve(raw);
26849
- }
26850
- const joined = path15.resolve(cwd, raw);
26851
- if (existsSync12(joined)) {
27827
+ const joined = path16.resolve(cwd, raw);
27828
+ if (existsSync15(joined)) {
26852
27829
  return joined;
26853
27830
  }
26854
27831
  return void 0;
@@ -26860,11 +27837,13 @@ function confirmLocalPath(identity, file, yes, cwd) {
26860
27837
  if (!resolved) {
26861
27838
  throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${normalizedFile}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
26862
27839
  }
27840
+ resolveExistingProjectPath(cwd, resolved);
26863
27841
  return resolved;
26864
27842
  }
26865
27843
  const recorded = identity.filePath ? normalizeProjectPath(cwd, identity.filePath) : void 0;
26866
27844
  const recordedExists = recorded ? resolveExistingPath(cwd, recorded) : void 0;
26867
27845
  if (recordedExists) {
27846
+ resolveExistingProjectPath(cwd, recordedExists);
26868
27847
  return recordedExists;
26869
27848
  }
26870
27849
  if (yes) {
@@ -26894,24 +27873,55 @@ async function waitAnalyze(sha1, typeCode, apis, now = Date.now, sleep = (ms) =>
26894
27873
  }
26895
27874
  throw new CliError("\u5C5E\u6027\u89E3\u6790\u8D85\u65F6", "FILE_ANALYZE_TIMEOUT");
26896
27875
  }
26897
- function sha1OfFile(uploadPath) {
26898
- return createHash3("sha1").update(readFileSync9(uploadPath)).digest("hex");
27876
+ function fingerprintFile(uploadPath) {
27877
+ const stats = statSync3(uploadPath);
27878
+ if (!stats.isFile()) {
27879
+ throw new CliError("\u4EA7\u7269\u5FC5\u987B\u662F\u53EF\u8BFB\u53D6\u7684\u6587\u4EF6", "FILE_ARTIFACT_UNSUPPORTED");
27880
+ }
27881
+ return { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs };
26899
27882
  }
26900
- async function uploadIfNew(uploadPath, sha1, typeCode, apis) {
27883
+ function assertFileUnchanged(uploadPath, expected) {
27884
+ const actual = fingerprintFile(uploadPath);
27885
+ if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs || actual.ctimeMs !== expected.ctimeMs) {
27886
+ throw new CliError(
27887
+ "\u53D1\u5E03\u671F\u95F4\u672C\u5730\u4EA7\u7269\u53D1\u751F\u53D8\u5316\uFF1B\u8BF7\u4FDD\u6301\u4EA7\u7269\u7A33\u5B9A\u540E\u91CD\u8BD5",
27888
+ "FILE_CHANGED_DURING_PUBLISH"
27889
+ );
27890
+ }
27891
+ }
27892
+ async function sha1OfFile(uploadPath) {
27893
+ const fingerprint = fingerprintFile(uploadPath);
27894
+ const hash = createHash3("sha1");
27895
+ await new Promise((resolve, reject) => {
27896
+ const stream4 = createReadStream(uploadPath);
27897
+ stream4.on("data", (chunk) => {
27898
+ hash.update(chunk);
27899
+ });
27900
+ stream4.once("error", reject);
27901
+ stream4.once("end", resolve);
27902
+ });
27903
+ assertFileUnchanged(uploadPath, fingerprint);
27904
+ return { sha1: hash.digest("hex"), fingerprint };
27905
+ }
27906
+ async function uploadIfNew(uploadPath, sha1, fingerprint, typeCode, apis) {
26901
27907
  const fileIsExist2 = apis.fileIsExist ?? ((params) => service_API_default.Storage.fileIsExist(params));
26902
27908
  const exists2 = unwrapFirst(await fileIsExist2({ sha1 }));
26903
27909
  if (exists2.isExisting || exists2.exist || exists2.data === true) {
27910
+ assertFileUnchanged(uploadPath, fingerprint);
26904
27911
  return;
26905
27912
  }
26906
27913
  const upload = apis.uploadFile ?? ((params) => service_API_default.Storage.uploadFile(params));
26907
- await upload({ file: readFileSync9(uploadPath), resourceType: typeCode });
27914
+ assertFileUnchanged(uploadPath, fingerprint);
27915
+ const file = await openAsBlob(uploadPath);
27916
+ await upload({ file, resourceType: typeCode });
27917
+ assertFileUnchanged(uploadPath, fingerprint);
26908
27918
  }
26909
27919
  function removeTempZip(uploadPath, localPath) {
26910
- if (uploadPath === localPath || !existsSync12(uploadPath)) {
27920
+ if (uploadPath === localPath || !existsSync15(uploadPath)) {
26911
27921
  return;
26912
27922
  }
26913
27923
  try {
26914
- unlinkSync6(uploadPath);
27924
+ unlinkSync7(uploadPath);
26915
27925
  } catch {
26916
27926
  }
26917
27927
  }
@@ -26926,16 +27936,22 @@ function writeSha1ToDraft(input2, uploaded, analysis) {
26926
27936
  metadata.filter((item) => Boolean(item) && typeof item === "object").filter((item) => Number(item.insertMode) === 2).map((item) => String(item.key ?? "")).filter(Boolean)
26927
27937
  );
26928
27938
  const previousAttrs = draft.inputAttrs ?? [];
27939
+ const previousOrphans = draft.orphanedInputAttrs ?? [];
26929
27940
  const reuseCurrentAnalysis = !hasMetadata && draft.fileSha1 === uploaded.fileSha1 && draft.analyzedSha1 === uploaded.fileSha1;
26930
- const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? "")));
27941
+ const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : [
27942
+ ...previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? ""))),
27943
+ // 新分析重新出现同 key 时,恢复用户上次保留的值;它不应永远卡在待复核队列。
27944
+ ...previousOrphans.filter((item) => additionalKeys.has(String(item.key ?? "")) && !previousAttrs.some((current) => additionalKeys.has(String(current.key ?? "")) && current.key === item.key))
27945
+ ];
26931
27946
  const newlyOrphaned = reuseCurrentAnalysis ? [] : previousAttrs.filter((item) => !additionalKeys.has(String(item.key ?? "")));
27947
+ const unresolvedOrphans = reuseCurrentAnalysis ? previousOrphans : previousOrphans.filter((item) => !additionalKeys.has(String(item.key ?? "")));
26932
27948
  draft.fileSha1 = uploaded.fileSha1;
26933
27949
  draft.filename = uploaded.filename;
26934
27950
  draft.analyzedSha1 = uploaded.fileSha1;
26935
27951
  draft.inputAttrs = retainedAttrs;
26936
27952
  draft.orphanedInputAttrs = [
26937
- ...draft.orphanedInputAttrs ?? [],
26938
- ...newlyOrphaned.filter((item) => !(draft.orphanedInputAttrs ?? []).some((old) => old.key === item.key))
27953
+ ...unresolvedOrphans,
27954
+ ...newlyOrphaned.filter((item) => !unresolvedOrphans.some((old) => old.key === item.key))
26939
27955
  ];
26940
27956
  const nextDraft = prepareDraft(input2.cwd, input2.identity.n, draft);
26941
27957
  const changes = [{
@@ -26959,10 +27975,11 @@ async function uploadAndAnalyzeLocked(input2) {
26959
27975
  const recordedFile = normalizeProjectPath(input2.cwd, input2.identity.filePath);
26960
27976
  const identity = recordedFile === input2.identity.filePath ? input2.identity : { ...input2.identity, filePath: recordedFile };
26961
27977
  const localPath = confirmLocalPath(identity, file, input2.yes, input2.cwd);
26962
- const uploadPath = await prepareUploadPath(identity.typeCode, localPath);
27978
+ const uploadPath = await prepareUploadPath(identity.typeCode, localPath, input2.cwd);
26963
27979
  try {
26964
- const uploaded = { fileSha1: sha1OfFile(uploadPath), filename: path15.basename(uploadPath) };
26965
- await uploadIfNew(uploadPath, uploaded.fileSha1, identity.typeCode, input2.apis ?? {});
27980
+ const hashed = await sha1OfFile(uploadPath);
27981
+ const uploaded = { fileSha1: hashed.sha1, filename: path16.basename(uploadPath) };
27982
+ await uploadIfNew(uploadPath, uploaded.fileSha1, hashed.fingerprint, identity.typeCode, input2.apis ?? {});
26966
27983
  const analysis = await waitAnalyze(
26967
27984
  uploaded.fileSha1,
26968
27985
  identity.typeCode,
@@ -26970,6 +27987,7 @@ async function uploadAndAnalyzeLocked(input2) {
26970
27987
  input2.now,
26971
27988
  input2.sleep
26972
27989
  );
27990
+ assertFileUnchanged(uploadPath, hashed.fingerprint);
26973
27991
  writeSha1ToDraft(
26974
27992
  {
26975
27993
  ...input2,
@@ -27004,7 +28022,7 @@ async function runCreateVersion(input2) {
27004
28022
  }
27005
28023
  async function runCreateVersionLocked(input2) {
27006
28024
  assertPlatformAllowed();
27007
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28025
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27008
28026
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27009
28027
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27010
28028
  const info2 = unwrapData2(
@@ -27021,8 +28039,10 @@ async function runCreateVersionLocked(input2) {
27021
28039
  throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "CREATE_VERSION_NEED_YES");
27022
28040
  }
27023
28041
  if (input2.reset) {
27024
- confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28042
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28043
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
27025
28044
  }
28045
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27026
28046
  let draft = existingDraft;
27027
28047
  if (input2.reset && existingDraft) {
27028
28048
  if (!input2.confirmReset) {
@@ -27060,6 +28080,7 @@ async function runCreateVersionLocked(input2) {
27060
28080
  isLoadLatestVersionInfo: 1
27061
28081
  })
27062
28082
  );
28083
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
27063
28084
  if (again.latestVersion) {
27064
28085
  throw new CliError(
27065
28086
  `\u8FD9\u4E2A\u8D44\u6E90\u5DF2\u7ECF\u6709\u53D1\u884C\u7248\u672C\u3002\u7EBF\u4E0A latest \u662F ${again.latestVersion}\uFF0C\u8BF7\u7528 update-version\u3002`,
@@ -27070,6 +28091,7 @@ async function runCreateVersionLocked(input2) {
27070
28091
  cwd: input2.cwd,
27071
28092
  identity,
27072
28093
  version: "1.0.0",
28094
+ homeDir: input2.homeDir,
27073
28095
  apis: input2.apis
27074
28096
  });
27075
28097
  return "1.0.0";
@@ -27120,6 +28142,26 @@ import { Command as Command21 } from "commander";
27120
28142
 
27121
28143
  // src/domain/version/form/dep.ts
27122
28144
  import semver from "semver";
28145
+ async function assertCurrentResourceWritable(input2) {
28146
+ const auth = requireAuth({ cwd: input2.cwd });
28147
+ const resourceId = input2.identity.resourceId;
28148
+ if (!resourceId) {
28149
+ throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
28150
+ }
28151
+ const infoApi2 = input2.apis?.ownInfo ?? ((params) => service_API_default.Resource.info(params));
28152
+ const info2 = unwrapData3(await infoApi2({ resourceIdOrName: resourceId }));
28153
+ assertRemoteResourceWritable({
28154
+ info: info2,
28155
+ resourceId,
28156
+ authUserId: auth.userId,
28157
+ codes: {
28158
+ invalid: "DEP_CURRENT_INFO_INVALID",
28159
+ notOwner: "DEP_CURRENT_NOT_OWNER",
28160
+ frozen: "DEP_CURRENT_RESOURCE_FROZEN"
28161
+ }
28162
+ });
28163
+ return resourceId;
28164
+ }
27123
28165
  function unwrapData3(result) {
27124
28166
  const envelope = result;
27125
28167
  const data = envelope.data ?? result;
@@ -27157,6 +28199,64 @@ function statusLabel(status) {
27157
28199
  }
27158
28200
  return void 0;
27159
28201
  }
28202
+ async function loadDependencyTarget(input2) {
28203
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28204
+ const info2 = unwrapData3(
28205
+ await infoApi2({
28206
+ resourceIdOrName: input2.resourceIdOrName,
28207
+ isLoadLatestVersionInfo: 1,
28208
+ isLoadPolicyInfo: 1
28209
+ })
28210
+ );
28211
+ const resourceId = typeof info2.resourceId === "string" ? info2.resourceId.trim() : "";
28212
+ if (!resourceId || input2.expectedResourceId && resourceId !== input2.expectedResourceId) {
28213
+ throw new CliError("\u5BF9\u65B9\u8D44\u6E90\u4FE1\u606F\u4E0D\u5B8C\u6574\u6216\u4E0E\u8981\u4FEE\u6539\u7684\u4F9D\u8D56\u4E0D\u4E00\u81F4", "DEP_TARGET_INVALID");
28214
+ }
28215
+ const resourceName = typeof info2.resourceName === "string" ? info2.resourceName : typeof info2.name === "string" ? info2.name : "";
28216
+ if (input2.identity.resourceId === resourceId || input2.identity.name && resourceName === input2.identity.name) {
28217
+ throw new CliError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1", "DEP_SELF");
28218
+ }
28219
+ if (Number(info2.subjectType) !== 1) {
28220
+ throw new CliError("\u5BF9\u65B9\u4E0D\u662F\u666E\u901A\u5355\u8D44\u6E90\uFF0C\u4E0D\u52A0", "DEP_NOT_RESOURCE");
28221
+ }
28222
+ const latestVersion = typeof info2.latestVersion === "string" ? info2.latestVersion.trim() : "";
28223
+ if (!latestVersion || !semver.valid(latestVersion)) {
28224
+ throw new CliError("\u5BF9\u65B9\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C", "DEP_NO_VERSION");
28225
+ }
28226
+ if (info2.status === void 0 || !Number.isFinite(Number(info2.status))) {
28227
+ throw new CliError("\u5BF9\u65B9\u8D44\u6E90\u4FE1\u606F\u4E0D\u5B8C\u6574\u6216\u72B6\u6001\u4E0D\u53EF\u7528", "DEP_TARGET_INVALID");
28228
+ }
28229
+ const blocked = statusLabel(Number(info2.status));
28230
+ if (blocked) {
28231
+ throw new CliError(blocked, "DEP_TARGET_STATUS");
28232
+ }
28233
+ if (Array.isArray(info2.baseUpcastResources) && info2.baseUpcastResources.length > 0) {
28234
+ throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
28235
+ }
28236
+ return { info: info2, resourceId, latestVersion };
28237
+ }
28238
+ async function assertDependencyVersionRange(input2) {
28239
+ const listApi = input2.apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
28240
+ const list2 = unwrapData3(await listApi({ resourceId: input2.resourceId }));
28241
+ const rawVersions = list2.dataList ?? list2.list;
28242
+ if (!Array.isArray(rawVersions) || rawVersions.length === 0) {
28243
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28244
+ }
28245
+ const versions = [];
28246
+ for (const item of rawVersions) {
28247
+ const version = typeof item === "object" && item !== null && typeof item.version === "string" ? item.version : "";
28248
+ if (!version || !semver.valid(version)) {
28249
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28250
+ }
28251
+ versions.push(version);
28252
+ }
28253
+ if (!versions.some((version) => semver.eq(version, input2.latestVersion))) {
28254
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28255
+ }
28256
+ if (!semver.validRange(input2.versionRange) || !semver.maxSatisfying(versions, input2.versionRange)) {
28257
+ throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
28258
+ }
28259
+ }
27160
28260
  function signablePolicies(info2) {
27161
28261
  const policies2 = info2.policies ?? [];
27162
28262
  return policies2.filter((item) => item.status === 1 && item.policyId).map((item) => ({ policyId: item.policyId, policyName: item.policyName }));
@@ -27197,49 +28297,26 @@ async function depAdd(input2) {
27197
28297
  }
27198
28298
  async function depAddLocked(input2) {
27199
28299
  assertPlatformAllowed();
27200
- const identity = resolveIdentity(input2.cwd, input2.file);
28300
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27201
28301
  const draft = readDraft(input2.cwd, identity.n) ?? {
27202
28302
  baseUpcastResources: [],
27203
28303
  authExcludedItems: []
27204
28304
  };
27205
- const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27206
- const info2 = unwrapData3(
27207
- await infoApi2({
27208
- resourceIdOrName: input2.resourceId,
27209
- isLoadLatestVersionInfo: 1,
27210
- isLoadPolicyInfo: 1
27211
- })
27212
- );
27213
- const targetId = String(info2.resourceId ?? input2.resourceId);
27214
- const targetName = String(info2.resourceName ?? info2.name ?? "");
27215
- if (identity.resourceId && (targetId === identity.resourceId || targetName === `${identity.name}`)) {
27216
- throw new CliError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1", "DEP_SELF");
27217
- }
27218
- if (info2.subjectType !== void 0 && Number(info2.subjectType) !== 1) {
27219
- throw new CliError("\u5BF9\u65B9\u4E0D\u662F\u666E\u901A\u5355\u8D44\u6E90\uFF0C\u4E0D\u52A0", "DEP_NOT_RESOURCE");
27220
- }
27221
- const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
27222
- if (!latestVersion) {
27223
- throw new CliError("\u5BF9\u65B9\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C", "DEP_NO_VERSION");
27224
- }
27225
- const status = Number(info2.status ?? 1);
27226
- const blocked = statusLabel(status);
27227
- if (blocked) {
27228
- throw new CliError(blocked, "DEP_TARGET_STATUS");
27229
- }
27230
- const upcast = info2.baseUpcastResources;
27231
- if (Array.isArray(upcast) && upcast.length > 0) {
27232
- throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
27233
- }
27234
- let versionRange = input2.versionRange ?? `^${latestVersion}`;
27235
- const listApi = input2.apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
27236
- const list2 = unwrapData3(await listApi({ resourceId: targetId }));
27237
- const versions = (list2.dataList ?? list2.list ?? []).map((item) => item.version).filter((item) => Boolean(item));
27238
- if (versions.length > 0) {
27239
- if (!semver.validRange(versionRange) || !semver.maxSatisfying(versions, versionRange)) {
27240
- throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
27241
- }
27242
- }
28305
+ const licenseeId = await assertCurrentResourceWritable({ cwd: input2.cwd, identity, apis: input2.apis });
28306
+ const target = await loadDependencyTarget({
28307
+ resourceIdOrName: input2.resourceId,
28308
+ identity,
28309
+ apis: input2.apis
28310
+ });
28311
+ const targetId = target.resourceId;
28312
+ const info2 = target.info;
28313
+ const versionRange = input2.versionRange ?? `^${target.latestVersion}`;
28314
+ await assertDependencyVersionRange({
28315
+ resourceId: targetId,
28316
+ latestVersion: target.latestVersion,
28317
+ versionRange,
28318
+ apis: input2.apis
28319
+ });
27243
28320
  if (identity.resourceId) {
27244
28321
  const cycleApi = input2.apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27245
28322
  const existing2 = (draft.dependencies ?? []).filter((item) => item.resourceId !== targetId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
@@ -27262,16 +28339,13 @@ async function depAddLocked(input2) {
27262
28339
  );
27263
28340
  if (!extractIsAuth(firstAuth, targetId)) {
27264
28341
  const policies2 = signablePolicies(info2);
27265
- if (!identity.resourceId) {
27266
- throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
27267
- }
27268
28342
  const sign = input2.apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27269
28343
  await signWithSelectedPolicy({
27270
28344
  policies: policies2,
27271
28345
  policyId: input2.policyId,
27272
28346
  yes: input2.yes,
27273
28347
  targetId,
27274
- licenseeId: identity.resourceId,
28348
+ licenseeId,
27275
28349
  sign
27276
28350
  });
27277
28351
  }
@@ -27317,7 +28391,8 @@ async function depRange(cwd, resourceId, versionRange, file, apis, options) {
27317
28391
  return withProjectLock(cwd, () => depRangeLocked(cwd, resourceId, versionRange, file, apis, options), "version-dep-range");
27318
28392
  }
27319
28393
  async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options) {
27320
- const identity = resolveIdentity(cwd, file);
28394
+ assertPlatformAllowed();
28395
+ const identity = resolveBoundIdentity(cwd, file);
27321
28396
  const draft = readDraft(cwd, identity.n);
27322
28397
  if (!draft) {
27323
28398
  throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
@@ -27326,39 +28401,27 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27326
28401
  if (!found) {
27327
28402
  throw new CliError(`\u7A3F\u4E0A\u6CA1\u6709\u4F9D\u8D56 ${resourceId}`, "DEP_NOT_FOUND");
27328
28403
  }
27329
- const infoApi2 = apis?.info ?? ((params) => service_API_default.Resource.info(params));
27330
- const info2 = unwrapData3(
27331
- await infoApi2({
27332
- resourceIdOrName: resourceId,
27333
- isLoadLatestVersionInfo: 1,
27334
- isLoadPolicyInfo: 1
27335
- })
27336
- );
27337
- const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
27338
- const status = Number(info2.status ?? 1);
27339
- const blocked = statusLabel(status);
27340
- if (blocked) {
27341
- throw new CliError(blocked, "DEP_TARGET_STATUS");
27342
- }
27343
- const upcast = info2.baseUpcastResources;
27344
- if (Array.isArray(upcast) && upcast.length > 0) {
27345
- throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
27346
- }
27347
- const listApi = apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
27348
- const list2 = unwrapData3(await listApi({ resourceId }));
27349
- const versions = (list2.dataList ?? list2.list ?? []).map((item) => item.version).filter((item) => Boolean(item));
27350
- if (versions.length > 0 && latestVersion) {
27351
- if (!semver.validRange(versionRange) || !semver.maxSatisfying(versions, versionRange)) {
27352
- throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
27353
- }
27354
- }
28404
+ const licenseeId = await assertCurrentResourceWritable({ cwd, identity, apis });
28405
+ const target = await loadDependencyTarget({
28406
+ resourceIdOrName: resourceId,
28407
+ expectedResourceId: resourceId,
28408
+ identity,
28409
+ apis
28410
+ });
28411
+ await assertDependencyVersionRange({
28412
+ resourceId: target.resourceId,
28413
+ latestVersion: target.latestVersion,
28414
+ versionRange,
28415
+ apis
28416
+ });
28417
+ const info2 = target.info;
27355
28418
  if (identity.resourceId) {
27356
28419
  const cycleApi = apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27357
28420
  const existing = (draft.dependencies ?? []).filter((item) => item.resourceId !== resourceId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
27358
28421
  const cycle = unwrapData3(
27359
28422
  await cycleApi({
27360
28423
  resourceId: identity.resourceId,
27361
- dependencies: [...existing, { resourceId, versionRange }]
28424
+ dependencies: [...existing, { resourceId: target.resourceId, versionRange }]
27362
28425
  })
27363
28426
  );
27364
28427
  if (cycle.ok === false || cycle.data === false) {
@@ -27366,22 +28429,22 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27366
28429
  }
27367
28430
  }
27368
28431
  const batchAuth3 = apis?.batchAuth ?? ((params) => service_API_default.Resource.batchAuth(params));
27369
- const firstAuth = unwrapData3(await batchAuth3({ resourceIds: resourceId, versionRanges: versionRange }));
27370
- if (!extractIsAuth(firstAuth, resourceId) && identity.resourceId) {
28432
+ const firstAuth = unwrapData3(await batchAuth3({ resourceIds: target.resourceId, versionRanges: versionRange }));
28433
+ if (!extractIsAuth(firstAuth, target.resourceId) && identity.resourceId) {
27371
28434
  const policies2 = signablePolicies(info2);
27372
28435
  const sign = apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27373
28436
  await signWithSelectedPolicy({
27374
28437
  policies: policies2,
27375
28438
  policyId: options?.policyId,
27376
28439
  yes: options?.yes,
27377
- targetId: resourceId,
27378
- licenseeId: identity.resourceId,
28440
+ targetId: target.resourceId,
28441
+ licenseeId,
27379
28442
  sign
27380
28443
  });
27381
28444
  }
27382
28445
  found.versionRange = versionRange;
27383
28446
  writeDraft(cwd, identity.n, draft);
27384
- return `${resourceId}@${versionRange}`;
28447
+ return `${target.resourceId}@${versionRange}`;
27385
28448
  }
27386
28449
 
27387
28450
  // src/commands/version/dep.ts
@@ -27424,7 +28487,7 @@ import { Command as Command22 } from "commander";
27424
28487
  // src/domain/version/description.ts
27425
28488
  async function updateOnlineDescription(input2) {
27426
28489
  assertPlatformAllowed();
27427
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28490
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27428
28491
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27429
28492
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27430
28493
  const info2 = unwrapData2(
@@ -27433,6 +28496,7 @@ async function updateOnlineDescription(input2) {
27433
28496
  isLoadLatestVersionInfo: 1
27434
28497
  })
27435
28498
  );
28499
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27436
28500
  if (!info2.latestVersion) {
27437
28501
  throw new CliError(
27438
28502
  "\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C\u3002\u8BF7\u5148\u6309\u53D1\u884C\u7248\u672C create-version \u63D0\u4EA4\u8D44\u6E90\u6587\u4EF6\u3002",
@@ -27655,210 +28719,6 @@ function createVersionDraftCommand() {
27655
28719
 
27656
28720
  // src/commands/version/option.ts
27657
28721
  import { Command as Command24 } from "commander";
27658
-
27659
- // src/domain/version/form/option.ts
27660
- var MAX_OPTION = 30;
27661
- var MAX_OPTION_ITEM = 30;
27662
- var MAX_TEXT_DEFAULT = 140;
27663
- var MAX_NAME2 = 50;
27664
- var MAX_REMARK2 = 50;
27665
- function assertValidName2(name) {
27666
- if (name.length > MAX_NAME2) {
27667
- throw new CliError("\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26", "OPTION_NAME_LONG");
27668
- }
27669
- }
27670
- function assertValidRemark2(remark) {
27671
- if (remark.length > MAX_REMARK2) {
27672
- throw new CliError("\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26\u3002", "OPTION_REMARK_LONG");
27673
- }
27674
- }
27675
- function assertOptionValueLength(value) {
27676
- if (value.length > MAX_TEXT_DEFAULT) {
27677
- throw new CliError("\u4E0D\u8D85\u8FC7140\u4E2A\u5B57\u7B26", "OPTION_VALUE_LONG");
27678
- }
27679
- }
27680
- function isOption(item) {
27681
- return item.type === "editableText" || item.type === "select";
27682
- }
27683
- async function assertTypeAllowsOption(input2) {
27684
- if (input2.supportOptionalConfig === true) return;
27685
- if (input2.supportOptionalConfig === false) {
27686
- throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27687
- }
27688
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27689
- const type = await getTypeInfo(input2.typeCode, input2.apis);
27690
- if (type.supportOptionalConfig !== 2) {
27691
- throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27692
- }
27693
- }
27694
- function optionListFromDraft(draft) {
27695
- return (draft.customPropertyDescriptors ?? []).filter((item) => isOption(item));
27696
- }
27697
- function normalizeMode(mode) {
27698
- if (mode === void 0) return void 0;
27699
- if (mode === "\u4E0B\u62C9" || mode === "\u4E0B\u62C9\u5217\u8868" || mode === "select") return "select";
27700
- if (mode === "\u6587\u672C" || mode === "\u6587\u672C\u8F93\u5165\u6846" || mode === "input") return "editableText";
27701
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27702
- }
27703
- function parseCandidates(raw) {
27704
- const items = (raw ?? "").split("|").map((item) => item.trim()).filter(Boolean);
27705
- if (items.length === 0) throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27706
- if (items.length > MAX_OPTION_ITEM) throw new CliError("\u9009\u9879\u4E2A\u6570\u4E0D\u80FD\u8D85\u8FC730\u9879", "OPTION_OPTIONS");
27707
- for (const item of items) assertOptionValueLength(item);
27708
- if (items.some((item, index) => items.indexOf(item) !== index)) {
27709
- throw new CliError("\u8BE5\u9009\u9879\u5DF2\u5B58\u5728", "OPTION_DUPLICATE_OPTION");
27710
- }
27711
- return items;
27712
- }
27713
- async function optionAdd(cwd, input2) {
27714
- return withProjectLock(cwd, () => optionAddLocked(cwd, input2), "version-option-add");
27715
- }
27716
- async function optionAddLocked(cwd, input2) {
27717
- const identity = resolveIdentity(cwd, input2.file);
27718
- await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27719
- const draft = readDraft(cwd, identity.n) ?? {
27720
- baseUpcastResources: [],
27721
- authExcludedItems: []
27722
- };
27723
- const parsed = parseLine(input2.line ?? "");
27724
- if (!parsed.name || !parsed.key) {
27725
- throw new CliError("\u53EF\u9009\u914D\u7F6E\u9700\u8981\u540D\u79F0\u548C\u952E", "OPTION_FIELDS");
27726
- }
27727
- assertValidKey(parsed.key);
27728
- const list2 = draft.customPropertyDescriptors ?? [];
27729
- if (optionListFromDraft(draft).length >= MAX_OPTION) {
27730
- throw new CliError("\u6700\u591A\u53EF\u6DFB\u52A030\u4E2A\u53EF\u9009\u914D\u7F6E", "OPTION_FULL");
27731
- }
27732
- if (list2.some((item) => item.key === parsed.key)) {
27733
- throw new CliError(`\u952E ${parsed.key} \u5DF2\u5B58\u5728`, "OPTION_DUPLICATE");
27734
- }
27735
- if (list2.some((item) => item.name === parsed.name)) {
27736
- throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27737
- }
27738
- const normalizedMode = normalizeMode(parsed.mode);
27739
- if (!normalizedMode) {
27740
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27741
- }
27742
- const isSelect = normalizedMode === "select";
27743
- const isText = normalizedMode === "editableText";
27744
- if (isSelect && parsed.defaultValue) {
27745
- throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
27746
- }
27747
- if (isText && parsed.options) {
27748
- throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27749
- }
27750
- if (!isSelect && !isText) {
27751
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27752
- }
27753
- let type = "editableText";
27754
- let defaultValue = parsed.defaultValue ?? parsed.value ?? "";
27755
- let candidateItems;
27756
- assertValidName2(parsed.name);
27757
- assertValidRemark2(parsed.remark ?? "");
27758
- if (isSelect) {
27759
- const options = parseCandidates(parsed.options);
27760
- type = "select";
27761
- defaultValue = options[0] ?? "";
27762
- candidateItems = options;
27763
- } else {
27764
- assertOptionValueLength(defaultValue);
27765
- }
27766
- const preview = await confirmWrite(previewLine(parsed), input2.yes);
27767
- list2.push({
27768
- name: parsed.name,
27769
- key: parsed.key,
27770
- remark: parsed.remark ?? "",
27771
- defaultValue,
27772
- type,
27773
- ...candidateItems ? { candidateItems } : {}
27774
- });
27775
- draft.customPropertyDescriptors = list2;
27776
- writeDraft(cwd, identity.n, draft);
27777
- return preview;
27778
- }
27779
- async function optionSet(cwd, input2) {
27780
- return withProjectLock(cwd, () => optionSetLocked(cwd, input2), "version-option-set");
27781
- }
27782
- async function optionSetLocked(cwd, input2) {
27783
- const identity = resolveIdentity(cwd, input2.file);
27784
- const draft = readDraft(cwd, identity.n);
27785
- if (!draft) {
27786
- throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27787
- }
27788
- const parsed = parseLine(input2.line ?? "");
27789
- const found = (draft.customPropertyDescriptors ?? []).find(
27790
- (item) => isOption(item) && (item.key === parsed.key || item.name === parsed.name)
27791
- );
27792
- if (!found) {
27793
- throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27794
- }
27795
- assertKeyUnchanged(String(found.key), parsed.key);
27796
- assertValidName2(parsed.name ?? String(found.name ?? ""));
27797
- assertValidRemark2(parsed.remark ?? String(found.remark ?? ""));
27798
- const nextType = normalizeMode(parsed.mode) ?? (String(found.type) === "select" ? "select" : "editableText");
27799
- const nextDefault = parsed.defaultValue ?? parsed.value;
27800
- if (nextType === "select" && nextDefault !== void 0) {
27801
- throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
27802
- }
27803
- if (nextType === "editableText") {
27804
- if (parsed.options !== void 0) {
27805
- throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27806
- }
27807
- if (nextDefault !== void 0) assertOptionValueLength(nextDefault);
27808
- }
27809
- if (parsed.name && (draft.customPropertyDescriptors ?? []).some((item) => item.name === parsed.name && item.key !== found.key)) {
27810
- throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27811
- }
27812
- if (parsed.name) {
27813
- found.name = parsed.name;
27814
- }
27815
- if (parsed.remark !== void 0) {
27816
- found.remark = parsed.remark;
27817
- }
27818
- if (nextType === "select") {
27819
- const candidates = parsed.options !== void 0 ? parseCandidates(parsed.options) : Array.isArray(found.candidateItems) ? found.candidateItems.map(String) : [];
27820
- if (candidates.length === 0) {
27821
- throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27822
- }
27823
- found.type = "select";
27824
- found.candidateItems = candidates;
27825
- found.defaultValue = candidates[0];
27826
- } else {
27827
- found.type = "editableText";
27828
- delete found.candidateItems;
27829
- if (nextDefault !== void 0) {
27830
- found.defaultValue = nextDefault;
27831
- }
27832
- }
27833
- const preview = await confirmWrite(previewLine(parsed), input2.yes);
27834
- writeDraft(cwd, identity.n, draft);
27835
- return preview;
27836
- }
27837
- function optionRm(cwd, key, file) {
27838
- return withProjectLock(cwd, () => optionRmLocked(cwd, key, file), "version-option-rm");
27839
- }
27840
- function optionRmLocked(cwd, key, file) {
27841
- const identity = resolveIdentity(cwd, file);
27842
- const draft = readDraft(cwd, identity.n);
27843
- if (!draft) {
27844
- throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27845
- }
27846
- const list2 = draft.customPropertyDescriptors ?? [];
27847
- const next = list2.filter((item) => !(isOption(item) && item.key === key));
27848
- if (next.length === list2.length) {
27849
- throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27850
- }
27851
- draft.customPropertyDescriptors = next;
27852
- writeDraft(cwd, identity.n, draft);
27853
- return key;
27854
- }
27855
- function optionList(cwd, file) {
27856
- const identity = resolveIdentity(cwd, file);
27857
- const draft = readDraft(cwd, identity.n);
27858
- return optionListFromDraft(draft ?? {}).map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
27859
- }
27860
-
27861
- // src/commands/version/option.ts
27862
28722
  function createVersionOptionCommand() {
27863
28723
  const option = addSharedOptions(new Command24("option"));
27864
28724
  option.description("\u6539\u7A3F\u4E0A\u7684\u53EF\u9009\u914D\u7F6E");
@@ -27893,7 +28753,16 @@ function createVersionOptionCommand() {
27893
28753
  import { Command as Command25 } from "commander";
27894
28754
 
27895
28755
  // src/domain/version/setPath.ts
27896
- import path16 from "path";
28756
+ import { existsSync as existsSync16, statSync as statSync4 } from "fs";
28757
+ import path17 from "path";
28758
+ function sameOrNestedPath(parent, candidate) {
28759
+ const relative = path17.relative(parent, candidate);
28760
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path17.sep}`) && !path17.isAbsolute(relative);
28761
+ }
28762
+ function artifactsOverlap(first, second) {
28763
+ if (first.path === second.path) return true;
28764
+ return (first.isDirectory || second.isDirectory) && (sameOrNestedPath(first.path, second.path) || sameOrNestedPath(second.path, first.path));
28765
+ }
27897
28766
  function setIdentityFilePath(cwd, input2) {
27898
28767
  return withProjectLock(cwd, () => {
27899
28768
  const artifact = input2.artifact !== void 0 ? normalizeProjectPath(cwd, input2.artifact) : void 0;
@@ -27901,7 +28770,24 @@ function setIdentityFilePath(cwd, input2) {
27901
28770
  throw new CliError("\u8BF7\u63D0\u4F9B --artifact", "SET_ARTIFACT_REQUIRED");
27902
28771
  }
27903
28772
  const identity = resolveIdentity(cwd, input2.file);
27904
- assertArtifactAnchor(identity.typeCode, path16.resolve(cwd, artifact));
28773
+ const absoluteArtifact = path17.resolve(cwd, artifact);
28774
+ assertArtifactAnchor(identity.typeCode, absoluteArtifact, cwd);
28775
+ const nextArtifact = {
28776
+ path: resolveExistingProjectPath(cwd, absoluteArtifact),
28777
+ isDirectory: statSync4(absoluteArtifact).isDirectory()
28778
+ };
28779
+ const occupant = validateLocalState(cwd).find((candidate) => {
28780
+ if (candidate.n === identity.n) return false;
28781
+ const candidatePath = path17.resolve(cwd, normalizeProjectPath(cwd, candidate.filePath));
28782
+ if (!existsSync16(candidatePath)) return false;
28783
+ return artifactsOverlap(nextArtifact, {
28784
+ path: resolveExistingProjectPath(cwd, candidatePath),
28785
+ isDirectory: statSync4(candidatePath).isDirectory()
28786
+ });
28787
+ });
28788
+ if (occupant) {
28789
+ throw new CliError(`\u4EA7\u7269\u8DEF\u5F84 ${artifact} \u5DF2\u88AB ${occupant.n}.json \u5360\u7528`, "SET_ARTIFACT_OCCUPIED");
28790
+ }
27905
28791
  const updated = prepareIdentityUpdate(cwd, identity.n, { filePath: artifact });
27906
28792
  commitLocalTransaction(cwd, [
27907
28793
  { path: identityFilePath(cwd, updated.n), content: serializeIdentity(updated) }
@@ -27950,10 +28836,11 @@ function showLocal(cwd, file) {
27950
28836
  async function showOnline(input2) {
27951
28837
  assertPlatformAllowed();
27952
28838
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27953
- const identity = resolveIdentity(input2.cwd, input2.file);
27954
- if (!identity.resourceId) {
28839
+ const local = resolveIdentity(input2.cwd, input2.file);
28840
+ if (!local.resourceId) {
27955
28841
  return "\u672C\u5730\u8FD8\u6CA1\u6709 resourceId";
27956
28842
  }
28843
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27957
28844
  const draft = readDraft(input2.cwd, identity.n);
27958
28845
  const hint = draft ? "\u672C\u5730\u6709\u672A\u63D0\u4EA4\u7684\u7248\u672C\u5DE5\u4F5C\u7A3F\uFF0C\u67E5\u770B\u8BF7 version show --local\n" : "";
27959
28846
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
@@ -28026,7 +28913,7 @@ async function runUpdateVersion(input2) {
28026
28913
  }
28027
28914
  async function runUpdateVersionLocked(input2) {
28028
28915
  assertPlatformAllowed();
28029
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28916
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28030
28917
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
28031
28918
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28032
28919
  const info2 = unwrapData2(
@@ -28057,8 +28944,10 @@ async function runUpdateVersionLocked(input2) {
28057
28944
  throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "UPDATE_VERSION_NEED_YES");
28058
28945
  }
28059
28946
  if (input2.reset) {
28060
- confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28947
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28948
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
28061
28949
  }
28950
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
28062
28951
  let draft = existingDraft;
28063
28952
  if (input2.reset && existingDraft) {
28064
28953
  if (!input2.confirmReset) {
@@ -28103,6 +28992,7 @@ async function runUpdateVersionLocked(input2) {
28103
28992
  isLoadLatestVersionInfo: 1
28104
28993
  })
28105
28994
  );
28995
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
28106
28996
  const currentLatest = String(again.latestVersion ?? latestVersion);
28107
28997
  if (!semver2.gt(next, currentLatest)) {
28108
28998
  throw new CliError(
@@ -28114,6 +29004,7 @@ async function runUpdateVersionLocked(input2) {
28114
29004
  cwd: input2.cwd,
28115
29005
  identity,
28116
29006
  version: next,
29007
+ homeDir: input2.homeDir,
28117
29008
  apis: input2.apis
28118
29009
  });
28119
29010
  return next;
@@ -28235,26 +29126,26 @@ function createSubCommands() {
28235
29126
  }
28236
29127
 
28237
29128
  // src/core/usageDocs.ts
28238
- import { existsSync as existsSync13 } from "fs";
28239
- import path17 from "path";
29129
+ import { existsSync as existsSync17 } from "fs";
29130
+ import path18 from "path";
28240
29131
  import { fileURLToPath } from "url";
28241
29132
  function usageDocsPath() {
28242
- const moduleDir = path17.dirname(fileURLToPath(import.meta.url));
28243
- const bundledPath = path17.resolve(moduleDir, "../docs/README.md");
28244
- if (existsSync13(bundledPath)) {
29133
+ const moduleDir = path18.dirname(fileURLToPath(import.meta.url));
29134
+ const bundledPath = path18.resolve(moduleDir, "../docs/README.md");
29135
+ if (existsSync17(bundledPath)) {
28245
29136
  return bundledPath;
28246
29137
  }
28247
- return path17.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
29138
+ return path18.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
28248
29139
  }
28249
29140
 
28250
29141
  // src/core/packageVersion.ts
28251
- import { readFileSync as readFileSync10 } from "fs";
28252
- import path18 from "path";
29142
+ import { readFileSync as readFileSync11 } from "fs";
29143
+ import path19 from "path";
28253
29144
  import { fileURLToPath as fileURLToPath2 } from "url";
28254
29145
  function packageVersion() {
28255
- const moduleDir = path18.dirname(fileURLToPath2(import.meta.url));
28256
- const packageJson = path18.resolve(moduleDir, "../..", "package.json");
28257
- const value = JSON.parse(readFileSync10(packageJson, "utf8"));
29146
+ const moduleDir = path19.dirname(fileURLToPath2(import.meta.url));
29147
+ const packageJson = path19.resolve(moduleDir, "../..", "package.json");
29148
+ const value = JSON.parse(readFileSync11(packageJson, "utf8"));
28258
29149
  return typeof value.version === "string" ? value.version : "unknown";
28259
29150
  }
28260
29151
 
@@ -28279,7 +29170,14 @@ function createProgram() {
28279
29170
  const cwdFlag = findOptionValue(actionCommand ?? thisCommand, "cwd");
28280
29171
  const cwd = resolveCwd(typeof cwdFlag === "string" ? cwdFlag : void 0);
28281
29172
  setAuthSearchCwd(cwd);
28282
- await chooseResourceWhenNeeded(actionCommand ?? thisCommand, cwd);
29173
+ const command = actionCommand ?? thisCommand;
29174
+ if (!isResourceFreeCommand(command)) {
29175
+ withProjectLock(cwd, () => void 0, "preselect-resource");
29176
+ }
29177
+ if (!isPendingOperationReadAllowed(command)) {
29178
+ withProjectLock(cwd, () => assertNoPendingOperation(cwd), "pending-operation-gate");
29179
+ }
29180
+ await chooseResourceWhenNeeded(command, cwd);
28283
29181
  });
28284
29182
  for (const command of Object.values(createSubCommands())) {
28285
29183
  program.addCommand(command);
@@ -28287,6 +29185,10 @@ function createProgram() {
28287
29185
  return program;
28288
29186
  }
28289
29187
  var RESOURCE_FREE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "init", "template", "type", "resource", "create", "bind"]);
29188
+ var PENDING_OPERATION_READ_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "template", "type", "status", "show", "validate", "list", "recover"]);
29189
+ function isPendingOperationReadAllowed(command) {
29190
+ return PENDING_OPERATION_READ_COMMANDS.has(command.name());
29191
+ }
28290
29192
  async function chooseResourceWhenNeeded(command, cwd) {
28291
29193
  if (isResourceFreeCommand(command)) return;
28292
29194
  const selector = findOptionValue(command, "resource");
@@ -28317,7 +29219,7 @@ function resourceChoiceLabel(cwd, identity) {
28317
29219
  return [
28318
29220
  `${identity.n}.json`,
28319
29221
  `\u6807\u9898=${identity.title ?? "\uFF08\u672A\u540C\u6B65\u6807\u9898\uFF09"}`,
28320
- `\u6807\u8BC6=${identity.name ?? "\u672A\u7ED1\u5B9A"}`,
29222
+ `\u6807\u8BC6=${identity.resourceName ?? identity.name ?? "\u672A\u7ED1\u5B9A"}`,
28321
29223
  `ID=${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}`,
28322
29224
  `\u7C7B\u578B=${identity.typeCode}`,
28323
29225
  `\u4EA7\u7269=${identity.filePath ?? "\u672A\u8BBE\u7F6E"}`,
@@ -28325,15 +29227,10 @@ function resourceChoiceLabel(cwd, identity) {
28325
29227
  ].join(" ");
28326
29228
  }
28327
29229
  function resourceSelectorExamples(identities) {
28328
- const titleCounts = /* @__PURE__ */ new Map();
28329
- for (const identity of identities) {
28330
- if (identity.title) titleCounts.set(identity.title, (titleCounts.get(identity.title) ?? 0) + 1);
28331
- }
28332
29230
  return identities.map((identity) => {
28333
29231
  const selectors = [
28334
29232
  ...identity.resourceId ? [`id:${identity.resourceId}`] : [],
28335
- ...identity.name ? [`name:${identity.name}`] : [],
28336
- ...identity.title && titleCounts.get(identity.title) === 1 ? [`title:${identity.title}`] : [],
29233
+ ...identity.resourceName ? [`name:${identity.resourceName}`] : [],
28337
29234
  `artifact:${identity.filePath}`,
28338
29235
  `file:${identity.n}.json`
28339
29236
  ];