@freelog-cli/cli2 0.5.3 → 0.5.5

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
  }
@@ -24927,13 +25006,18 @@ function supportsResourceSubject(subjectType) {
24927
25006
  function hasChildren(node) {
24928
25007
  return Array.isArray(node.children) && node.children.length > 0;
24929
25008
  }
24930
- function flattenLeaves(nodes2, acc = []) {
25009
+ function flattenLeaves(nodes2, ancestors = [], acc = []) {
24931
25010
  for (const node of nodes2) {
25011
+ const path20 = [...ancestors, node];
24932
25012
  if (hasChildren(node)) {
24933
- flattenLeaves(node.children, acc);
25013
+ flattenLeaves(node.children, path20, acc);
24934
25014
  }
24935
25015
  if (isEnabledResourceLeafCandidate(node)) {
24936
- acc.push(node);
25016
+ const names = path20.map((item) => item.name.trim());
25017
+ if (names.some((name) => !name)) {
25018
+ 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");
25019
+ }
25020
+ acc.push({ ...node, nameChain: names.join(" / ") });
24937
25021
  }
24938
25022
  }
24939
25023
  return acc;
@@ -24943,18 +25027,58 @@ async function resourceTypeTree(apis) {
24943
25027
  const request2 = apis.resourceTypes ?? ((params) => service_API_default.Resource.resourceTypes(params));
24944
25028
  return unwrapList(await request2({ category: 1, status: 1, subjectType: 1 }));
24945
25029
  }
25030
+ function findTypePath(nodes2, code, ancestors = []) {
25031
+ for (const node of nodes2) {
25032
+ const path20 = [...ancestors, node];
25033
+ if (node.code === code) return path20;
25034
+ if (hasChildren(node)) {
25035
+ const found = findTypePath(node.children, code, path20);
25036
+ if (found) return found;
25037
+ }
25038
+ }
25039
+ return void 0;
25040
+ }
25041
+ async function getTypeHierarchy(code, apis = {}) {
25042
+ const normalized = code.trim();
25043
+ if (!normalized) throw new CliError("\u8BF7\u63D0\u4F9B\u8D44\u6E90\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
25044
+ const path20 = findTypePath(await resourceTypeTree(apis), normalized);
25045
+ if (!path20) throw new CliError(`\u5E73\u53F0\u7C7B\u578B\u6811\u4E0D\u5305\u542B\u5F53\u524D\u7C7B\u578B ${normalized}`, "TYPE_HIERARCHY_NOT_FOUND");
25046
+ const names = path20.map((node) => node.name.trim());
25047
+ if (names.some((name) => !name)) {
25048
+ 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");
25049
+ }
25050
+ return names;
25051
+ }
24946
25052
  async function listLeafTypes(apis = {}) {
24947
25053
  return flattenLeaves(await resourceTypeTree(apis));
24948
25054
  }
24949
25055
  async function searchLeafTypes(keyword, apis = {}) {
24950
25056
  assertPlatformAllowed();
24951
25057
  const request2 = apis.searchLeaves ?? ((params) => service_API_default.Resource.ListSimpleByParentCode(params));
24952
- return unwrapList(await request2({
24953
- nameChain: keyword,
24954
- isTerminate: true,
24955
- status: 1,
24956
- subjectType: 1
24957
- })).filter(isEnabledResourceLeaf);
25058
+ const [tree, response] = await Promise.all([
25059
+ resourceTypeTree(apis),
25060
+ request2({
25061
+ category: 1,
25062
+ nameChain: keyword,
25063
+ isTerminate: true,
25064
+ status: 1,
25065
+ subjectType: 1
25066
+ })
25067
+ ]);
25068
+ const codes = /* @__PURE__ */ new Set();
25069
+ return unwrapList(response).flatMap((item) => {
25070
+ if (!item.code || codes.has(item.code)) return [];
25071
+ codes.add(item.code);
25072
+ const path20 = findTypePath(tree, item.code);
25073
+ if (!path20) return [];
25074
+ const leaf = path20[path20.length - 1];
25075
+ if (!isEnabledResourceLeafCandidate(leaf)) return [];
25076
+ const names = path20.map((node) => node.name.trim());
25077
+ if (names.some((name) => !name)) {
25078
+ 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");
25079
+ }
25080
+ return [{ ...leaf, nameChain: names.join(" / ") }];
25081
+ });
24958
25082
  }
24959
25083
  async function getTypeInfo(code, apis = {}) {
24960
25084
  const normalized = code.trim();
@@ -24963,7 +25087,8 @@ async function getTypeInfo(code, apis = {}) {
24963
25087
  }
24964
25088
  assertPlatformAllowed();
24965
25089
  const request2 = apis.getByCode ?? ((params) => service_API_default.Resource.getResourceTypeInfoByCode(params));
24966
- const info2 = unwrapOne(await request2({ code: normalized }));
25090
+ const rawInfo = unwrapOne(await request2({ code: normalized }));
25091
+ const info2 = rawInfo ? normalizeTypeInfo(rawInfo) : void 0;
24967
25092
  if (!info2?.code) {
24968
25093
  throw new CliError(`\u627E\u4E0D\u5230\u7C7B\u578B ${normalized}`, "TYPE_NOT_FOUND");
24969
25094
  }
@@ -25027,13 +25152,41 @@ async function chooseLeafType(apis = {}) {
25027
25152
  }
25028
25153
  throw new CliError("\u5DF2\u53D6\u6D88\u8D44\u6E90\u7C7B\u578B\u9009\u62E9", "TYPE_PICK_CANCELLED");
25029
25154
  }
25155
+ var TYPE_LIST_PAGE_SIZE = 50;
25156
+ function typeListPage(items, page) {
25157
+ const pageCount = Math.max(1, Math.ceil(items.length / TYPE_LIST_PAGE_SIZE));
25158
+ if (!Number.isInteger(page) || page < 1 || page > pageCount) {
25159
+ throw new CliError(`\u7C7B\u578B\u9875\u7801\u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5171 ${pageCount} \u9875`, "TYPE_LIST_PAGE");
25160
+ }
25161
+ return {
25162
+ page,
25163
+ pageCount,
25164
+ total: items.length,
25165
+ hasPrevious: page > 1,
25166
+ hasNext: page < pageCount,
25167
+ items: items.slice((page - 1) * TYPE_LIST_PAGE_SIZE, page * TYPE_LIST_PAGE_SIZE)
25168
+ };
25169
+ }
25030
25170
  function formatTypeList(items) {
25031
25171
  return items.map((item) => `${item.code} ${item.nameChain ?? item.name}`).join("\n");
25032
25172
  }
25173
+ function formatTypeListPage(page) {
25174
+ const header = `\u7B2C ${page.page}/${page.pageCount} \u9875\uFF0C\u5171 ${page.total} \u4E2A\u53EF\u7528\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B`;
25175
+ const rows = formatTypeList(page.items);
25176
+ return rows ? `${header}
25177
+ ${rows}` : `${header}
25178
+ \u6CA1\u6709\u5339\u914D\u7684\u542F\u7528\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B`;
25179
+ }
25180
+ function formatTypeInfo(item, hierarchy) {
25181
+ const optionalConfig = supportsOptionalConfig(item) ? "\u652F\u6301" : "\u4E0D\u652F\u6301";
25182
+ const label = hierarchy?.join(" / ") || item.nameChain || item.name;
25183
+ return `${item.code} ${label} \u53EF\u9009\u914D\u7F6E\uFF1A${optionalConfig}`;
25184
+ }
25033
25185
 
25034
25186
  // src/domain/version/zip.ts
25035
- import { createWriteStream, existsSync as existsSync8, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync5 } from "fs";
25187
+ import { createWriteStream, existsSync as existsSync9, lstatSync, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync5 } from "fs";
25036
25188
  import { tmpdir } from "os";
25189
+ import { randomUUID } from "crypto";
25037
25190
  import path9 from "path";
25038
25191
  import { finished } from "stream/promises";
25039
25192
  import archiver from "archiver";
@@ -25041,56 +25194,83 @@ function isThemeOrWidget(typeCode) {
25041
25194
  return typeCode === "RT001" || typeCode === "RT002";
25042
25195
  }
25043
25196
  function assertArtifactPath(typeCode, filePath) {
25044
- const stats = existsSync8(filePath) ? statSync(filePath) : void 0;
25197
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25045
25198
  const themeOrWidget = isThemeOrWidget(typeCode);
25046
25199
  if (stats?.isDirectory() && !themeOrWidget) {
25047
25200
  throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25048
25201
  }
25049
25202
  }
25050
- function assertArtifactAnchor(typeCode, filePath) {
25051
- if (!existsSync8(filePath)) {
25203
+ function assertArtifactAnchor(typeCode, filePath, cwd) {
25204
+ if (!existsSync9(filePath)) {
25052
25205
  throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25053
25206
  }
25207
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25054
25208
  const stats = statSync(filePath);
25055
25209
  assertArtifactPath(typeCode, filePath);
25056
25210
  if (!isThemeOrWidget(typeCode) && !stats.isFile()) {
25057
25211
  throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25058
25212
  }
25059
25213
  }
25060
- async function zipDirectoryContents(dir) {
25061
- if (!existsSync8(dir) || !statSync(dir).isDirectory()) {
25214
+ function assertUploadArtifactReady(typeCode, filePath, cwd) {
25215
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25216
+ assertArtifactPath(typeCode, filePath);
25217
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25218
+ if (!stats) {
25219
+ throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25220
+ }
25221
+ if (stats.isDirectory() && isThemeOrWidget(typeCode)) {
25222
+ assertZipDirectoryReady(filePath);
25223
+ return;
25224
+ }
25225
+ if (!stats.isFile()) {
25226
+ 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");
25227
+ }
25228
+ }
25229
+ function assertZipDirectoryReady(dir) {
25230
+ if (!existsSync9(dir) || !statSync(dir).isDirectory()) {
25062
25231
  throw new CliError(`\u6CA1\u6709\u6784\u5EFA\u4EA7\u7269 ${dir}\uFF0C\u8BF7\u5148\u6784\u5EFA`, "ZIP_MISSING");
25063
25232
  }
25064
- if (existsSync8(path9.join(dir, ".freelog"))) {
25233
+ if (existsSync9(path9.join(dir, ".freelog"))) {
25065
25234
  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
25235
  }
25067
- const entries = readdirSync3(dir);
25068
- if (entries.length === 0) {
25236
+ if (readdirSync3(dir).length === 0) {
25069
25237
  throw new CliError("\u6784\u5EFA\u4EA7\u7269\u662F\u7A7A\u7684\uFF0C\u8BF7\u5148\u6784\u5EFA", "ZIP_EMPTY");
25070
25238
  }
25239
+ }
25240
+ async function zipDirectoryContents(dir) {
25241
+ assertZipDirectoryReady(dir);
25071
25242
  const outPath = path9.join(
25072
25243
  tmpdir(),
25073
- `freelog-zip-${process.pid}-${Date.now()}.zip`
25244
+ `freelog-zip-${process.pid}-${randomUUID()}.zip`
25074
25245
  );
25075
25246
  const output2 = createWriteStream(outPath);
25076
25247
  const archive = archiver("zip", { zlib: { level: 9 } });
25077
25248
  try {
25078
25249
  archive.on("error", (error2) => output2.destroy(error2));
25079
25250
  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 });
25251
+ const appendEntries = (currentDir, prefix = "") => {
25252
+ for (const entry2 of readdirSync3(currentDir).sort((left, right) => left < right ? -1 : left > right ? 1 : 0)) {
25253
+ const full = path9.join(currentDir, entry2);
25254
+ const stats = lstatSync(full);
25255
+ const entryName = prefix ? `${prefix}/${entry2}` : entry2;
25256
+ if (stats.isSymbolicLink()) {
25257
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u4E0D\u80FD\u5305\u542B\u8F6F\u94FE\u63A5\uFF1A${entryName}`, "ZIP_SYMLINK_UNSUPPORTED");
25258
+ }
25259
+ if (stats.isDirectory()) {
25260
+ appendEntries(full, entryName);
25261
+ } else if (stats.isFile()) {
25262
+ archive.append(readFileSync6(full), { name: entryName });
25263
+ } else {
25264
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u5305\u542B\u4E0D\u652F\u6301\u7684\u6761\u76EE\uFF1A${entryName}`, "ZIP_ENTRY_UNSUPPORTED");
25265
+ }
25087
25266
  }
25088
- }
25267
+ };
25268
+ appendEntries(dir);
25089
25269
  await archive.finalize();
25090
25270
  await finished(output2);
25091
25271
  return outPath;
25092
25272
  } catch (error2) {
25093
- if (existsSync8(outPath)) {
25273
+ if (existsSync9(outPath)) {
25094
25274
  try {
25095
25275
  unlinkSync5(outPath);
25096
25276
  } catch {
@@ -25099,9 +25279,9 @@ async function zipDirectoryContents(dir) {
25099
25279
  throw error2;
25100
25280
  }
25101
25281
  }
25102
- async function prepareUploadPath(typeCode, filePath) {
25103
- assertArtifactPath(typeCode, filePath);
25104
- if (isThemeOrWidget(typeCode) && existsSync8(filePath) && statSync(filePath).isDirectory()) {
25282
+ async function prepareUploadPath(typeCode, filePath, cwd) {
25283
+ assertUploadArtifactReady(typeCode, filePath, cwd);
25284
+ if (isThemeOrWidget(typeCode) && existsSync9(filePath) && statSync(filePath).isDirectory()) {
25105
25285
  return zipDirectoryContents(filePath);
25106
25286
  }
25107
25287
  return filePath;
@@ -25164,6 +25344,9 @@ function validateCreateFlags(input2) {
25164
25344
  function resolveTargetIdentity(input2) {
25165
25345
  if (input2.selector) return resolveIdentity(input2.cwd, input2.selector);
25166
25346
  const unbound = input2.identities.filter((identity) => !identity.resourceId);
25347
+ if (input2.artifact !== void 0) {
25348
+ return unbound.find((identity) => identity.filePath === input2.artifact);
25349
+ }
25167
25350
  if (unbound.length === 1) return unbound[0];
25168
25351
  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
25352
  return void 0;
@@ -25177,7 +25360,8 @@ async function assertOwnShellAvailable(input2) {
25177
25360
  if (!existing.resourceId) {
25178
25361
  return;
25179
25362
  }
25180
- if (existing.userId !== input2.authUserId) {
25363
+ const ownerId = existing.userId ?? existing.ownerId ?? existing.creatorId;
25364
+ if (ownerId !== input2.authUserId) {
25181
25365
  throw new CliError(
25182
25366
  `\u8D44\u6E90\u6388\u6743\u6807\u8BC6 ${input2.name} \u5DF2\u88AB\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002`,
25183
25367
  "CREATE_NAME_TAKEN"
@@ -25193,6 +25377,7 @@ function writeCreatedIdentity(input2) {
25193
25377
  if (!filePath) throw new CliError("\u65B0\u8D44\u6E90\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "CREATE_ARTIFACT_REQUIRED");
25194
25378
  const patch = {
25195
25379
  resourceId: input2.resourceId,
25380
+ resourceName: input2.resourceName,
25196
25381
  name: input2.name,
25197
25382
  title: input2.title,
25198
25383
  typeCode: input2.typeCode,
@@ -25202,6 +25387,7 @@ function writeCreatedIdentity(input2) {
25202
25387
  return input2.target ? updateIdentity(input2.cwd, input2.target.n, patch) : createIdentity(input2.cwd, {
25203
25388
  subject: "resource",
25204
25389
  resourceId: input2.resourceId,
25390
+ resourceName: input2.resourceName,
25205
25391
  name: input2.name,
25206
25392
  title: input2.title,
25207
25393
  typeCode: input2.typeCode,
@@ -25212,7 +25398,6 @@ function writeCreatedIdentity(input2) {
25212
25398
  async function createResource(input2) {
25213
25399
  assertPlatformAllowed();
25214
25400
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25215
- validateLocalState(input2.cwd);
25216
25401
  const { title, name } = validateCreateFlags(input2);
25217
25402
  const file = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25218
25403
  code: "CREATE_FILE_OUTSIDE",
@@ -25223,6 +25408,7 @@ async function createResource(input2) {
25223
25408
  const target = resolveTargetIdentity({
25224
25409
  cwd: input2.cwd,
25225
25410
  selector: input2.selector,
25411
+ artifact: file,
25226
25412
  identities
25227
25413
  });
25228
25414
  const occupant = file ? identities.find((identity) => identity.filePath === file && identity.n !== target?.n) : void 0;
@@ -25248,7 +25434,7 @@ async function createResource(input2) {
25248
25434
  }
25249
25435
  const typeCode = resolveTypeCode(input2.type, target);
25250
25436
  await getTypeInfo(typeCode, input2.apis);
25251
- assertArtifactAnchor(typeCode, path10.resolve(input2.cwd, artifact));
25437
+ assertArtifactAnchor(typeCode, path10.resolve(input2.cwd, artifact), input2.cwd);
25252
25438
  await assertOwnShellAvailable({
25253
25439
  authLoginName: auth.loginName,
25254
25440
  authUserId: auth.userId,
@@ -25271,6 +25457,7 @@ async function createResource(input2) {
25271
25457
  cwd: input2.cwd,
25272
25458
  env: getEnv2(),
25273
25459
  name,
25460
+ resourceName: typeof created.resourceName === "string" && created.resourceName ? created.resourceName : `${auth.loginName}/${name}`,
25274
25461
  typeCode,
25275
25462
  resourceId,
25276
25463
  title,
@@ -25318,6 +25505,10 @@ function createCreateCommand() {
25318
25505
  // src/commands/listing/update.ts
25319
25506
  import { Command as Command5 } from "commander";
25320
25507
 
25508
+ // src/domain/listing/update.ts
25509
+ import { existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
25510
+ import path11 from "path";
25511
+
25321
25512
  // src/domain/version/gates.ts
25322
25513
  function evaluateGates(input2, intent) {
25323
25514
  const latest = input2.latestVersion;
@@ -25359,19 +25550,170 @@ function resolveBoundIdentity(cwd, file) {
25359
25550
  if (!identity.resourceId) {
25360
25551
  throw new CliError("\u8BF7\u5148 create \u6216 bind", "GATE_NO_RESOURCE");
25361
25552
  }
25553
+ const identityEnv = identity.env ?? "prod";
25554
+ const activeEnv2 = getEnv2();
25555
+ if (identityEnv !== activeEnv2) {
25556
+ throw new CliError(
25557
+ `\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`,
25558
+ "RESOURCE_ENV_MISMATCH"
25559
+ );
25560
+ }
25362
25561
  return identity;
25363
25562
  }
25563
+ function assertRemoteResourceWritable(input2) {
25564
+ const codes = {
25565
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25566
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER",
25567
+ frozen: input2.codes?.frozen ?? "RESOURCE_FROZEN"
25568
+ };
25569
+ assertRemoteResourceOwned({
25570
+ info: input2.info,
25571
+ resourceId: input2.resourceId,
25572
+ authUserId: input2.authUserId,
25573
+ codes
25574
+ });
25575
+ if (input2.info.status === 2 || input2.info.isFrozen === true) {
25576
+ throw new CliError("\u8D44\u6E90\u5DF2\u88AB\u51BB\u7ED3\uFF0C\u4E0D\u80FD\u4FEE\u6539", codes.frozen);
25577
+ }
25578
+ }
25579
+ function assertRemoteResourceOwned(input2) {
25580
+ const codes = {
25581
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25582
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER"
25583
+ };
25584
+ if (input2.info.resourceId !== input2.resourceId) {
25585
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25586
+ }
25587
+ const ownerId = input2.info.userId ?? input2.info.ownerId ?? input2.info.creatorId;
25588
+ if (typeof ownerId !== "number" || !Number.isSafeInteger(ownerId)) {
25589
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8D44\u6E90 owner\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25590
+ }
25591
+ if (ownerId !== input2.authUserId) {
25592
+ throw new CliError("\u53EA\u80FD\u4FEE\u6539\u81EA\u5DF1\u7684\u8D44\u6E90", codes.notOwner);
25593
+ }
25594
+ }
25595
+
25596
+ // src/platform/unwrap.ts
25597
+ function unwrapData2(result) {
25598
+ const envelope = result;
25599
+ const data = envelope.data ?? result;
25600
+ return data ?? {};
25601
+ }
25602
+ function unwrapFirst(result) {
25603
+ const envelope = result;
25604
+ const data = envelope.data ?? result;
25605
+ if (Array.isArray(data)) {
25606
+ return data[0] ?? {};
25607
+ }
25608
+ return data ?? {};
25609
+ }
25610
+ function unwrapList2(result, fields = ["dataList", "list"]) {
25611
+ const envelope = result;
25612
+ const data = envelope.data ?? result;
25613
+ if (Array.isArray(data)) {
25614
+ return data;
25615
+ }
25616
+ if (data && typeof data === "object") {
25617
+ for (const field of fields) {
25618
+ const list2 = data[field];
25619
+ if (Array.isArray(list2)) {
25620
+ return list2;
25621
+ }
25622
+ }
25623
+ }
25624
+ return [];
25625
+ }
25364
25626
 
25365
25627
  // src/domain/listing/update.ts
25366
25628
  var MAX_TITLE = 100;
25367
25629
  var MAX_INTRO = 200;
25368
25630
  var MAX_TAGS = 20;
25369
25631
  var MAX_TAG_LENGTH = 20;
25632
+ var MAX_COVER_BYTES = 5 * 1024 * 1024;
25633
+ var COVER_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif"]);
25370
25634
  function assertTitle(title) {
25635
+ if (!title) {
25636
+ throw new CliError("\u8BF7\u8F93\u5165\u8D44\u6E90\u6807\u9898", "UPDATE_TITLE_REQUIRED");
25637
+ }
25371
25638
  if (title.length > MAX_TITLE) {
25372
25639
  throw new CliError("\u6807\u9898\u4E0D\u80FD\u8D85\u8FC7100\u4E2A\u5B57\u7B26", "UPDATE_TITLE_LONG");
25373
25640
  }
25374
25641
  }
25642
+ function isAnimatedGif(content) {
25643
+ if (content.subarray(0, 6).toString("ascii") !== "GIF87a" && content.subarray(0, 6).toString("ascii") !== "GIF89a" || content.length < 13) {
25644
+ return true;
25645
+ }
25646
+ let offset = 13;
25647
+ if ((content[10] & 128) !== 0) {
25648
+ offset += 3 * 2 ** ((content[10] & 7) + 1);
25649
+ }
25650
+ let frames = 0;
25651
+ while (offset < content.length) {
25652
+ const marker = content[offset++];
25653
+ if (marker === 59) return frames > 1;
25654
+ if (marker === 44) {
25655
+ frames += 1;
25656
+ if (frames > 1) return true;
25657
+ if (offset + 9 > content.length) return true;
25658
+ const packed = content[offset + 8];
25659
+ offset += 9;
25660
+ if ((packed & 128) !== 0) offset += 3 * 2 ** ((packed & 7) + 1);
25661
+ offset += 1;
25662
+ } else if (marker === 33) {
25663
+ offset += 1;
25664
+ } else {
25665
+ return true;
25666
+ }
25667
+ while (offset < content.length) {
25668
+ const size = content[offset++];
25669
+ if (size === 0) break;
25670
+ offset += size;
25671
+ }
25672
+ }
25673
+ return true;
25674
+ }
25675
+ function assertCoverContent(extension, content) {
25676
+ const isJpeg = content.length >= 3 && content[0] === 255 && content[1] === 216 && content[2] === 255;
25677
+ const isPng = content.length >= 8 && content.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
25678
+ const isGif = content.subarray(0, 6).toString("ascii") === "GIF87a" || content.subarray(0, 6).toString("ascii") === "GIF89a";
25679
+ if ((extension === ".jpg" || extension === ".jpeg") && isJpeg) return;
25680
+ if (extension === ".png" && isPng) return;
25681
+ if (extension === ".gif" && isGif && !isAnimatedGif(content)) return;
25682
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F JPG\u3001PNG \u6216\u9759\u6001 GIF \u56FE\u7247", "UPDATE_COVER_INVALID");
25683
+ }
25684
+ async function uploadCover(cwd, cover, apis) {
25685
+ const normalized = normalizeProjectPath(cwd, cover, {
25686
+ code: "UPDATE_COVER_OUTSIDE",
25687
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25688
+ });
25689
+ const localPath = resolveExistingProjectPath(cwd, path11.resolve(cwd, normalized), {
25690
+ code: "UPDATE_COVER_OUTSIDE",
25691
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25692
+ });
25693
+ if (!existsSync10(localPath)) {
25694
+ throw new CliError(`\u5C01\u9762\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${normalized}`, "UPDATE_COVER_MISSING");
25695
+ }
25696
+ const stats = statSync2(localPath);
25697
+ if (!stats.isFile()) {
25698
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F\u4E00\u4E2A\u56FE\u7247\u6587\u4EF6", "UPDATE_COVER_INVALID");
25699
+ }
25700
+ if (stats.size > MAX_COVER_BYTES) {
25701
+ throw new CliError("\u5C01\u9762\u4E0D\u80FD\u8D85\u8FC7 5MB", "UPDATE_COVER_TOO_LARGE");
25702
+ }
25703
+ const extension = path11.extname(normalized).toLowerCase();
25704
+ if (!COVER_EXTENSIONS.has(extension)) {
25705
+ throw new CliError("\u5C01\u9762\u53EA\u652F\u6301 JPG\u3001PNG \u6216\u9759\u6001 GIF", "UPDATE_COVER_INVALID");
25706
+ }
25707
+ const content = readFileSync7(localPath);
25708
+ assertCoverContent(extension, content);
25709
+ const upload = apis?.uploadImage ?? ((params) => service_API_default.Storage.uploadImage(params));
25710
+ const result = unwrapData2(await upload({ file: content }));
25711
+ const url2 = result.url ?? result.fileUrl ?? result.fileURL;
25712
+ if (typeof url2 !== "string" || !url2.trim()) {
25713
+ throw new CliError("\u5C01\u9762\u4E0A\u4F20\u6210\u529F\u4F46\u5E73\u53F0\u672A\u8FD4\u56DE\u56FE\u7247 URL", "UPDATE_COVER_UPLOAD_INVALID");
25714
+ }
25715
+ return url2;
25716
+ }
25375
25717
  function assertIntro(intro) {
25376
25718
  if (intro.length > MAX_INTRO) {
25377
25719
  throw new CliError("\u7B80\u4ECB\u4E0D\u80FD\u8D85\u8FC7200\u4E2A\u5B57\u7B26", "UPDATE_INTRO_LONG");
@@ -25385,9 +25727,6 @@ function assertTags(tags) {
25385
25727
  if (tag.length > MAX_TAG_LENGTH) {
25386
25728
  throw new CliError("\u5355\u4E2A\u6807\u7B7E\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC720\u4E2A\u5B57\u7B26", "UPDATE_TAG_LONG");
25387
25729
  }
25388
- if (tag.includes("#")) {
25389
- throw new CliError(`\u6807\u7B7E\u4E0D\u80FD\u5305\u542B #\uFF1A${tag}`, "UPDATE_TAG_INVALID");
25390
- }
25391
25730
  }
25392
25731
  if (tags.some((tag, i) => tags.indexOf(tag) !== i)) {
25393
25732
  throw new CliError("\u8BE5\u6807\u7B7E\u5DF2\u6DFB\u52A0", "UPDATE_TAG_DUPLICATE");
@@ -25395,34 +25734,47 @@ function assertTags(tags) {
25395
25734
  }
25396
25735
  async function updateListing(input2) {
25397
25736
  assertPlatformAllowed();
25398
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25399
- if (input2.yes && !input2.title && !input2.intro && !input2.cover && !input2.tags) {
25737
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25738
+ if (input2.yes && input2.title === void 0 && input2.intro === void 0 && input2.cover === void 0 && input2.tags === void 0) {
25400
25739
  throw new CliError("--yes \u4E14\u65E0\u4FEE\u6539\u9879\uFF0C\u62D2\u7EDD\u7A7A\u66F4\u65B0", "UPDATE_NO_FLAGS");
25401
25740
  }
25402
25741
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
25742
+ const info2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
25743
+ const remote = unwrapData2(await info2({ resourceIdOrName: identity.resourceId }));
25744
+ assertRemoteResourceWritable({ info: remote, resourceId: identity.resourceId, authUserId: auth.userId });
25403
25745
  const payload = {
25404
25746
  resourceId: identity.resourceId
25405
25747
  };
25406
- if (input2.title) {
25407
- assertTitle(input2.title);
25408
- payload.resourceTitle = input2.title;
25748
+ const title = input2.title?.trim();
25749
+ if (input2.title !== void 0) {
25750
+ assertTitle(title ?? "");
25751
+ payload.resourceTitle = title;
25409
25752
  }
25410
- if (input2.intro) {
25411
- assertIntro(input2.intro);
25412
- payload.intro = input2.intro;
25753
+ if (input2.intro !== void 0) {
25754
+ const intro = input2.intro.trim();
25755
+ assertIntro(intro);
25756
+ payload.intro = intro;
25757
+ }
25758
+ if (input2.cover !== void 0) {
25759
+ if (!input2.cover.trim()) {
25760
+ 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");
25761
+ }
25762
+ payload.coverImages = [await uploadCover(input2.cwd, input2.cover, input2.apis)];
25413
25763
  }
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;
25764
+ if (input2.tags !== void 0) {
25765
+ const rawTags = input2.tags === "" ? [] : input2.tags.split(",").map((item) => item.trim().replaceAll("#", ""));
25766
+ if (rawTags.some((tag) => !tag)) {
25767
+ throw new CliError("\u6807\u7B7E\u4E0D\u80FD\u5305\u542B\u7A7A\u9879", "UPDATE_TAG_EMPTY");
25768
+ }
25769
+ assertTags(rawTags);
25770
+ payload.tags = rawTags;
25419
25771
  }
25420
25772
  if ("status" in payload) {
25421
25773
  delete payload.status;
25422
25774
  }
25423
25775
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
25424
25776
  await update2(payload);
25425
- if (input2.title) updateIdentity(input2.cwd, identity.n, { title: input2.title });
25777
+ if (title !== void 0) updateIdentity(input2.cwd, identity.n, { title });
25426
25778
  return payload;
25427
25779
  }
25428
25780
 
@@ -25448,27 +25800,13 @@ function createUpdateCommand() {
25448
25800
  }
25449
25801
 
25450
25802
  // src/commands/policy/apply.ts
25451
- import { readFileSync as readFileSync6 } from "fs";
25452
- import path11 from "path";
25803
+ import { readFileSync as readFileSync8 } from "fs";
25804
+ import path12 from "path";
25453
25805
  import { confirm, input } from "@inquirer/prompts";
25454
25806
  import { Command as Command6 } from "commander";
25455
25807
 
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
25808
  // src/domain/policy/list.ts
25809
+ var POLICY_TEMPLATE_PAGE_SIZE = 20;
25472
25810
  function infoApi(apis) {
25473
25811
  return apis?.info ?? ((params) => service_API_default.Resource.info(params));
25474
25812
  }
@@ -25497,14 +25835,17 @@ function decoded(value) {
25497
25835
  return value;
25498
25836
  }
25499
25837
  }
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
- }
25838
+ function assertEditable(info2, authUserId, resourceId) {
25839
+ assertRemoteResourceWritable({
25840
+ info: info2,
25841
+ resourceId,
25842
+ authUserId,
25843
+ codes: {
25844
+ invalid: "POLICY_INFO_INVALID",
25845
+ notOwner: "POLICY_NOT_OWNER",
25846
+ frozen: "POLICY_RESOURCE_FROZEN"
25847
+ }
25848
+ });
25508
25849
  }
25509
25850
  async function loadPolicyContext(input2) {
25510
25851
  assertPlatformAllowed();
@@ -25523,7 +25864,7 @@ async function loadPolicyContext(input2) {
25523
25864
  isLoadPolicyInfo: 1,
25524
25865
  isTranslate: 1
25525
25866
  }));
25526
- if (input2.editable) assertEditable(info2, auth.userId);
25867
+ if (input2.editable) assertEditable(info2, auth.userId, resourceId);
25527
25868
  return {
25528
25869
  resourceId,
25529
25870
  typeCode,
@@ -25534,55 +25875,97 @@ async function loadPolicyContext(input2) {
25534
25875
  function templateFrom(value) {
25535
25876
  if (!value || typeof value !== "object") return void 0;
25536
25877
  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;
25878
+ const id = raw.id ?? raw.templateId ?? raw.policyTemplateId ?? raw._id;
25879
+ const name = raw.name ?? raw.templateName ?? raw.policyName ?? raw.title;
25880
+ const defaultValue = raw.defaultValue ?? raw.policyText ?? raw.value ?? raw.template;
25540
25881
  if (typeof id !== "string" || !id || typeof name !== "string" || !name || typeof defaultValue !== "string" || !defaultValue.trim()) {
25541
25882
  return void 0;
25542
25883
  }
25543
25884
  const summaryValue = raw.summary ?? raw.description ?? raw.eventSummary;
25885
+ const fillArgs = Array.isArray(raw.reportUiTemplate) ? raw.reportUiTemplate.flatMap((item) => {
25886
+ if (!item || typeof item !== "object") return [];
25887
+ const field = item;
25888
+ const name2 = field.id;
25889
+ const value2 = field.uiSectionDefaultValue;
25890
+ return typeof name2 === "string" && name2 && (typeof value2 === "string" || typeof value2 === "number") ? [{ name: name2, value: value2 }] : [];
25891
+ }) : [];
25544
25892
  return {
25545
25893
  id,
25546
25894
  name,
25547
25895
  defaultValue,
25896
+ fillArgs,
25548
25897
  ...typeof summaryValue === "string" && summaryValue.trim() ? { summary: summaryValue.trim() } : {}
25549
25898
  };
25550
25899
  }
25900
+ function normalizeCompiledTemplateDsl(contract2) {
25901
+ return contract2.replace(/\bfor\s+public\b/gi, "FOR PUBLIC").replace(/\binitial\b/gi, "Initial");
25902
+ }
25903
+ function compiledContract(result) {
25904
+ const envelope = result;
25905
+ const data = envelope.data ?? result;
25906
+ const contract2 = data && typeof data === "object" ? data.contractNew ?? data.contract : data;
25907
+ if (typeof contract2 !== "string" || !contract2.trim()) {
25908
+ throw new CliError("\u7B56\u7565\u6A21\u677F\u7F16\u8BD1\u7ED3\u679C\u7F3A\u5C11 contractNew", "POLICY_TEMPLATE_COMPILE_INVALID");
25909
+ }
25910
+ return normalizeCompiledTemplateDsl(contract2).trim();
25911
+ }
25551
25912
  async function getPolicyTemplates(input2) {
25552
- const context = await loadPolicyContext(input2);
25913
+ await loadPolicyContext(input2);
25553
25914
  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 : [];
25915
+ const rawList = unwrapList2(await request2({}), ["list", "dataList", "templates"]);
25556
25916
  return rawList.flatMap((item) => {
25557
25917
  const template = templateFrom(item);
25558
25918
  return template ? [template] : [];
25559
25919
  });
25560
25920
  }
25561
- function positiveInteger(value, fallback, name, max) {
25562
- const actual = value ?? fallback;
25563
- if (!Number.isInteger(actual) || actual < 1 || actual > max) {
25564
- throw new CliError(`${name} \u5FC5\u987B\u662F 1\u2013${max} \u7684\u6574\u6570`, "POLICY_TEMPLATE_PAGE");
25565
- }
25566
- return actual;
25921
+ async function applyPolicyTemplate(input2) {
25922
+ const template = (await getPolicyTemplates(input2)).find((item) => item.id === input2.templateId);
25923
+ if (!template) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25924
+ const request2 = input2.apis?.policyReCompile ?? ((params) => service_API_default.Policy.policyReCompile(params));
25925
+ const policyText = compiledContract(await request2({ _id: template.id, fillArgs: template.fillArgs }));
25926
+ await applyPolicy({ ...input2, policyText });
25567
25927
  }
25568
- async function listPolicies(input2) {
25928
+ async function getPolicyList(input2) {
25569
25929
  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");
25572
- }
25573
- async function listPolicyTemplates(input2) {
25574
- const page = positiveInteger(input2.page, 1, "--page", Number.MAX_SAFE_INTEGER);
25575
- const pageSize2 = positiveInteger(input2.pageSize, 20, "--page-size", 100);
25576
- const templates = await getPolicyTemplates(input2);
25577
- const pageCount = Math.max(1, Math.ceil(templates.length / pageSize2));
25578
- if (page > pageCount) {
25579
- throw new CliError(`--page \u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5171 ${pageCount} \u9875`, "POLICY_TEMPLATE_PAGE");
25580
- }
25581
- const rows = templates.slice((page - 1) * pageSize2, page * pageSize2).map((item) => [item.id, item.name, item.summary].filter(Boolean).join(" "));
25582
- const next = page < pageCount ? `
25583
- \u4E0B\u4E00\u9875\uFF1A--page ${page + 1}` : "";
25584
- return `\u7B2C ${page}/${pageCount} \u9875\uFF0C\u5171 ${templates.length} \u6761${rows.length ? `
25585
- ${rows.join("\n")}` : ""}${next}`;
25930
+ const typeHierarchy = await getTypeHierarchy(context.typeCode, input2.apis);
25931
+ return {
25932
+ typeHierarchy,
25933
+ policies: [...context.policies].sort((a, b) => b.status - a.status || a.policyName.localeCompare(b.policyName))
25934
+ };
25935
+ }
25936
+ function formatPolicyList(list2) {
25937
+ const header = [
25938
+ `\u8D44\u6E90\u7C7B\u578B\uFF1A${list2.typeHierarchy.join(" / ")}`,
25939
+ `\u5171 ${list2.policies.length} \u6761\u6388\u6743\u7B56\u7565`
25940
+ ];
25941
+ if (list2.policies.length === 0) return [...header, "\u8FD8\u6CA1\u6709\u6388\u6743\u7B56\u7565"].join("\n");
25942
+ return [
25943
+ ...header,
25944
+ ...list2.policies.map((item) => `${item.policyId} ${item.policyName} ${item.status === 1 ? "on" : "off"}`)
25945
+ ].join("\n");
25946
+ }
25947
+ function policyTemplatePage(templates, page) {
25948
+ const pageCount = Math.max(1, Math.ceil(templates.length / POLICY_TEMPLATE_PAGE_SIZE));
25949
+ if (!Number.isInteger(page) || page < 1 || page > pageCount) {
25950
+ throw new CliError(`\u7B56\u7565\u6A21\u677F\u9875\u7801\u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5171 ${pageCount} \u9875`, "POLICY_TEMPLATE_PAGE");
25951
+ }
25952
+ return {
25953
+ page,
25954
+ pageCount,
25955
+ total: templates.length,
25956
+ hasPrevious: page > 1,
25957
+ hasNext: page < pageCount,
25958
+ items: templates.slice((page - 1) * POLICY_TEMPLATE_PAGE_SIZE, page * POLICY_TEMPLATE_PAGE_SIZE)
25959
+ };
25960
+ }
25961
+ function formatPolicyTemplatePage(page) {
25962
+ const header = `\u7B2C ${page.page}/${page.pageCount} \u9875\uFF0C\u5171 ${page.total} \u4E2A\u6388\u6743\u7B56\u7565\u6A21\u677F`;
25963
+ if (page.items.length === 0) return `${header}
25964
+ \u6CA1\u6709\u53EF\u7528\u6388\u6743\u7B56\u7565\u6A21\u677F`;
25965
+ return [
25966
+ header,
25967
+ ...page.items.map((item) => [item.id, item.name, item.summary].filter(Boolean).join(" "))
25968
+ ].join("\n");
25586
25969
  }
25587
25970
  function assertPolicyInput(context, policyName, policyText) {
25588
25971
  const name = policyName.trim();
@@ -25627,8 +26010,8 @@ async function setPolicy(input2) {
25627
26010
 
25628
26011
  // src/commands/policy/apply.ts
25629
26012
  function parsePolicy(file) {
25630
- const text = readFileSync6(file, "utf8");
25631
- if (path11.extname(file).toLowerCase() !== ".json") return { policyText: text };
26013
+ const text = readFileSync8(file, "utf8");
26014
+ if (path12.extname(file).toLowerCase() !== ".json") return { policyText: text };
25632
26015
  let parsed;
25633
26016
  try {
25634
26017
  parsed = JSON.parse(text);
@@ -25667,13 +26050,14 @@ function createPolicyListCommand() {
25667
26050
  const command = addSharedOptions(new Command7("list"));
25668
26051
  command.description(
25669
26052
  // i18n: cli.command.policy.list.description
25670
- "\u770B\u5DF2\u6709\u7B56\u7565"
26053
+ "\u67E5\u770B\u5F53\u524D\u8D44\u6E90\u7684\u5168\u90E8\u6388\u6743\u7B56\u7565\u53CA\u8D44\u6E90\u7C7B\u578B\u5C42\u7EA7"
25671
26054
  ).action(async function() {
25672
26055
  const shared = readSharedOptions(this);
25673
- console.log(await listPolicies({
26056
+ const list2 = await getPolicyList({
25674
26057
  cwd: resolveCwd(shared.cwd),
25675
26058
  file: shared.file
25676
- }));
26059
+ });
26060
+ console.log(formatPolicyList(list2));
25677
26061
  });
25678
26062
  return command;
25679
26063
  }
@@ -25695,22 +26079,39 @@ function createPolicySetCommand() {
25695
26079
  }
25696
26080
 
25697
26081
  // src/commands/policy/template.ts
25698
- import { confirm as confirm3, select } from "@inquirer/prompts";
26082
+ import { confirm as confirm3 } from "@inquirer/prompts";
25699
26083
  import { Command as Command9 } from "commander";
26084
+ async function showTemplatePages(templates) {
26085
+ let page = 1;
26086
+ while (true) {
26087
+ const current = policyTemplatePage(templates, page);
26088
+ console.log(formatPolicyTemplatePage(current));
26089
+ if (!current.hasPrevious && !current.hasNext) return;
26090
+ if (!isInteractive()) {
26091
+ if (current.hasNext) {
26092
+ console.log(`\u8FD8\u6709 ${current.total - current.page * POLICY_TEMPLATE_PAGE_SIZE} \u4E2A\u6388\u6743\u7B56\u7565\u6A21\u677F\uFF1B\u8BF7\u5728\u4EA4\u4E92\u7EC8\u7AEF\u8FD0\u884C policy template list \u67E5\u770B\u540E\u7EED\u9875`);
26093
+ }
26094
+ return;
26095
+ }
26096
+ const action = await selectQuestion("\u6388\u6743\u7B56\u7565\u6A21\u677F\u5217\u8868", [
26097
+ ...current.hasPrevious ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
26098
+ ...current.hasNext ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
26099
+ { name: "\u9000\u51FA", value: "__exit__" }
26100
+ ]);
26101
+ if (action === "__exit__") return;
26102
+ page += action === "__next__" ? 1 : -1;
26103
+ }
26104
+ }
25700
26105
  async function chooseTemplate(templates) {
25701
- let page = 0;
25702
- const pageSize2 = 20;
26106
+ let page = 1;
25703
26107
  while (true) {
25704
- const slice = templates.slice(page * pageSize2, (page + 1) * pageSize2);
25705
- const value = await select({
25706
- message: `\u8BF7\u9009\u62E9\u6388\u6743\u7B56\u7565\u6A21\u677F\uFF08\u7B2C ${page + 1}/${Math.ceil(templates.length / pageSize2)} \u9875\uFF09`,
25707
- choices: [
25708
- ...slice.map((item) => ({ name: `${item.name} (${item.id})${item.summary ? ` \u2014 ${item.summary}` : ""}`, value: item.id })),
25709
- ...page > 0 ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
25710
- ...page < Math.ceil(templates.length / pageSize2) - 1 ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
25711
- { name: "\u53D6\u6D88", value: "__cancel__" }
25712
- ]
25713
- });
26108
+ const current = policyTemplatePage(templates, page);
26109
+ const value = await selectQuestion(`\u8BF7\u9009\u62E9\u6388\u6743\u7B56\u7565\u6A21\u677F\uFF08\u7B2C ${current.page}/${current.pageCount} \u9875\uFF09`, [
26110
+ ...current.items.map((item) => ({ name: `${item.name} (${item.id})${item.summary ? ` \u2014 ${item.summary}` : ""}`, value: item.id })),
26111
+ ...current.hasPrevious ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
26112
+ ...current.hasNext ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
26113
+ { name: "\u53D6\u6D88", value: "__cancel__" }
26114
+ ]);
25714
26115
  if (value === "__previous__") {
25715
26116
  page -= 1;
25716
26117
  continue;
@@ -25725,9 +26126,9 @@ async function chooseTemplate(templates) {
25725
26126
  }
25726
26127
  function createPolicyTemplateCommand() {
25727
26128
  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) {
26129
+ addSharedOptions(template.command("list")).description("\u5217\u5F53\u524D\u6A21\u677F\u5217\u8868\uFF08\u6BCF\u9875 20 \u6761\uFF09").action(async function() {
25729
26130
  const shared = readSharedOptions(this);
25730
- console.log(await listPolicyTemplates({ cwd: resolveCwd(shared.cwd), file: shared.file, page: options.page, pageSize: options.pageSize }));
26131
+ await showTemplatePages(await getPolicyTemplates({ cwd: resolveCwd(shared.cwd), file: shared.file }));
25731
26132
  });
25732
26133
  addSharedOptions(template.command("apply")).description("\u5E94\u7528\u5F53\u524D\u7C7B\u578B\u7684\u4E00\u6761\u6A21\u677F\u5E76\u542F\u7528").argument("[templateId]", "\u6A21\u677F\u7F16\u53F7").option("--name <name>", "\u7B56\u7565\u540D").action(async function(templateId, options) {
25733
26134
  const shared = readSharedOptions(this);
@@ -25735,7 +26136,7 @@ function createPolicyTemplateCommand() {
25735
26136
  const yes = shared.yes === true;
25736
26137
  const templates = await getPolicyTemplates({ cwd, file: shared.file });
25737
26138
  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");
26139
+ if (!item && templateId) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25739
26140
  if (!item) {
25740
26141
  if (yes || !process.stdin.isTTY) throw new CliError("--yes \u6216\u975E\u4EA4\u4E92\u6A21\u5F0F\u5FC5\u987B\u63D0\u4F9B templateId", "POLICY_TEMPLATE_ID");
25741
26142
  item = await chooseTemplate(templates);
@@ -25743,7 +26144,7 @@ function createPolicyTemplateCommand() {
25743
26144
  }
25744
26145
  const policyName = options.name ?? item.name;
25745
26146
  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 });
26147
+ await applyPolicyTemplate({ cwd, file: shared.file, templateId: item.id, policyName });
25747
26148
  console.log("\u5DF2\u6DFB\u52A0\u5E76\u542F\u7528\u6388\u6743\u7B56\u7565");
25748
26149
  });
25749
26150
  return template;
@@ -25753,28 +26154,18 @@ function createPolicyTemplateCommand() {
25753
26154
  import { Command as Command10 } from "commander";
25754
26155
 
25755
26156
  // 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
26157
+ import { existsSync as existsSync11 } from "fs";
25766
26158
  import path13 from "path";
25767
26159
  async function bindResource(input2) {
25768
26160
  assertPlatformAllowed();
25769
26161
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25770
- validateLocalState(input2.cwd);
25771
26162
  const filePath = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25772
26163
  code: "BIND_FILE_OUTSIDE",
25773
26164
  message: "--artifact \u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
25774
26165
  }) : void 0;
25775
26166
  if (filePath) {
25776
26167
  const absolute = path13.resolve(input2.cwd, filePath);
25777
- if (!existsSync10(absolute)) {
26168
+ if (!existsSync11(absolute)) {
25778
26169
  throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${absolute}`, "ARTIFACT_ANCHOR_MISSING");
25779
26170
  }
25780
26171
  }
@@ -25791,23 +26182,34 @@ async function bindResource(input2) {
25791
26182
  if (!isResourceSubject(info2.subjectType)) {
25792
26183
  throw new CliError("\u53EA\u80FD bind \u5355\u8D44\u6E90", "BIND_SUBJECT_INVALID");
25793
26184
  }
25794
- if (info2.userId !== auth.userId) {
25795
- throw new CliError("\u53EA\u80FD bind \u81EA\u5DF1\u7684\u8D44\u6E90", "BIND_NOT_OWNER");
25796
- }
25797
26185
  const resourceId = String(info2.resourceId ?? "");
25798
- const name = String(info2.resourceName ?? info2.name ?? "").split("/").pop() ?? "";
26186
+ if (!resourceId) {
26187
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
26188
+ }
26189
+ assertRemoteResourceOwned({
26190
+ info: info2,
26191
+ resourceId,
26192
+ authUserId: auth.userId,
26193
+ codes: {
26194
+ invalid: "BIND_INFO_INVALID",
26195
+ notOwner: "BIND_NOT_OWNER"
26196
+ }
26197
+ });
26198
+ const resourceName = String(info2.resourceName ?? "");
26199
+ const name = (resourceName || String(info2.name ?? "")).split("/").pop() ?? "";
25799
26200
  const title = String(info2.resourceTitle ?? info2.title ?? name);
25800
26201
  const resourceType = info2.resourceType;
25801
26202
  const typeFromArray = Array.isArray(resourceType) ? String(resourceType[0] ?? "") : "";
25802
26203
  const typeCode = String(info2.resourceTypeCode ?? typeFromArray);
25803
- if (!resourceId || !name || !typeCode || !title) {
26204
+ if (!name || !typeCode || !title) {
25804
26205
  throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
25805
26206
  }
26207
+ const stableResourceName = resourceName || `${auth.loginName}/${name}`;
25806
26208
  return withProjectLock(input2.cwd, () => {
25807
26209
  const identities = validateLocalState(input2.cwd);
25808
26210
  const byId = identities.find((item) => item.resourceId === resourceId);
25809
26211
  const byFile = filePath ? identities.find(
25810
- (item) => item.filePath !== void 0 && normalizeFileKey(item.filePath) === filePath
26212
+ (item) => normalizeProjectPath(input2.cwd, item.filePath) === filePath
25811
26213
  ) : void 0;
25812
26214
  const unbound = identities.filter((item) => !item.resourceId);
25813
26215
  if (byFile && byId && byFile.n !== byId.n) {
@@ -25817,7 +26219,10 @@ async function bindResource(input2) {
25817
26219
  if (input2.selector && byId && target && target.n !== byId.n) {
25818
26220
  throw new CliError(`\u8D44\u6E90\u5DF2\u7ED1\u5B9A\u5230 ${byId.n}.json`, "BIND_ID_TAKEN");
25819
26221
  }
25820
- if (!target && unbound.length === 1) {
26222
+ if (!target && filePath) {
26223
+ target = unbound.find((item) => item.filePath === filePath);
26224
+ }
26225
+ if (!target && filePath === void 0 && unbound.length === 1) {
25821
26226
  target = unbound[0];
25822
26227
  }
25823
26228
  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 +26233,15 @@ async function bindResource(input2) {
25828
26233
  if (!resolvedFilePath) {
25829
26234
  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
26235
  }
25831
- assertArtifactAnchor(typeCode, path13.resolve(input2.cwd, resolvedFilePath));
26236
+ assertArtifactAnchor(typeCode, path13.resolve(input2.cwd, resolvedFilePath), input2.cwd);
25832
26237
  const env = getEnv2();
25833
26238
  if (target) {
26239
+ if (target.resourceId && (target.env ?? "prod") !== env) {
26240
+ throw new CliError(
26241
+ `\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`,
26242
+ "BIND_ENV_MISMATCH"
26243
+ );
26244
+ }
25834
26245
  const resourceChanged = target.resourceId !== resourceId;
25835
26246
  const typeChanged = target.typeCode !== typeCode;
25836
26247
  if (target.resourceId && resourceChanged) {
@@ -25841,6 +26252,7 @@ async function bindResource(input2) {
25841
26252
  const discardDraft = target.resourceId && resourceChanged || typeChanged;
25842
26253
  const updated = prepareIdentityUpdate(input2.cwd, target.n, {
25843
26254
  resourceId,
26255
+ resourceName: stableResourceName,
25844
26256
  name,
25845
26257
  title,
25846
26258
  typeCode,
@@ -25856,6 +26268,7 @@ async function bindResource(input2) {
25856
26268
  const created = prepareIdentityCreate(input2.cwd, {
25857
26269
  subject: "resource",
25858
26270
  resourceId,
26271
+ resourceName: stableResourceName,
25859
26272
  name,
25860
26273
  title,
25861
26274
  typeCode,
@@ -25863,7 +26276,8 @@ async function bindResource(input2) {
25863
26276
  env
25864
26277
  });
25865
26278
  commitLocalTransaction(input2.cwd, [
25866
- { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) }
26279
+ { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) },
26280
+ { path: identitySequenceFilePath(input2.cwd), content: serializeIdentitySequence(created.n) }
25867
26281
  ]);
25868
26282
  return created;
25869
26283
  });
@@ -25911,7 +26325,7 @@ import { Command as Command11 } from "commander";
25911
26325
 
25912
26326
  // src/domain/init/scaffold.ts
25913
26327
  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";
26328
+ import { cpSync, existsSync as existsSync12, lstatSync as lstatSync2, mkdirSync as mkdirSync2, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync9, renameSync, rmSync } from "fs";
25915
26329
  import os4 from "os";
25916
26330
  import path14 from "path";
25917
26331
  import { promisify } from "util";
@@ -25943,7 +26357,7 @@ function resolveTargetDir(input2) {
25943
26357
  return input2.dir ? path14.isAbsolute(input2.dir) ? input2.dir : path14.resolve(input2.cwd, input2.dir) : path14.resolve(input2.cwd);
25944
26358
  }
25945
26359
  function targetHasOnlyAuthSelector(targetDir) {
25946
- if (!existsSync11(targetDir)) return false;
26360
+ if (!existsSync12(targetDir)) return false;
25947
26361
  let entries;
25948
26362
  try {
25949
26363
  entries = readdirSync4(targetDir);
@@ -25956,20 +26370,20 @@ function targetHasOnlyAuthSelector(targetDir) {
25956
26370
  return stateDirHasOnlyAuthSelector(stateDir);
25957
26371
  }
25958
26372
  function stateDirHasOnlyAuthSelector(stateDir) {
25959
- if (!existsSync11(stateDir) || !lstatSync(stateDir).isDirectory() || lstatSync(stateDir).isSymbolicLink()) return false;
26373
+ if (!existsSync12(stateDir) || !lstatSync2(stateDir).isDirectory() || lstatSync2(stateDir).isSymbolicLink()) return false;
25960
26374
  const stateEntries = readdirSync4(stateDir);
25961
26375
  if (stateEntries.length !== 1 || stateEntries[0] !== "auth") return false;
25962
- const auth = lstatSync(path14.join(stateDir, "auth"));
26376
+ const auth = lstatSync2(path14.join(stateDir, "auth"));
25963
26377
  return auth.isFile() && !auth.isSymbolicLink();
25964
26378
  }
25965
26379
  function assertInitialTarget(targetDir, adoptExistingProject) {
25966
- if (!existsSync11(targetDir)) return { targetExisted: false, preserveAuth: false, adoptExistingProject };
26380
+ if (!existsSync12(targetDir)) return { targetExisted: false, preserveAuth: false, adoptExistingProject };
25967
26381
  const entries = readdirSync4(targetDir);
25968
26382
  if (entries.length === 0) return { targetExisted: true, preserveAuth: false, adoptExistingProject };
25969
26383
  if (targetHasOnlyAuthSelector(targetDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject };
25970
26384
  if (adoptExistingProject) {
25971
26385
  const stateDir = path14.join(targetDir, ".freelog");
25972
- if (!existsSync11(stateDir)) return { targetExisted: true, preserveAuth: false, adoptExistingProject: true };
26386
+ if (!existsSync12(stateDir)) return { targetExisted: true, preserveAuth: false, adoptExistingProject: true };
25973
26387
  if (stateDirHasOnlyAuthSelector(stateDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject: true };
25974
26388
  }
25975
26389
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u4E0D\u662F\u7A7A\u76EE\u5F55\uFF0C\u62D2\u7EDD\u8986\u76D6", "INIT_TARGET_NOT_EMPTY");
@@ -25997,12 +26411,12 @@ function validateRemoteTemplate(packageDir, template) {
25997
26411
  const templateDir = path14.join(packageDir, "template");
25998
26412
  let manifest;
25999
26413
  try {
26000
- manifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
26414
+ manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
26001
26415
  } catch {
26002
26416
  throw new CliError("\u6A21\u677F\u6821\u9A8C\u5931\u8D25\uFF1A\u7F3A\u5C11\u6216\u65E0\u6CD5\u8BFB\u53D6 template.manifest.json", "TEMPLATE_INVALID");
26003
26417
  }
26004
26418
  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)) {
26419
+ 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
26420
  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
26421
  }
26008
26422
  return templateDir;
@@ -26040,7 +26454,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26040
26454
  if (preserveAuth) {
26041
26455
  assertOnlyAuthSelectorStillThere(targetDir);
26042
26456
  } else if (adoptExistingProject) {
26043
- if (existsSync11(path14.join(targetDir, ".freelog"))) {
26457
+ if (existsSync12(path14.join(targetDir, ".freelog"))) {
26044
26458
  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
26459
  }
26046
26460
  renameSync(path14.join(stagingDir, ".freelog"), path14.join(targetDir, ".freelog"));
@@ -26054,7 +26468,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26054
26468
  if (preserveAuth && entry2 === ".freelog") {
26055
26469
  for (const stateEntry of readdirSync4(path14.join(stagingDir, entry2))) {
26056
26470
  const stateDestination = path14.join(destination, stateEntry);
26057
- if (existsSync11(stateDestination)) {
26471
+ if (existsSync12(stateDestination)) {
26058
26472
  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
26473
  }
26060
26474
  renameSync(path14.join(stagingDir, entry2, stateEntry), stateDestination);
@@ -26062,7 +26476,7 @@ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adopt
26062
26476
  rmSync(path14.join(stagingDir, entry2), { recursive: true, force: true });
26063
26477
  continue;
26064
26478
  }
26065
- if (existsSync11(destination)) {
26479
+ if (existsSync12(destination)) {
26066
26480
  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
26481
  }
26068
26482
  renameSync(path14.join(stagingDir, entry2), destination);
@@ -26086,7 +26500,7 @@ async function initProject(input2) {
26086
26500
  }
26087
26501
  const typeCode = shortcut === "theme" ? "RT001" : shortcut === "widget" ? "RT002" : (await (input2.typeValidator ?? ((code) => getTypeInfo(code, input2.typeApis)))(input2.typeCode)).code;
26088
26502
  const artifact = shortcut ? "dist" : normalizeProjectPath(targetDir, input2.artifact);
26089
- if (!shortcut) assertArtifactAnchor(typeCode, path14.resolve(targetDir, artifact));
26503
+ if (!shortcut) assertArtifactAnchor(typeCode, path14.resolve(targetDir, artifact), targetDir);
26090
26504
  const template = shortcut && input2.template ? getTemplate(input2.template, shortcut) : void 0;
26091
26505
  if (shortcut && !template) throw new CliError("\u8BF7\u9009\u62E9\u6A21\u677F", "INIT_TEMPLATE_REQUIRED");
26092
26506
  const parent = path14.dirname(targetDir);
@@ -26188,6 +26602,7 @@ async function statusProject(input2) {
26188
26602
  if (local?.resourceId) {
26189
26603
  assertPlatformAllowed();
26190
26604
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26605
+ resolveBoundIdentity(input2.cwd, input2.file);
26191
26606
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26192
26607
  const result = await infoApi2({
26193
26608
  resourceIdOrName: local.resourceId,
@@ -26248,22 +26663,40 @@ function requireTypeAuth(command) {
26248
26663
  const shared = readSharedOptions(command);
26249
26664
  requireAuth({ cwd: resolveCwd(shared.cwd) });
26250
26665
  }
26666
+ async function showTypePages(items, commandText) {
26667
+ let page = 1;
26668
+ while (true) {
26669
+ const current = typeListPage(items, page);
26670
+ console.log(formatTypeListPage(current));
26671
+ if (!current.hasPrevious && !current.hasNext) return;
26672
+ if (!isInteractive()) {
26673
+ if (current.hasNext) {
26674
+ console.log(`\u8FD8\u6709 ${current.total - current.page * TYPE_LIST_PAGE_SIZE} \u4E2A\u8D44\u6E90\u7C7B\u578B\uFF1B\u8BF7\u5728\u4EA4\u4E92\u7EC8\u7AEF\u8FD0\u884C ${commandText} \u67E5\u770B\u540E\u7EED\u9875`);
26675
+ }
26676
+ return;
26677
+ }
26678
+ const action = await selectQuestion("\u8D44\u6E90\u7C7B\u578B\u5217\u8868", [
26679
+ ...current.hasPrevious ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
26680
+ ...current.hasNext ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
26681
+ { name: "\u9000\u51FA", value: "__exit__" }
26682
+ ]);
26683
+ if (action === "__exit__") return;
26684
+ page += action === "__next__" ? 1 : -1;
26685
+ }
26686
+ }
26251
26687
  function createTypeCommand() {
26252
26688
  const type = addSharedOptions(new Command14("type"));
26253
26689
  type.description(
26254
26690
  // i18n: cli.command.type.description
26255
- "\u67E5\u8BE2\u53F6\u5B50\u7C7B\u578B"
26691
+ "\u67E5\u8BE2\u53EF\u9009\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B"
26256
26692
  );
26257
26693
  addSharedOptions(type.command("list")).description(
26258
26694
  // i18n: cli.command.type.list.description
26259
- "\u5217\u51FA\u53F6\u5B50\u7C7B\u578B"
26695
+ "\u5217\u51FA\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84"
26260
26696
  ).action(async function() {
26261
26697
  requireTypeAuth(this);
26262
26698
  const items = await listLeafTypes();
26263
- const text = formatTypeList(items);
26264
- if (text) {
26265
- console.log(text);
26266
- }
26699
+ await showTypePages(items, "type list");
26267
26700
  });
26268
26701
  addSharedOptions(type.command("search")).description(
26269
26702
  // i18n: cli.command.type.search.description
@@ -26275,10 +26708,7 @@ function createTypeCommand() {
26275
26708
  ).action(async function(keyword) {
26276
26709
  requireTypeAuth(this);
26277
26710
  const items = await searchLeafTypes(keyword ?? "");
26278
- const text = formatTypeList(items);
26279
- if (text) {
26280
- console.log(text);
26281
- }
26711
+ await showTypePages(items, `type search ${keyword ?? ""}`.trim());
26282
26712
  });
26283
26713
  addSharedOptions(type.command("pick")).description(
26284
26714
  // i18n: cli.command.type.pick.description
@@ -26291,14 +26721,11 @@ function createTypeCommand() {
26291
26721
  requireTypeAuth(this);
26292
26722
  if (!options.type) {
26293
26723
  const items = await listLeafTypes();
26294
- const text = formatTypeList(items);
26295
- if (text) {
26296
- console.log(text);
26297
- }
26724
+ await showTypePages(items, "type pick");
26298
26725
  return;
26299
26726
  }
26300
26727
  const info2 = await getTypeInfo(options.type);
26301
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26728
+ console.log(formatTypeInfo(info2, await getTypeHierarchy(info2.code)));
26302
26729
  });
26303
26730
  addSharedOptions(type.command("info")).description(
26304
26731
  // i18n: cli.command.type.info.description
@@ -26313,7 +26740,7 @@ function createTypeCommand() {
26313
26740
  throw new CliError("\u8BF7\u63D0\u4F9B\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
26314
26741
  }
26315
26742
  const info2 = await getTypeInfo(code);
26316
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26743
+ console.log(formatTypeInfo(info2, await getTypeHierarchy(info2.code)));
26317
26744
  });
26318
26745
  return type;
26319
26746
  }
@@ -26340,6 +26767,9 @@ async function syncResourceTitles(input2) {
26340
26767
  if (!identity.resourceId) return { identity, error: "\u8D44\u6E90\u5C1A\u672A\u7ED1\u5B9A" };
26341
26768
  try {
26342
26769
  const data = unwrapData2(await info2({ resourceIdOrName: identity.resourceId, isLoadLatestVersionInfo: 1 }));
26770
+ if (data.resourceId !== identity.resourceId) {
26771
+ return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId" };
26772
+ }
26343
26773
  const title = String(data.resourceTitle ?? data.title ?? "");
26344
26774
  if (!title) return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6807\u9898" };
26345
26775
  return { identity, title };
@@ -26363,55 +26793,346 @@ async function syncResourceTitles(input2) {
26363
26793
  }, "sync-resource-titles");
26364
26794
  }
26365
26795
 
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;
26796
+ // src/local/pendingOperation.ts
26797
+ import { randomUUID as randomUUID2 } from "crypto";
26798
+ import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync as unlinkSync6 } from "fs";
26799
+ import path15 from "path";
26800
+ import { z as z4 } from "zod";
26801
+ var pendingVersionSubmitV1Schema = z4.object({
26802
+ schemaVersion: z4.literal(1),
26803
+ operationId: z4.string().uuid(),
26804
+ kind: z4.literal("version-submit"),
26805
+ resourceN: z4.number().int().positive(),
26806
+ resourceId: z4.string().min(1),
26807
+ env: z4.enum(["prod", "test", "dev"]),
26808
+ version: z4.string().min(1),
26809
+ fileSha1: z4.string().min(1),
26810
+ createdAt: z4.string().min(1)
26811
+ }).strict();
26812
+ var pendingVersionSubmitV2Schema = pendingVersionSubmitV1Schema.omit({ schemaVersion: true }).extend({
26813
+ schemaVersion: z4.literal(2),
26814
+ state: z4.enum(["prepared", "sending"])
26815
+ }).strict();
26816
+ function pendingOperationFilePath(cwd) {
26817
+ return path15.join(freelogDir(cwd), ".pending-operation.json");
26374
26818
  }
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
- );
26819
+ function invalidPending() {
26820
+ 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
26821
  }
26390
- function validateForOnline(info2) {
26391
- if (!info2.latestVersion) {
26392
- throw new CliError("\u4E0A\u67B6\u987B\u5DF2\u6709\u7248\u672C", "ONLINE_NO_VERSION");
26822
+ function readPendingOperation(cwd) {
26823
+ const filePath = pendingOperationFilePath(cwd);
26824
+ if (!existsSync13(filePath)) return void 0;
26825
+ let raw;
26826
+ try {
26827
+ raw = JSON.parse(readFileSync10(filePath, "utf8"));
26828
+ } catch {
26829
+ return invalidPending();
26393
26830
  }
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");
26831
+ const v2 = pendingVersionSubmitV2Schema.safeParse(raw);
26832
+ if (v2.success) return v2.data;
26833
+ const v1 = pendingVersionSubmitV1Schema.safeParse(raw);
26834
+ if (v1.success) {
26835
+ return { ...v1.data, state: "sending" };
26397
26836
  }
26837
+ return invalidPending();
26398
26838
  }
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
- });
26410
- }
26411
- async function offlineResource(input2) {
26412
- assertPlatformAllowed();
26413
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26839
+ function createPendingVersionSubmit(input2) {
26840
+ if (readPendingOperation(input2.cwd)) {
26841
+ throw new CliError("\u5B58\u5728\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C\uFF1B\u8BF7\u5148 resource recover", "PENDING_OPERATION_EXISTS");
26842
+ }
26843
+ const pending = {
26844
+ schemaVersion: 2,
26845
+ operationId: randomUUID2(),
26846
+ kind: "version-submit",
26847
+ resourceN: input2.resourceN,
26848
+ resourceId: input2.resourceId,
26849
+ env: input2.env,
26850
+ version: input2.version,
26851
+ fileSha1: input2.fileSha1,
26852
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
26853
+ state: "prepared"
26854
+ };
26855
+ atomicWriteFile(pendingOperationFilePath(input2.cwd), `${JSON.stringify(pending, null, 2)}
26856
+ `);
26857
+ return pending;
26858
+ }
26859
+ function markPendingVersionSubmitSending(cwd) {
26860
+ const pending = readPendingOperation(cwd);
26861
+ if (!pending || pending.state !== "prepared") {
26862
+ 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");
26863
+ }
26864
+ const sending = {
26865
+ ...pending,
26866
+ schemaVersion: 2,
26867
+ state: "sending"
26868
+ };
26869
+ atomicWriteFile(pendingOperationFilePath(cwd), `${JSON.stringify(sending, null, 2)}
26870
+ `);
26871
+ return sending;
26872
+ }
26873
+ function clearPendingOperation(cwd) {
26874
+ const filePath = pendingOperationFilePath(cwd);
26875
+ if (!existsSync13(filePath)) return false;
26876
+ unlinkSync6(filePath);
26877
+ return true;
26878
+ }
26879
+ function assertNoPendingOperation(cwd) {
26880
+ const pending = readPendingOperation(cwd);
26881
+ if (pending) {
26882
+ throw new CliError(
26883
+ `\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`,
26884
+ "PENDING_OPERATION_EXISTS"
26885
+ );
26886
+ }
26887
+ }
26888
+
26889
+ // src/domain/resource/recover.ts
26890
+ async function recoverPendingOperation(input2) {
26891
+ return withProjectLock(input2.cwd, () => recoverPendingOperationLocked(input2), "resource-recover");
26892
+ }
26893
+ async function recoverPendingOperationLocked(input2) {
26894
+ assertPlatformAllowed();
26895
+ const pending = readPendingOperation(input2.cwd);
26896
+ if (!pending) return "\u6CA1\u6709\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C";
26897
+ const activeEnv2 = getEnv2();
26898
+ if (pending.env !== activeEnv2) {
26899
+ throw new CliError(
26900
+ `\u672A\u51B3\u64CD\u4F5C\u5C5E\u4E8E ${pending.env} \u73AF\u5883\uFF0C\u672C\u6B21\u662F ${activeEnv2}\uFF1B\u8BF7\u5207\u6362 --env \u540E\u6062\u590D`,
26901
+ "PENDING_OPERATION_ENV_MISMATCH"
26902
+ );
26903
+ }
26904
+ const identity = readIdentity(input2.cwd, pending.resourceN);
26905
+ if (identity.resourceId !== pending.resourceId || (identity.env ?? "prod") !== pending.env) {
26906
+ 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");
26907
+ }
26908
+ const draft = readDraft(input2.cwd, identity.n);
26909
+ if (draft?.resourceId && draft.resourceId !== pending.resourceId) {
26910
+ 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");
26911
+ }
26912
+ if (pending.state === "prepared") {
26913
+ if (!input2.apply) {
26914
+ 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`;
26915
+ }
26916
+ if (!input2.yes) {
26917
+ 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");
26918
+ }
26919
+ commitLocalTransaction(input2.cwd, [
26920
+ { path: pendingOperationFilePath(input2.cwd), content: null }
26921
+ ]);
26922
+ 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`;
26923
+ }
26924
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26925
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26926
+ const info2 = unwrapData2(await infoApi2({ resourceIdOrName: pending.resourceId }));
26927
+ assertRemoteResourceOwned({
26928
+ info: info2,
26929
+ resourceId: pending.resourceId,
26930
+ authUserId: auth.userId,
26931
+ codes: {
26932
+ invalid: "RECOVER_RESOURCE_INFO_INVALID",
26933
+ notOwner: "RECOVER_NOT_OWNER"
26934
+ }
26935
+ });
26936
+ const versionInfoApi = input2.apis?.resourceVersionInfo1 ?? ((params) => service_API_default.Resource.resourceVersionInfo1(params));
26937
+ const versionInfo = unwrapData2(await versionInfoApi({
26938
+ resourceId: pending.resourceId,
26939
+ version: pending.version
26940
+ }));
26941
+ const remoteSha = typeof versionInfo.fileSha1 === "string" ? versionInfo.fileSha1 : "";
26942
+ if (!remoteSha) {
26943
+ if (input2.apply) {
26944
+ 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");
26945
+ }
26946
+ 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`;
26947
+ }
26948
+ if (remoteSha !== pending.fileSha1) {
26949
+ if (input2.apply) {
26950
+ 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");
26951
+ }
26952
+ 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`;
26953
+ }
26954
+ if (!input2.apply) {
26955
+ 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`;
26956
+ }
26957
+ if (!input2.yes) {
26958
+ 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");
26959
+ }
26960
+ commitLocalTransaction(input2.cwd, [
26961
+ { path: pendingOperationFilePath(input2.cwd), content: null },
26962
+ { path: draftFilePath(input2.cwd, identity.n), content: null }
26963
+ ]);
26964
+ 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`;
26965
+ }
26966
+
26967
+ // src/local/diagnostics.ts
26968
+ import { existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
26969
+ var IDENTITY_FILE_RE2 = /^([1-9]\d*)\.json$/;
26970
+ var DRAFT_FILE_RE2 = /^([1-9]\d*)\.version\.json$/;
26971
+ function errorMessage(error2) {
26972
+ return error2 instanceof Error ? error2.message : "\u672A\u77E5\u9519\u8BEF";
26973
+ }
26974
+ function duplicateDiagnostics(cwd, identities) {
26975
+ const result = [];
26976
+ for (const key of ["resourceId", "resourceName", "filePath"]) {
26977
+ const seen = /* @__PURE__ */ new Map();
26978
+ for (const identity of identities) {
26979
+ const raw = identity[key];
26980
+ if (!raw) continue;
26981
+ let value;
26982
+ try {
26983
+ value = key === "filePath" ? normalizeProjectPath(cwd, raw) : raw;
26984
+ } catch (error2) {
26985
+ result.push({ file: `${identity.n}.json`, code: "IDENTITY_PATH_INVALID", message: errorMessage(error2) });
26986
+ continue;
26987
+ }
26988
+ const previous = seen.get(value);
26989
+ if (previous !== void 0) {
26990
+ result.push({
26991
+ file: `${identity.n}.json`,
26992
+ code: "IDENTITY_CONFLICT",
26993
+ message: `\u4E0E ${previous}.json \u4F7F\u7528\u76F8\u540C ${key}`
26994
+ });
26995
+ } else {
26996
+ seen.set(value, identity.n);
26997
+ }
26998
+ }
26999
+ }
27000
+ return result;
27001
+ }
27002
+ function diagnoseLocalState(cwd) {
27003
+ const dir = freelogDir(cwd);
27004
+ if (!existsSync14(dir)) return "\u6CA1\u6709 .freelog \u672C\u5730\u72B6\u6001";
27005
+ let entries;
27006
+ try {
27007
+ entries = readdirSync5(dir).sort((left, right) => left.localeCompare(right, "en"));
27008
+ } catch (error2) {
27009
+ return `\u65E0\u6CD5\u8BFB\u53D6 .freelog\uFF1A${errorMessage(error2)}`;
27010
+ }
27011
+ const lines = [];
27012
+ const diagnostics = [];
27013
+ const identityNumbers = /* @__PURE__ */ new Set();
27014
+ const validIdentities = [];
27015
+ for (const file of entries) {
27016
+ const match = IDENTITY_FILE_RE2.exec(file);
27017
+ if (!match) continue;
27018
+ const n = Number(match[1]);
27019
+ identityNumbers.add(n);
27020
+ try {
27021
+ const identity = readIdentity(cwd, n);
27022
+ validIdentities.push(identity);
27023
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}\uFF0C${identity.typeCode}\uFF0C${identity.filePath}\uFF09`);
27024
+ } catch (error2) {
27025
+ diagnostics.push({ file, code: "IDENTITY_INVALID", message: errorMessage(error2) });
27026
+ }
27027
+ }
27028
+ for (const file of entries) {
27029
+ const match = DRAFT_FILE_RE2.exec(file);
27030
+ if (!match) continue;
27031
+ const n = Number(match[1]);
27032
+ if (!identityNumbers.has(n)) {
27033
+ diagnostics.push({ file, code: "DRAFT_ORPHAN", message: `\u6CA1\u6709\u540C\u53F7 ${n}.json` });
27034
+ continue;
27035
+ }
27036
+ if (!validIdentities.some((identity) => identity.n === n)) {
27037
+ diagnostics.push({ file, code: "DRAFT_IDENTITY_INVALID", message: `\u540C\u53F7 ${n}.json \u5DF2\u635F\u574F\uFF0C\u65E0\u6CD5\u6838\u9A8C\u5DE5\u4F5C\u7A3F` });
27038
+ continue;
27039
+ }
27040
+ try {
27041
+ const draft = readDraft(cwd, n);
27042
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${draft?.draftKind ?? "\u65E0"} \u5DE5\u4F5C\u7A3F\uFF09`);
27043
+ } catch (error2) {
27044
+ diagnostics.push({ file, code: "DRAFT_INVALID", message: errorMessage(error2) });
27045
+ }
27046
+ }
27047
+ diagnostics.push(...duplicateDiagnostics(cwd, validIdentities));
27048
+ if (entries.includes("index.json")) {
27049
+ 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");
27050
+ }
27051
+ try {
27052
+ const pending = readPendingOperation(cwd);
27053
+ if (pending) lines.push(`.pending-operation.json\uFF1A\u672A\u51B3 ${pending.kind}\uFF08${pending.resourceId} ${pending.version}\uFF09`);
27054
+ } catch (error2) {
27055
+ diagnostics.push({ file: ".pending-operation.json", code: "PENDING_OPERATION_INVALID", message: errorMessage(error2) });
27056
+ }
27057
+ if (entries.includes(".txn.json")) {
27058
+ 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");
27059
+ }
27060
+ if (lines.length === 0) lines.push("\u6CA1\u6709\u53EF\u8BC6\u522B\u7684\u8D44\u6E90\u8EAB\u4EFD\u6216\u5DE5\u4F5C\u7A3F");
27061
+ if (diagnostics.length === 0) return `\u672C\u5730\u72B6\u6001\u6B63\u5E38\uFF1A
27062
+ ${lines.map((line) => `- ${line}`).join("\n")}`;
27063
+ return [
27064
+ "\u672C\u5730\u72B6\u6001\u5B58\u5728\u95EE\u9898\uFF08\u672A\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\uFF09\uFF1A",
27065
+ ...lines.map((line) => `- ${line}`),
27066
+ ...diagnostics.map((item) => `- ${item.file} [${item.code}]\uFF1A${item.message}`),
27067
+ "\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"
27068
+ ].join("\n");
27069
+ }
27070
+
27071
+ // src/commands/project/resource.ts
27072
+ function createResourceCommand() {
27073
+ const resource = addSharedOptions(new Command15("resource")).description("\u8D44\u6E90\u672C\u5730\u72B6\u6001\u7EF4\u62A4");
27074
+ 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() {
27075
+ const shared = readSharedOptions(this);
27076
+ console.log(diagnoseLocalState(resolveCwd(shared.cwd)));
27077
+ });
27078
+ resource.command("sync").description("\u4ECE\u5E73\u53F0\u540C\u6B65\u672C\u5730\u8D44\u6E90\u6807\u9898").action(async function() {
27079
+ const shared = readSharedOptions(this);
27080
+ console.log(await syncResourceTitles({ cwd: resolveCwd(shared.cwd), selector: shared.file }));
27081
+ });
27082
+ 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() {
27083
+ const shared = readSharedOptions(this);
27084
+ const options = this.opts();
27085
+ console.log(await recoverPendingOperation({
27086
+ cwd: resolveCwd(shared.cwd),
27087
+ apply: options.apply === true,
27088
+ yes: shared.yes
27089
+ }));
27090
+ });
27091
+ return resource;
27092
+ }
27093
+
27094
+ // src/commands/shelf/offline.ts
27095
+ import { Command as Command16 } from "commander";
27096
+
27097
+ // src/domain/online/online.ts
27098
+ async function loadInfo(resourceId, apis) {
27099
+ const infoApi2 = apis?.info ?? ((params) => service_API_default.Resource.info(params));
27100
+ return unwrapData2(
27101
+ await infoApi2({
27102
+ resourceIdOrName: resourceId,
27103
+ isLoadLatestVersionInfo: 1,
27104
+ isLoadPolicyInfo: 1
27105
+ })
27106
+ );
27107
+ }
27108
+ function validateForOnline(info2) {
27109
+ if (!info2.latestVersion) {
27110
+ throw new CliError("\u4E0A\u67B6\u987B\u5DF2\u6709\u7248\u672C", "ONLINE_NO_VERSION");
27111
+ }
27112
+ const policies2 = info2.policies ?? [];
27113
+ if (!policies2.some((item) => item.status === 1)) {
27114
+ throw new CliError("\u4E0A\u67B6\u987B\u81F3\u5C11\u4E00\u6761\u542F\u7528\u7B56\u7565", "ONLINE_NO_POLICY");
27115
+ }
27116
+ }
27117
+ async function onlineResource(input2) {
27118
+ assertPlatformAllowed();
27119
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26414
27120
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27121
+ const info2 = await loadInfo(identity.resourceId, input2.apis);
27122
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27123
+ validateForOnline(info2);
27124
+ const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
27125
+ await update2({
27126
+ resourceId: identity.resourceId,
27127
+ status: 1
27128
+ });
27129
+ }
27130
+ async function offlineResource(input2) {
27131
+ assertPlatformAllowed();
27132
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27133
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27134
+ const info2 = await loadInfo(identity.resourceId, input2.apis);
27135
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
26415
27136
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
26416
27137
  await update2({
26417
27138
  resourceId: identity.resourceId,
@@ -26461,7 +27182,7 @@ async function validateOnline(input2) {
26461
27182
  throw new CliError("\u76EE\u524D\u53EA\u652F\u6301 --for online", "VALIDATE_FOR");
26462
27183
  }
26463
27184
  assertPlatformAllowed();
26464
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27185
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26465
27186
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
26466
27187
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26467
27188
  const result = await infoApi2({
@@ -26470,6 +27191,11 @@ async function validateOnline(input2) {
26470
27191
  isLoadPolicyInfo: 1
26471
27192
  });
26472
27193
  const data = result.data ?? result;
27194
+ assertRemoteResourceWritable({
27195
+ info: data,
27196
+ resourceId: identity.resourceId,
27197
+ authUserId: auth.userId
27198
+ });
26473
27199
  validateForOnline(data);
26474
27200
  return "\u53EF\u4EE5\u4E0A\u67B6";
26475
27201
  }
@@ -26753,6 +27479,42 @@ function attrList(cwd, file) {
26753
27479
  );
26754
27480
  return list2.map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
26755
27481
  }
27482
+ function attrReview(cwd, file) {
27483
+ const identity = resolveIdentity(cwd, file);
27484
+ const draft = readDraft(cwd, identity.n);
27485
+ if (!draft) {
27486
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27487
+ }
27488
+ const items = draft.orphanedInputAttrs ?? [];
27489
+ if (items.length === 0) return "\u6CA1\u6709\u5F85\u590D\u6838\u7684\u9644\u52A0\u5C5E\u6027";
27490
+ return [
27491
+ "\u4EE5\u4E0B\u9644\u52A0\u5C5E\u6027\u5DF2\u4E0D\u5728\u5F53\u524D\u6587\u4EF6\u5206\u6790\u7ED3\u679C\u4E2D\uFF0C\u4E0D\u80FD\u63D0\u4EA4\uFF1A",
27492
+ ...items.map((item) => ` ${String(item.key ?? "\uFF08\u65E0\u952E\uFF09")}=${String(item.value ?? "")}`),
27493
+ "\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"
27494
+ ].join("\n");
27495
+ }
27496
+ async function attrReviewDiscard(cwd, input2) {
27497
+ return withProjectLock(cwd, () => attrReviewDiscardLocked(cwd, input2), "version-attr-review-discard");
27498
+ }
27499
+ async function attrReviewDiscardLocked(cwd, input2) {
27500
+ const identity = resolveIdentity(cwd, input2.file);
27501
+ const draft = readDraft(cwd, identity.n);
27502
+ if (!draft) {
27503
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27504
+ }
27505
+ const key = input2.key.trim();
27506
+ const items = draft.orphanedInputAttrs ?? [];
27507
+ if (!key || !items.some((item) => item.key === key)) {
27508
+ throw new CliError("\u627E\u4E0D\u5230\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", "ATTR_REVIEW_NOT_FOUND");
27509
+ }
27510
+ const preview = `\u5373\u5C06\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027\uFF1A${key}`;
27511
+ if (!await confirmDestructive(preview, "\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", input2.yes)) {
27512
+ return "\u5DF2\u53D6\u6D88";
27513
+ }
27514
+ draft.orphanedInputAttrs = items.filter((item) => item.key !== key);
27515
+ writeDraft(cwd, identity.n, draft);
27516
+ return preview;
27517
+ }
26756
27518
 
26757
27519
  // src/commands/version/attr.ts
26758
27520
  function createVersionAttrCommand() {
@@ -26774,12 +27536,232 @@ function createVersionAttrCommand() {
26774
27536
  const shared = readSharedOptions(this);
26775
27537
  console.log(attrList(resolveCwd(shared.cwd), shared.file));
26776
27538
  });
27539
+ const review = addSharedOptions(attr.command("review")).description("\u67E5\u770B\u6216\u5904\u7406\u6587\u4EF6\u5206\u6790\u53D8\u5316\u540E\u7684\u9644\u52A0\u5C5E\u6027");
27540
+ review.action(function() {
27541
+ const shared = readSharedOptions(this);
27542
+ console.log(attrReview(resolveCwd(shared.cwd), shared.file));
27543
+ });
27544
+ 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) {
27545
+ const shared = readSharedOptions(this);
27546
+ console.log(await attrReviewDiscard(resolveCwd(shared.cwd), {
27547
+ key,
27548
+ file: shared.file,
27549
+ yes: shared.yes
27550
+ }));
27551
+ });
26777
27552
  return attr;
26778
27553
  }
26779
27554
 
26780
27555
  // src/commands/version/create-version.ts
26781
27556
  import { Command as Command20 } from "commander";
26782
27557
 
27558
+ // src/domain/version/form/option.ts
27559
+ var MAX_OPTION = 30;
27560
+ var MAX_OPTION_ITEM = 30;
27561
+ var MAX_TEXT_DEFAULT = 140;
27562
+ var MAX_NAME2 = 50;
27563
+ var MAX_REMARK2 = 50;
27564
+ function assertValidName2(name) {
27565
+ if (name.length > MAX_NAME2) {
27566
+ throw new CliError("\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26", "OPTION_NAME_LONG");
27567
+ }
27568
+ }
27569
+ function assertValidRemark2(remark) {
27570
+ if (remark.length > MAX_REMARK2) {
27571
+ throw new CliError("\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26\u3002", "OPTION_REMARK_LONG");
27572
+ }
27573
+ }
27574
+ function assertOptionValueLength(value) {
27575
+ if (value.length > MAX_TEXT_DEFAULT) {
27576
+ throw new CliError("\u4E0D\u8D85\u8FC7140\u4E2A\u5B57\u7B26", "OPTION_VALUE_LONG");
27577
+ }
27578
+ }
27579
+ function isOption(item) {
27580
+ return item.type === "editableText" || item.type === "select";
27581
+ }
27582
+ async function assertTypeAllowsOption(input2) {
27583
+ if (input2.supportOptionalConfig === true) return;
27584
+ if (input2.supportOptionalConfig === false) {
27585
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27586
+ }
27587
+ requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27588
+ const type = await getTypeInfo(input2.typeCode, input2.apis);
27589
+ if (type.supportOptionalConfig !== 2) {
27590
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27591
+ }
27592
+ }
27593
+ async function assertDraftOptionsAllowed(input2) {
27594
+ if (!(input2.customPropertyDescriptors ?? []).some((item) => isOption(item))) return;
27595
+ await assertTypeAllowsOption(input2);
27596
+ }
27597
+ function optionListFromDraft(draft) {
27598
+ return (draft.customPropertyDescriptors ?? []).filter((item) => isOption(item));
27599
+ }
27600
+ function normalizeMode(mode) {
27601
+ if (mode === void 0) return void 0;
27602
+ if (mode === "\u4E0B\u62C9" || mode === "\u4E0B\u62C9\u5217\u8868" || mode === "select") return "select";
27603
+ if (mode === "\u6587\u672C" || mode === "\u6587\u672C\u8F93\u5165\u6846" || mode === "input") return "editableText";
27604
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27605
+ }
27606
+ function parseCandidates(raw) {
27607
+ const items = (raw ?? "").split("|").map((item) => item.trim()).filter(Boolean);
27608
+ if (items.length === 0) throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27609
+ if (items.length > MAX_OPTION_ITEM) throw new CliError("\u9009\u9879\u4E2A\u6570\u4E0D\u80FD\u8D85\u8FC730\u9879", "OPTION_OPTIONS");
27610
+ for (const item of items) assertOptionValueLength(item);
27611
+ if (items.some((item, index) => items.indexOf(item) !== index)) {
27612
+ throw new CliError("\u8BE5\u9009\u9879\u5DF2\u5B58\u5728", "OPTION_DUPLICATE_OPTION");
27613
+ }
27614
+ return items;
27615
+ }
27616
+ async function optionAdd(cwd, input2) {
27617
+ return withProjectLock(cwd, () => optionAddLocked(cwd, input2), "version-option-add");
27618
+ }
27619
+ async function optionAddLocked(cwd, input2) {
27620
+ const identity = resolveIdentity(cwd, input2.file);
27621
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27622
+ const draft = readDraft(cwd, identity.n) ?? {
27623
+ baseUpcastResources: [],
27624
+ authExcludedItems: []
27625
+ };
27626
+ const parsed = parseLine(input2.line ?? "");
27627
+ if (!parsed.name || !parsed.key) {
27628
+ throw new CliError("\u53EF\u9009\u914D\u7F6E\u9700\u8981\u540D\u79F0\u548C\u952E", "OPTION_FIELDS");
27629
+ }
27630
+ assertValidKey(parsed.key);
27631
+ const list2 = draft.customPropertyDescriptors ?? [];
27632
+ if (optionListFromDraft(draft).length >= MAX_OPTION) {
27633
+ throw new CliError("\u6700\u591A\u53EF\u6DFB\u52A030\u4E2A\u53EF\u9009\u914D\u7F6E", "OPTION_FULL");
27634
+ }
27635
+ if (list2.some((item) => item.key === parsed.key)) {
27636
+ throw new CliError(`\u952E ${parsed.key} \u5DF2\u5B58\u5728`, "OPTION_DUPLICATE");
27637
+ }
27638
+ if (list2.some((item) => item.name === parsed.name)) {
27639
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27640
+ }
27641
+ const normalizedMode = normalizeMode(parsed.mode);
27642
+ if (!normalizedMode) {
27643
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27644
+ }
27645
+ const isSelect = normalizedMode === "select";
27646
+ const isText = normalizedMode === "editableText";
27647
+ if (isSelect && parsed.defaultValue) {
27648
+ 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");
27649
+ }
27650
+ if (isText && parsed.options) {
27651
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27652
+ }
27653
+ if (!isSelect && !isText) {
27654
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27655
+ }
27656
+ let type = "editableText";
27657
+ let defaultValue = parsed.defaultValue ?? parsed.value ?? "";
27658
+ let candidateItems;
27659
+ assertValidName2(parsed.name);
27660
+ assertValidRemark2(parsed.remark ?? "");
27661
+ if (isSelect) {
27662
+ const options = parseCandidates(parsed.options);
27663
+ type = "select";
27664
+ defaultValue = options[0] ?? "";
27665
+ candidateItems = options;
27666
+ } else {
27667
+ assertOptionValueLength(defaultValue);
27668
+ }
27669
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27670
+ list2.push({
27671
+ name: parsed.name,
27672
+ key: parsed.key,
27673
+ remark: parsed.remark ?? "",
27674
+ defaultValue,
27675
+ type,
27676
+ ...candidateItems ? { candidateItems } : {}
27677
+ });
27678
+ draft.customPropertyDescriptors = list2;
27679
+ writeDraft(cwd, identity.n, draft);
27680
+ return preview;
27681
+ }
27682
+ async function optionSet(cwd, input2) {
27683
+ return withProjectLock(cwd, () => optionSetLocked(cwd, input2), "version-option-set");
27684
+ }
27685
+ async function optionSetLocked(cwd, input2) {
27686
+ const identity = resolveIdentity(cwd, input2.file);
27687
+ const draft = readDraft(cwd, identity.n);
27688
+ if (!draft) {
27689
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27690
+ }
27691
+ const parsed = parseLine(input2.line ?? "");
27692
+ const found = (draft.customPropertyDescriptors ?? []).find(
27693
+ (item) => isOption(item) && (item.key === parsed.key || item.name === parsed.name)
27694
+ );
27695
+ if (!found) {
27696
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27697
+ }
27698
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27699
+ assertKeyUnchanged(String(found.key), parsed.key);
27700
+ assertValidName2(parsed.name ?? String(found.name ?? ""));
27701
+ assertValidRemark2(parsed.remark ?? String(found.remark ?? ""));
27702
+ const nextType = normalizeMode(parsed.mode) ?? (String(found.type) === "select" ? "select" : "editableText");
27703
+ const nextDefault = parsed.defaultValue ?? parsed.value;
27704
+ if (nextType === "select" && nextDefault !== void 0) {
27705
+ 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");
27706
+ }
27707
+ if (nextType === "editableText") {
27708
+ if (parsed.options !== void 0) {
27709
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27710
+ }
27711
+ if (nextDefault !== void 0) assertOptionValueLength(nextDefault);
27712
+ }
27713
+ if (parsed.name && (draft.customPropertyDescriptors ?? []).some((item) => item.name === parsed.name && item.key !== found.key)) {
27714
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27715
+ }
27716
+ if (parsed.name) {
27717
+ found.name = parsed.name;
27718
+ }
27719
+ if (parsed.remark !== void 0) {
27720
+ found.remark = parsed.remark;
27721
+ }
27722
+ if (nextType === "select") {
27723
+ const candidates = parsed.options !== void 0 ? parseCandidates(parsed.options) : Array.isArray(found.candidateItems) ? found.candidateItems.map(String) : [];
27724
+ if (candidates.length === 0) {
27725
+ throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27726
+ }
27727
+ found.type = "select";
27728
+ found.candidateItems = candidates;
27729
+ found.defaultValue = candidates[0];
27730
+ } else {
27731
+ found.type = "editableText";
27732
+ delete found.candidateItems;
27733
+ if (nextDefault !== void 0) {
27734
+ found.defaultValue = nextDefault;
27735
+ }
27736
+ }
27737
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27738
+ writeDraft(cwd, identity.n, draft);
27739
+ return preview;
27740
+ }
27741
+ function optionRm(cwd, key, file) {
27742
+ return withProjectLock(cwd, () => optionRmLocked(cwd, key, file), "version-option-rm");
27743
+ }
27744
+ function optionRmLocked(cwd, key, file) {
27745
+ const identity = resolveIdentity(cwd, file);
27746
+ const draft = readDraft(cwd, identity.n);
27747
+ if (!draft) {
27748
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27749
+ }
27750
+ const list2 = draft.customPropertyDescriptors ?? [];
27751
+ const next = list2.filter((item) => !(isOption(item) && item.key === key));
27752
+ if (next.length === list2.length) {
27753
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27754
+ }
27755
+ draft.customPropertyDescriptors = next;
27756
+ writeDraft(cwd, identity.n, draft);
27757
+ return key;
27758
+ }
27759
+ function optionList(cwd, file) {
27760
+ const identity = resolveIdentity(cwd, file);
27761
+ const draft = readDraft(cwd, identity.n);
27762
+ return optionListFromDraft(draft ?? {}).map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
27763
+ }
27764
+
26783
27765
  // src/domain/version/submit.ts
26784
27766
  function fieldName(error2) {
26785
27767
  const rec = error2;
@@ -26804,6 +27786,9 @@ function buildVersionPayload(input2) {
26804
27786
  };
26805
27787
  }
26806
27788
  async function submitVersion(input2) {
27789
+ return withProjectLock(input2.cwd, () => submitVersionLocked(input2), "submit-version");
27790
+ }
27791
+ async function submitVersionLocked(input2) {
26807
27792
  assertPlatformAllowed();
26808
27793
  if (!input2.identity.resourceId) {
26809
27794
  throw new CliError("\u8BF7\u5148 create \u6216 bind", "SUBMIT_NO_RESOURCE");
@@ -26818,15 +27803,39 @@ async function submitVersion(input2) {
26818
27803
  if ((draft.orphanedInputAttrs ?? []).length > 0) {
26819
27804
  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
27805
  }
27806
+ await assertDraftOptionsAllowed({
27807
+ cwd: input2.cwd,
27808
+ typeCode: input2.identity.typeCode,
27809
+ customPropertyDescriptors: draft.customPropertyDescriptors,
27810
+ homeDir: input2.homeDir,
27811
+ apis: input2.apis
27812
+ });
26821
27813
  const payload = buildVersionPayload({
26822
27814
  resourceId: input2.identity.resourceId,
26823
27815
  version: input2.version,
26824
27816
  draft
26825
27817
  });
26826
27818
  const createVersion2 = input2.apis?.createVersion ?? ((params) => service_API_default.Resource.createVersion(params));
27819
+ assertNoPendingOperation(input2.cwd);
27820
+ createPendingVersionSubmit({
27821
+ cwd: input2.cwd,
27822
+ resourceN: input2.identity.n,
27823
+ resourceId: input2.identity.resourceId,
27824
+ env: getEnv2(),
27825
+ version: input2.version,
27826
+ fileSha1: draft.fileSha1
27827
+ });
27828
+ markPendingVersionSubmitSending(input2.cwd);
26827
27829
  try {
26828
27830
  await createVersion2(payload);
26829
27831
  } catch (error2) {
27832
+ if (!isDefinitivePlatformRejection(error2)) {
27833
+ throw new CliError(
27834
+ "\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",
27835
+ "SUBMIT_RESULT_UNKNOWN"
27836
+ );
27837
+ }
27838
+ clearPendingOperation(input2.cwd);
26830
27839
  const field = fieldName(error2);
26831
27840
  const detail = errorDetail(error2);
26832
27841
  throw new CliError(
@@ -26834,21 +27843,30 @@ async function submitVersion(input2) {
26834
27843
  "SUBMIT_FAILED"
26835
27844
  );
26836
27845
  }
26837
- deleteDraft(input2.cwd, input2.identity.n);
27846
+ commitLocalTransaction(input2.cwd, [
27847
+ { path: draftFilePath(input2.cwd, input2.identity.n), content: null },
27848
+ { path: pendingOperationFilePath(input2.cwd), content: null }
27849
+ ]);
27850
+ }
27851
+ function isDefinitivePlatformRejection(error2) {
27852
+ if (error2 instanceof CliError) return true;
27853
+ const rec = error2;
27854
+ if (typeof rec.field === "string" || typeof rec.msg === "string" || typeof rec.result?.msg === "string") {
27855
+ return true;
27856
+ }
27857
+ const status = rec.status ?? rec.statusCode ?? rec.response?.status;
27858
+ return typeof status === "number" && status >= 400 && status < 500;
26838
27859
  }
26839
27860
 
26840
27861
  // src/domain/version/file.ts
26841
27862
  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";
27863
+ import { createReadStream, existsSync as existsSync15, openAsBlob, statSync as statSync3, unlinkSync as unlinkSync7 } from "fs";
27864
+ import path16 from "path";
26844
27865
  var ANALYZE_TIMEOUT_MS = 12e4;
26845
27866
  var ANALYZE_POLL_INTERVAL_MS = 200;
26846
27867
  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)) {
27868
+ const joined = path16.resolve(cwd, raw);
27869
+ if (existsSync15(joined)) {
26852
27870
  return joined;
26853
27871
  }
26854
27872
  return void 0;
@@ -26860,11 +27878,13 @@ function confirmLocalPath(identity, file, yes, cwd) {
26860
27878
  if (!resolved) {
26861
27879
  throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${normalizedFile}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
26862
27880
  }
27881
+ resolveExistingProjectPath(cwd, resolved);
26863
27882
  return resolved;
26864
27883
  }
26865
27884
  const recorded = identity.filePath ? normalizeProjectPath(cwd, identity.filePath) : void 0;
26866
27885
  const recordedExists = recorded ? resolveExistingPath(cwd, recorded) : void 0;
26867
27886
  if (recordedExists) {
27887
+ resolveExistingProjectPath(cwd, recordedExists);
26868
27888
  return recordedExists;
26869
27889
  }
26870
27890
  if (yes) {
@@ -26894,24 +27914,55 @@ async function waitAnalyze(sha1, typeCode, apis, now = Date.now, sleep = (ms) =>
26894
27914
  }
26895
27915
  throw new CliError("\u5C5E\u6027\u89E3\u6790\u8D85\u65F6", "FILE_ANALYZE_TIMEOUT");
26896
27916
  }
26897
- function sha1OfFile(uploadPath) {
26898
- return createHash3("sha1").update(readFileSync9(uploadPath)).digest("hex");
27917
+ function fingerprintFile(uploadPath) {
27918
+ const stats = statSync3(uploadPath);
27919
+ if (!stats.isFile()) {
27920
+ throw new CliError("\u4EA7\u7269\u5FC5\u987B\u662F\u53EF\u8BFB\u53D6\u7684\u6587\u4EF6", "FILE_ARTIFACT_UNSUPPORTED");
27921
+ }
27922
+ return { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs };
27923
+ }
27924
+ function assertFileUnchanged(uploadPath, expected) {
27925
+ const actual = fingerprintFile(uploadPath);
27926
+ if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs || actual.ctimeMs !== expected.ctimeMs) {
27927
+ throw new CliError(
27928
+ "\u53D1\u5E03\u671F\u95F4\u672C\u5730\u4EA7\u7269\u53D1\u751F\u53D8\u5316\uFF1B\u8BF7\u4FDD\u6301\u4EA7\u7269\u7A33\u5B9A\u540E\u91CD\u8BD5",
27929
+ "FILE_CHANGED_DURING_PUBLISH"
27930
+ );
27931
+ }
27932
+ }
27933
+ async function sha1OfFile(uploadPath) {
27934
+ const fingerprint = fingerprintFile(uploadPath);
27935
+ const hash = createHash3("sha1");
27936
+ await new Promise((resolve, reject) => {
27937
+ const stream4 = createReadStream(uploadPath);
27938
+ stream4.on("data", (chunk) => {
27939
+ hash.update(chunk);
27940
+ });
27941
+ stream4.once("error", reject);
27942
+ stream4.once("end", resolve);
27943
+ });
27944
+ assertFileUnchanged(uploadPath, fingerprint);
27945
+ return { sha1: hash.digest("hex"), fingerprint };
26899
27946
  }
26900
- async function uploadIfNew(uploadPath, sha1, typeCode, apis) {
27947
+ async function uploadIfNew(uploadPath, sha1, fingerprint, typeCode, apis) {
26901
27948
  const fileIsExist2 = apis.fileIsExist ?? ((params) => service_API_default.Storage.fileIsExist(params));
26902
27949
  const exists2 = unwrapFirst(await fileIsExist2({ sha1 }));
26903
27950
  if (exists2.isExisting || exists2.exist || exists2.data === true) {
27951
+ assertFileUnchanged(uploadPath, fingerprint);
26904
27952
  return;
26905
27953
  }
26906
27954
  const upload = apis.uploadFile ?? ((params) => service_API_default.Storage.uploadFile(params));
26907
- await upload({ file: readFileSync9(uploadPath), resourceType: typeCode });
27955
+ assertFileUnchanged(uploadPath, fingerprint);
27956
+ const file = await openAsBlob(uploadPath);
27957
+ await upload({ file, resourceType: typeCode });
27958
+ assertFileUnchanged(uploadPath, fingerprint);
26908
27959
  }
26909
27960
  function removeTempZip(uploadPath, localPath) {
26910
- if (uploadPath === localPath || !existsSync12(uploadPath)) {
27961
+ if (uploadPath === localPath || !existsSync15(uploadPath)) {
26911
27962
  return;
26912
27963
  }
26913
27964
  try {
26914
- unlinkSync6(uploadPath);
27965
+ unlinkSync7(uploadPath);
26915
27966
  } catch {
26916
27967
  }
26917
27968
  }
@@ -26926,16 +27977,22 @@ function writeSha1ToDraft(input2, uploaded, analysis) {
26926
27977
  metadata.filter((item) => Boolean(item) && typeof item === "object").filter((item) => Number(item.insertMode) === 2).map((item) => String(item.key ?? "")).filter(Boolean)
26927
27978
  );
26928
27979
  const previousAttrs = draft.inputAttrs ?? [];
27980
+ const previousOrphans = draft.orphanedInputAttrs ?? [];
26929
27981
  const reuseCurrentAnalysis = !hasMetadata && draft.fileSha1 === uploaded.fileSha1 && draft.analyzedSha1 === uploaded.fileSha1;
26930
- const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? "")));
27982
+ const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : [
27983
+ ...previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? ""))),
27984
+ // 新分析重新出现同 key 时,恢复用户上次保留的值;它不应永远卡在待复核队列。
27985
+ ...previousOrphans.filter((item) => additionalKeys.has(String(item.key ?? "")) && !previousAttrs.some((current) => additionalKeys.has(String(current.key ?? "")) && current.key === item.key))
27986
+ ];
26931
27987
  const newlyOrphaned = reuseCurrentAnalysis ? [] : previousAttrs.filter((item) => !additionalKeys.has(String(item.key ?? "")));
27988
+ const unresolvedOrphans = reuseCurrentAnalysis ? previousOrphans : previousOrphans.filter((item) => !additionalKeys.has(String(item.key ?? "")));
26932
27989
  draft.fileSha1 = uploaded.fileSha1;
26933
27990
  draft.filename = uploaded.filename;
26934
27991
  draft.analyzedSha1 = uploaded.fileSha1;
26935
27992
  draft.inputAttrs = retainedAttrs;
26936
27993
  draft.orphanedInputAttrs = [
26937
- ...draft.orphanedInputAttrs ?? [],
26938
- ...newlyOrphaned.filter((item) => !(draft.orphanedInputAttrs ?? []).some((old) => old.key === item.key))
27994
+ ...unresolvedOrphans,
27995
+ ...newlyOrphaned.filter((item) => !unresolvedOrphans.some((old) => old.key === item.key))
26939
27996
  ];
26940
27997
  const nextDraft = prepareDraft(input2.cwd, input2.identity.n, draft);
26941
27998
  const changes = [{
@@ -26959,10 +28016,11 @@ async function uploadAndAnalyzeLocked(input2) {
26959
28016
  const recordedFile = normalizeProjectPath(input2.cwd, input2.identity.filePath);
26960
28017
  const identity = recordedFile === input2.identity.filePath ? input2.identity : { ...input2.identity, filePath: recordedFile };
26961
28018
  const localPath = confirmLocalPath(identity, file, input2.yes, input2.cwd);
26962
- const uploadPath = await prepareUploadPath(identity.typeCode, localPath);
28019
+ const uploadPath = await prepareUploadPath(identity.typeCode, localPath, input2.cwd);
26963
28020
  try {
26964
- const uploaded = { fileSha1: sha1OfFile(uploadPath), filename: path15.basename(uploadPath) };
26965
- await uploadIfNew(uploadPath, uploaded.fileSha1, identity.typeCode, input2.apis ?? {});
28021
+ const hashed = await sha1OfFile(uploadPath);
28022
+ const uploaded = { fileSha1: hashed.sha1, filename: path16.basename(uploadPath) };
28023
+ await uploadIfNew(uploadPath, uploaded.fileSha1, hashed.fingerprint, identity.typeCode, input2.apis ?? {});
26966
28024
  const analysis = await waitAnalyze(
26967
28025
  uploaded.fileSha1,
26968
28026
  identity.typeCode,
@@ -26970,6 +28028,7 @@ async function uploadAndAnalyzeLocked(input2) {
26970
28028
  input2.now,
26971
28029
  input2.sleep
26972
28030
  );
28031
+ assertFileUnchanged(uploadPath, hashed.fingerprint);
26973
28032
  writeSha1ToDraft(
26974
28033
  {
26975
28034
  ...input2,
@@ -27004,7 +28063,7 @@ async function runCreateVersion(input2) {
27004
28063
  }
27005
28064
  async function runCreateVersionLocked(input2) {
27006
28065
  assertPlatformAllowed();
27007
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28066
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27008
28067
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27009
28068
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27010
28069
  const info2 = unwrapData2(
@@ -27021,8 +28080,10 @@ async function runCreateVersionLocked(input2) {
27021
28080
  throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "CREATE_VERSION_NEED_YES");
27022
28081
  }
27023
28082
  if (input2.reset) {
27024
- confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28083
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28084
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
27025
28085
  }
28086
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27026
28087
  let draft = existingDraft;
27027
28088
  if (input2.reset && existingDraft) {
27028
28089
  if (!input2.confirmReset) {
@@ -27060,6 +28121,7 @@ async function runCreateVersionLocked(input2) {
27060
28121
  isLoadLatestVersionInfo: 1
27061
28122
  })
27062
28123
  );
28124
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
27063
28125
  if (again.latestVersion) {
27064
28126
  throw new CliError(
27065
28127
  `\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 +28132,7 @@ async function runCreateVersionLocked(input2) {
27070
28132
  cwd: input2.cwd,
27071
28133
  identity,
27072
28134
  version: "1.0.0",
28135
+ homeDir: input2.homeDir,
27073
28136
  apis: input2.apis
27074
28137
  });
27075
28138
  return "1.0.0";
@@ -27120,6 +28183,26 @@ import { Command as Command21 } from "commander";
27120
28183
 
27121
28184
  // src/domain/version/form/dep.ts
27122
28185
  import semver from "semver";
28186
+ async function assertCurrentResourceWritable(input2) {
28187
+ const auth = requireAuth({ cwd: input2.cwd });
28188
+ const resourceId = input2.identity.resourceId;
28189
+ if (!resourceId) {
28190
+ throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
28191
+ }
28192
+ const infoApi2 = input2.apis?.ownInfo ?? ((params) => service_API_default.Resource.info(params));
28193
+ const info2 = unwrapData3(await infoApi2({ resourceIdOrName: resourceId }));
28194
+ assertRemoteResourceWritable({
28195
+ info: info2,
28196
+ resourceId,
28197
+ authUserId: auth.userId,
28198
+ codes: {
28199
+ invalid: "DEP_CURRENT_INFO_INVALID",
28200
+ notOwner: "DEP_CURRENT_NOT_OWNER",
28201
+ frozen: "DEP_CURRENT_RESOURCE_FROZEN"
28202
+ }
28203
+ });
28204
+ return resourceId;
28205
+ }
27123
28206
  function unwrapData3(result) {
27124
28207
  const envelope = result;
27125
28208
  const data = envelope.data ?? result;
@@ -27157,6 +28240,64 @@ function statusLabel(status) {
27157
28240
  }
27158
28241
  return void 0;
27159
28242
  }
28243
+ async function loadDependencyTarget(input2) {
28244
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28245
+ const info2 = unwrapData3(
28246
+ await infoApi2({
28247
+ resourceIdOrName: input2.resourceIdOrName,
28248
+ isLoadLatestVersionInfo: 1,
28249
+ isLoadPolicyInfo: 1
28250
+ })
28251
+ );
28252
+ const resourceId = typeof info2.resourceId === "string" ? info2.resourceId.trim() : "";
28253
+ if (!resourceId || input2.expectedResourceId && resourceId !== input2.expectedResourceId) {
28254
+ 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");
28255
+ }
28256
+ const resourceName = typeof info2.resourceName === "string" ? info2.resourceName : typeof info2.name === "string" ? info2.name : "";
28257
+ if (input2.identity.resourceId === resourceId || input2.identity.name && resourceName === input2.identity.name) {
28258
+ throw new CliError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1", "DEP_SELF");
28259
+ }
28260
+ if (Number(info2.subjectType) !== 1) {
28261
+ throw new CliError("\u5BF9\u65B9\u4E0D\u662F\u666E\u901A\u5355\u8D44\u6E90\uFF0C\u4E0D\u52A0", "DEP_NOT_RESOURCE");
28262
+ }
28263
+ const latestVersion = typeof info2.latestVersion === "string" ? info2.latestVersion.trim() : "";
28264
+ if (!latestVersion || !semver.valid(latestVersion)) {
28265
+ throw new CliError("\u5BF9\u65B9\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C", "DEP_NO_VERSION");
28266
+ }
28267
+ if (info2.status === void 0 || !Number.isFinite(Number(info2.status))) {
28268
+ throw new CliError("\u5BF9\u65B9\u8D44\u6E90\u4FE1\u606F\u4E0D\u5B8C\u6574\u6216\u72B6\u6001\u4E0D\u53EF\u7528", "DEP_TARGET_INVALID");
28269
+ }
28270
+ const blocked = statusLabel(Number(info2.status));
28271
+ if (blocked) {
28272
+ throw new CliError(blocked, "DEP_TARGET_STATUS");
28273
+ }
28274
+ if (Array.isArray(info2.baseUpcastResources) && info2.baseUpcastResources.length > 0) {
28275
+ throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
28276
+ }
28277
+ return { info: info2, resourceId, latestVersion };
28278
+ }
28279
+ async function assertDependencyVersionRange(input2) {
28280
+ const listApi = input2.apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
28281
+ const list2 = unwrapData3(await listApi({ resourceId: input2.resourceId }));
28282
+ const rawVersions = list2.dataList ?? list2.list;
28283
+ if (!Array.isArray(rawVersions) || rawVersions.length === 0) {
28284
+ 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");
28285
+ }
28286
+ const versions = [];
28287
+ for (const item of rawVersions) {
28288
+ const version = typeof item === "object" && item !== null && typeof item.version === "string" ? item.version : "";
28289
+ if (!version || !semver.valid(version)) {
28290
+ 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");
28291
+ }
28292
+ versions.push(version);
28293
+ }
28294
+ if (!versions.some((version) => semver.eq(version, input2.latestVersion))) {
28295
+ 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");
28296
+ }
28297
+ if (!semver.validRange(input2.versionRange) || !semver.maxSatisfying(versions, input2.versionRange)) {
28298
+ throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
28299
+ }
28300
+ }
27160
28301
  function signablePolicies(info2) {
27161
28302
  const policies2 = info2.policies ?? [];
27162
28303
  return policies2.filter((item) => item.status === 1 && item.policyId).map((item) => ({ policyId: item.policyId, policyName: item.policyName }));
@@ -27197,49 +28338,26 @@ async function depAdd(input2) {
27197
28338
  }
27198
28339
  async function depAddLocked(input2) {
27199
28340
  assertPlatformAllowed();
27200
- const identity = resolveIdentity(input2.cwd, input2.file);
28341
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27201
28342
  const draft = readDraft(input2.cwd, identity.n) ?? {
27202
28343
  baseUpcastResources: [],
27203
28344
  authExcludedItems: []
27204
28345
  };
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
- }
28346
+ const licenseeId = await assertCurrentResourceWritable({ cwd: input2.cwd, identity, apis: input2.apis });
28347
+ const target = await loadDependencyTarget({
28348
+ resourceIdOrName: input2.resourceId,
28349
+ identity,
28350
+ apis: input2.apis
28351
+ });
28352
+ const targetId = target.resourceId;
28353
+ const info2 = target.info;
28354
+ const versionRange = input2.versionRange ?? `^${target.latestVersion}`;
28355
+ await assertDependencyVersionRange({
28356
+ resourceId: targetId,
28357
+ latestVersion: target.latestVersion,
28358
+ versionRange,
28359
+ apis: input2.apis
28360
+ });
27243
28361
  if (identity.resourceId) {
27244
28362
  const cycleApi = input2.apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27245
28363
  const existing2 = (draft.dependencies ?? []).filter((item) => item.resourceId !== targetId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
@@ -27262,16 +28380,13 @@ async function depAddLocked(input2) {
27262
28380
  );
27263
28381
  if (!extractIsAuth(firstAuth, targetId)) {
27264
28382
  const policies2 = signablePolicies(info2);
27265
- if (!identity.resourceId) {
27266
- throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
27267
- }
27268
28383
  const sign = input2.apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27269
28384
  await signWithSelectedPolicy({
27270
28385
  policies: policies2,
27271
28386
  policyId: input2.policyId,
27272
28387
  yes: input2.yes,
27273
28388
  targetId,
27274
- licenseeId: identity.resourceId,
28389
+ licenseeId,
27275
28390
  sign
27276
28391
  });
27277
28392
  }
@@ -27317,7 +28432,8 @@ async function depRange(cwd, resourceId, versionRange, file, apis, options) {
27317
28432
  return withProjectLock(cwd, () => depRangeLocked(cwd, resourceId, versionRange, file, apis, options), "version-dep-range");
27318
28433
  }
27319
28434
  async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options) {
27320
- const identity = resolveIdentity(cwd, file);
28435
+ assertPlatformAllowed();
28436
+ const identity = resolveBoundIdentity(cwd, file);
27321
28437
  const draft = readDraft(cwd, identity.n);
27322
28438
  if (!draft) {
27323
28439
  throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
@@ -27326,39 +28442,27 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27326
28442
  if (!found) {
27327
28443
  throw new CliError(`\u7A3F\u4E0A\u6CA1\u6709\u4F9D\u8D56 ${resourceId}`, "DEP_NOT_FOUND");
27328
28444
  }
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
- }
28445
+ const licenseeId = await assertCurrentResourceWritable({ cwd, identity, apis });
28446
+ const target = await loadDependencyTarget({
28447
+ resourceIdOrName: resourceId,
28448
+ expectedResourceId: resourceId,
28449
+ identity,
28450
+ apis
28451
+ });
28452
+ await assertDependencyVersionRange({
28453
+ resourceId: target.resourceId,
28454
+ latestVersion: target.latestVersion,
28455
+ versionRange,
28456
+ apis
28457
+ });
28458
+ const info2 = target.info;
27355
28459
  if (identity.resourceId) {
27356
28460
  const cycleApi = apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27357
28461
  const existing = (draft.dependencies ?? []).filter((item) => item.resourceId !== resourceId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
27358
28462
  const cycle = unwrapData3(
27359
28463
  await cycleApi({
27360
28464
  resourceId: identity.resourceId,
27361
- dependencies: [...existing, { resourceId, versionRange }]
28465
+ dependencies: [...existing, { resourceId: target.resourceId, versionRange }]
27362
28466
  })
27363
28467
  );
27364
28468
  if (cycle.ok === false || cycle.data === false) {
@@ -27366,22 +28470,22 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27366
28470
  }
27367
28471
  }
27368
28472
  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) {
28473
+ const firstAuth = unwrapData3(await batchAuth3({ resourceIds: target.resourceId, versionRanges: versionRange }));
28474
+ if (!extractIsAuth(firstAuth, target.resourceId) && identity.resourceId) {
27371
28475
  const policies2 = signablePolicies(info2);
27372
28476
  const sign = apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27373
28477
  await signWithSelectedPolicy({
27374
28478
  policies: policies2,
27375
28479
  policyId: options?.policyId,
27376
28480
  yes: options?.yes,
27377
- targetId: resourceId,
27378
- licenseeId: identity.resourceId,
28481
+ targetId: target.resourceId,
28482
+ licenseeId,
27379
28483
  sign
27380
28484
  });
27381
28485
  }
27382
28486
  found.versionRange = versionRange;
27383
28487
  writeDraft(cwd, identity.n, draft);
27384
- return `${resourceId}@${versionRange}`;
28488
+ return `${target.resourceId}@${versionRange}`;
27385
28489
  }
27386
28490
 
27387
28491
  // src/commands/version/dep.ts
@@ -27424,7 +28528,7 @@ import { Command as Command22 } from "commander";
27424
28528
  // src/domain/version/description.ts
27425
28529
  async function updateOnlineDescription(input2) {
27426
28530
  assertPlatformAllowed();
27427
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28531
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27428
28532
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27429
28533
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27430
28534
  const info2 = unwrapData2(
@@ -27433,6 +28537,7 @@ async function updateOnlineDescription(input2) {
27433
28537
  isLoadLatestVersionInfo: 1
27434
28538
  })
27435
28539
  );
28540
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27436
28541
  if (!info2.latestVersion) {
27437
28542
  throw new CliError(
27438
28543
  "\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 +28760,6 @@ function createVersionDraftCommand() {
27655
28760
 
27656
28761
  // src/commands/version/option.ts
27657
28762
  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
28763
  function createVersionOptionCommand() {
27863
28764
  const option = addSharedOptions(new Command24("option"));
27864
28765
  option.description("\u6539\u7A3F\u4E0A\u7684\u53EF\u9009\u914D\u7F6E");
@@ -27893,7 +28794,16 @@ function createVersionOptionCommand() {
27893
28794
  import { Command as Command25 } from "commander";
27894
28795
 
27895
28796
  // src/domain/version/setPath.ts
27896
- import path16 from "path";
28797
+ import { existsSync as existsSync16, statSync as statSync4 } from "fs";
28798
+ import path17 from "path";
28799
+ function sameOrNestedPath(parent, candidate) {
28800
+ const relative = path17.relative(parent, candidate);
28801
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path17.sep}`) && !path17.isAbsolute(relative);
28802
+ }
28803
+ function artifactsOverlap(first, second) {
28804
+ if (first.path === second.path) return true;
28805
+ return (first.isDirectory || second.isDirectory) && (sameOrNestedPath(first.path, second.path) || sameOrNestedPath(second.path, first.path));
28806
+ }
27897
28807
  function setIdentityFilePath(cwd, input2) {
27898
28808
  return withProjectLock(cwd, () => {
27899
28809
  const artifact = input2.artifact !== void 0 ? normalizeProjectPath(cwd, input2.artifact) : void 0;
@@ -27901,7 +28811,24 @@ function setIdentityFilePath(cwd, input2) {
27901
28811
  throw new CliError("\u8BF7\u63D0\u4F9B --artifact", "SET_ARTIFACT_REQUIRED");
27902
28812
  }
27903
28813
  const identity = resolveIdentity(cwd, input2.file);
27904
- assertArtifactAnchor(identity.typeCode, path16.resolve(cwd, artifact));
28814
+ const absoluteArtifact = path17.resolve(cwd, artifact);
28815
+ assertArtifactAnchor(identity.typeCode, absoluteArtifact, cwd);
28816
+ const nextArtifact = {
28817
+ path: resolveExistingProjectPath(cwd, absoluteArtifact),
28818
+ isDirectory: statSync4(absoluteArtifact).isDirectory()
28819
+ };
28820
+ const occupant = validateLocalState(cwd).find((candidate) => {
28821
+ if (candidate.n === identity.n) return false;
28822
+ const candidatePath = path17.resolve(cwd, normalizeProjectPath(cwd, candidate.filePath));
28823
+ if (!existsSync16(candidatePath)) return false;
28824
+ return artifactsOverlap(nextArtifact, {
28825
+ path: resolveExistingProjectPath(cwd, candidatePath),
28826
+ isDirectory: statSync4(candidatePath).isDirectory()
28827
+ });
28828
+ });
28829
+ if (occupant) {
28830
+ throw new CliError(`\u4EA7\u7269\u8DEF\u5F84 ${artifact} \u5DF2\u88AB ${occupant.n}.json \u5360\u7528`, "SET_ARTIFACT_OCCUPIED");
28831
+ }
27905
28832
  const updated = prepareIdentityUpdate(cwd, identity.n, { filePath: artifact });
27906
28833
  commitLocalTransaction(cwd, [
27907
28834
  { path: identityFilePath(cwd, updated.n), content: serializeIdentity(updated) }
@@ -27950,10 +28877,11 @@ function showLocal(cwd, file) {
27950
28877
  async function showOnline(input2) {
27951
28878
  assertPlatformAllowed();
27952
28879
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27953
- const identity = resolveIdentity(input2.cwd, input2.file);
27954
- if (!identity.resourceId) {
28880
+ const local = resolveIdentity(input2.cwd, input2.file);
28881
+ if (!local.resourceId) {
27955
28882
  return "\u672C\u5730\u8FD8\u6CA1\u6709 resourceId";
27956
28883
  }
28884
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27957
28885
  const draft = readDraft(input2.cwd, identity.n);
27958
28886
  const hint = draft ? "\u672C\u5730\u6709\u672A\u63D0\u4EA4\u7684\u7248\u672C\u5DE5\u4F5C\u7A3F\uFF0C\u67E5\u770B\u8BF7 version show --local\n" : "";
27959
28887
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
@@ -28026,7 +28954,7 @@ async function runUpdateVersion(input2) {
28026
28954
  }
28027
28955
  async function runUpdateVersionLocked(input2) {
28028
28956
  assertPlatformAllowed();
28029
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28957
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28030
28958
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
28031
28959
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28032
28960
  const info2 = unwrapData2(
@@ -28057,8 +28985,10 @@ async function runUpdateVersionLocked(input2) {
28057
28985
  throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "UPDATE_VERSION_NEED_YES");
28058
28986
  }
28059
28987
  if (input2.reset) {
28060
- confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28988
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28989
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
28061
28990
  }
28991
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
28062
28992
  let draft = existingDraft;
28063
28993
  if (input2.reset && existingDraft) {
28064
28994
  if (!input2.confirmReset) {
@@ -28103,6 +29033,7 @@ async function runUpdateVersionLocked(input2) {
28103
29033
  isLoadLatestVersionInfo: 1
28104
29034
  })
28105
29035
  );
29036
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
28106
29037
  const currentLatest = String(again.latestVersion ?? latestVersion);
28107
29038
  if (!semver2.gt(next, currentLatest)) {
28108
29039
  throw new CliError(
@@ -28114,6 +29045,7 @@ async function runUpdateVersionLocked(input2) {
28114
29045
  cwd: input2.cwd,
28115
29046
  identity,
28116
29047
  version: next,
29048
+ homeDir: input2.homeDir,
28117
29049
  apis: input2.apis
28118
29050
  });
28119
29051
  return next;
@@ -28235,26 +29167,26 @@ function createSubCommands() {
28235
29167
  }
28236
29168
 
28237
29169
  // src/core/usageDocs.ts
28238
- import { existsSync as existsSync13 } from "fs";
28239
- import path17 from "path";
29170
+ import { existsSync as existsSync17 } from "fs";
29171
+ import path18 from "path";
28240
29172
  import { fileURLToPath } from "url";
28241
29173
  function usageDocsPath() {
28242
- const moduleDir = path17.dirname(fileURLToPath(import.meta.url));
28243
- const bundledPath = path17.resolve(moduleDir, "../docs/README.md");
28244
- if (existsSync13(bundledPath)) {
29174
+ const moduleDir = path18.dirname(fileURLToPath(import.meta.url));
29175
+ const bundledPath = path18.resolve(moduleDir, "../docs/README.md");
29176
+ if (existsSync17(bundledPath)) {
28245
29177
  return bundledPath;
28246
29178
  }
28247
- return path17.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
29179
+ return path18.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
28248
29180
  }
28249
29181
 
28250
29182
  // src/core/packageVersion.ts
28251
- import { readFileSync as readFileSync10 } from "fs";
28252
- import path18 from "path";
29183
+ import { readFileSync as readFileSync11 } from "fs";
29184
+ import path19 from "path";
28253
29185
  import { fileURLToPath as fileURLToPath2 } from "url";
28254
29186
  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"));
29187
+ const moduleDir = path19.dirname(fileURLToPath2(import.meta.url));
29188
+ const packageJson = path19.resolve(moduleDir, "../..", "package.json");
29189
+ const value = JSON.parse(readFileSync11(packageJson, "utf8"));
28258
29190
  return typeof value.version === "string" ? value.version : "unknown";
28259
29191
  }
28260
29192
 
@@ -28279,7 +29211,14 @@ function createProgram() {
28279
29211
  const cwdFlag = findOptionValue(actionCommand ?? thisCommand, "cwd");
28280
29212
  const cwd = resolveCwd(typeof cwdFlag === "string" ? cwdFlag : void 0);
28281
29213
  setAuthSearchCwd(cwd);
28282
- await chooseResourceWhenNeeded(actionCommand ?? thisCommand, cwd);
29214
+ const command = actionCommand ?? thisCommand;
29215
+ if (!isResourceFreeCommand(command)) {
29216
+ withProjectLock(cwd, () => void 0, "preselect-resource");
29217
+ }
29218
+ if (!isPendingOperationReadAllowed(command)) {
29219
+ withProjectLock(cwd, () => assertNoPendingOperation(cwd), "pending-operation-gate");
29220
+ }
29221
+ await chooseResourceWhenNeeded(command, cwd);
28283
29222
  });
28284
29223
  for (const command of Object.values(createSubCommands())) {
28285
29224
  program.addCommand(command);
@@ -28287,6 +29226,10 @@ function createProgram() {
28287
29226
  return program;
28288
29227
  }
28289
29228
  var RESOURCE_FREE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "init", "template", "type", "resource", "create", "bind"]);
29229
+ var PENDING_OPERATION_READ_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "template", "type", "status", "show", "validate", "list", "recover"]);
29230
+ function isPendingOperationReadAllowed(command) {
29231
+ return PENDING_OPERATION_READ_COMMANDS.has(command.name());
29232
+ }
28290
29233
  async function chooseResourceWhenNeeded(command, cwd) {
28291
29234
  if (isResourceFreeCommand(command)) return;
28292
29235
  const selector = findOptionValue(command, "resource");
@@ -28317,7 +29260,7 @@ function resourceChoiceLabel(cwd, identity) {
28317
29260
  return [
28318
29261
  `${identity.n}.json`,
28319
29262
  `\u6807\u9898=${identity.title ?? "\uFF08\u672A\u540C\u6B65\u6807\u9898\uFF09"}`,
28320
- `\u6807\u8BC6=${identity.name ?? "\u672A\u7ED1\u5B9A"}`,
29263
+ `\u6807\u8BC6=${identity.resourceName ?? identity.name ?? "\u672A\u7ED1\u5B9A"}`,
28321
29264
  `ID=${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}`,
28322
29265
  `\u7C7B\u578B=${identity.typeCode}`,
28323
29266
  `\u4EA7\u7269=${identity.filePath ?? "\u672A\u8BBE\u7F6E"}`,
@@ -28325,15 +29268,10 @@ function resourceChoiceLabel(cwd, identity) {
28325
29268
  ].join(" ");
28326
29269
  }
28327
29270
  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
29271
  return identities.map((identity) => {
28333
29272
  const selectors = [
28334
29273
  ...identity.resourceId ? [`id:${identity.resourceId}`] : [],
28335
- ...identity.name ? [`name:${identity.name}`] : [],
28336
- ...identity.title && titleCounts.get(identity.title) === 1 ? [`title:${identity.title}`] : [],
29274
+ ...identity.resourceName ? [`name:${identity.resourceName}`] : [],
28337
29275
  `artifact:${identity.filePath}`,
28338
29276
  `file:${identity.n}.json`
28339
29277
  ];