@freelog-cli/cli2 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/index.js CHANGED
@@ -9168,11 +9168,11 @@ var require_mime_types = __commonJS({
9168
9168
  }
9169
9169
  return exts[0];
9170
9170
  }
9171
- function lookup(path16) {
9172
- if (!path16 || typeof path16 !== "string") {
9171
+ function lookup(path20) {
9172
+ if (!path20 || typeof path20 !== "string") {
9173
9173
  return false;
9174
9174
  }
9175
- var extension2 = extname("x." + path16).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 path16 = __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 = path16.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 = path16.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 = path16.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(path16, key, dots) {
13343
- if (!path16) return key;
13344
- return path16.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, path16) {
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(path16, key, dots), convertValue(value));
13433
+ formData.append(renderKey(path20, key, dots), convertValue(value));
13434
13434
  return false;
13435
13435
  }
13436
- if (value && !path16 && 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(path16, 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, path16, 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 " + path16.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, path16, 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, path16 ? path16.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, path16, 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 path16 = [];
13707
+ const path20 = [];
13708
13708
  const pattern = /[^.[\]]+|\[([^.[\]]*)]/g;
13709
13709
  let match;
13710
13710
  while ((match = pattern.exec(name)) !== null) {
13711
- throwIfDepthExceeded(path16.length);
13712
- path16.push(match[0] === "[]" ? "" : match[1] || match[0]);
13711
+ throwIfDepthExceeded(path20.length);
13712
+ path20.push(match[0] === "[]" ? "" : match[1] || match[0]);
13713
13713
  }
13714
- return path16;
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(path16, value, target, index) {
13729
+ function buildPath(path20, value, target, index) {
13730
13730
  throwIfDepthExceeded(index);
13731
- let name = path16[index++];
13731
+ let name = path20[index++];
13732
13732
  if (name === "__proto__") return true;
13733
13733
  const isNumericKey = Number.isFinite(+name);
13734
- const isLast = index >= path16.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(path16, 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 path16;
15465
+ let path20;
15466
15466
  try {
15467
- path16 = 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: path16,
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, path16, 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(path16)) {
15895
- cookie.push(`path=${path16}`);
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, path16, 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 path16 !== "string" ? [].concat(path16) : path16.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, path16, Empty) {
17810
17810
  k: cleanKey(stack.shift())
17811
17811
  };
17812
17812
  }
17813
- function setPath(object, path16, newValue) {
17814
- var _getLastOfPath = getLastOfPath(object, path16, 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, path16, newValue, concat) {
17818
- var _getLastOfPath2 = getLastOfPath(object, path16, 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, path16) {
17824
- var _getLastOfPath3 = getLastOfPath(object, path16), 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, path16) {
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[path16]) return obj[path16];
17950
- var paths = path16.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, path16) {
17965
17965
  }
17966
17966
  if (mix === void 0) return void 0;
17967
17967
  if (mix === null) return null;
17968
- if (path16.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 path16 = [lng, ns];
18026
- if (key && typeof key !== "string") path16 = path16.concat(key);
18027
- if (key && typeof key === "string") path16 = path16.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
- path16 = lng.split(".");
18029
+ path20 = lng.split(".");
18030
18030
  }
18031
- var result = getPath(this.data, path16);
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 path16 = [lng, ns];
18044
- if (key) path16 = path16.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
- path16 = lng.split(".");
18046
+ path20 = lng.split(".");
18047
18047
  value = ns;
18048
- ns = path16[1];
18048
+ ns = path20[1];
18049
18049
  }
18050
18050
  this.addNamespaces(ns);
18051
- setPath(this.data, path16, 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 path16 = [lng, ns];
18073
+ var path20 = [lng, ns];
18074
18074
  if (lng.indexOf(".") > -1) {
18075
- path16 = lng.split(".");
18075
+ path20 = lng.split(".");
18076
18076
  deep = resources;
18077
18077
  resources = ns;
18078
- ns = path16[1];
18078
+ ns = path20[1];
18079
18079
  }
18080
18080
  this.addNamespaces(ns);
18081
- var pack = getPath(this.data, path16) || {};
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, path16, 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 path16 = getPathWithDefaults(data, defaultData, key);
19082
- return _this.alwaysFormat ? _this.format(path16, 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
- })) : path16;
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\u72B6\u6001\uFF08N.json\u3001\u8D44\u6E90 ID\u3001\u6807\u8BC6\u7B26\u6216\u6807\u9898\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();
@@ -24247,6 +24247,9 @@ function createLogoutCommand() {
24247
24247
  // src/commands/create/create.ts
24248
24248
  import { Command as Command4 } from "commander";
24249
24249
 
24250
+ // src/domain/create/create.ts
24251
+ import path10 from "path";
24252
+
24250
24253
  // src/local/identity.ts
24251
24254
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4 } from "fs";
24252
24255
  import path6 from "path";
@@ -24270,28 +24273,47 @@ function sha256(content) {
24270
24273
  function readText(filePath) {
24271
24274
  return existsSync3(filePath) ? readFileSync2(filePath, "utf8") : null;
24272
24275
  }
24273
- 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) {
24274
24289
  const value = raw;
24275
24290
  if (value?.schemaVersion !== 1 || !Array.isArray(value.entries) || value.entries.length === 0) {
24276
24291
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u65E5\u5FD7\u65E0\u6548\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "LOCAL_TXN_INVALID");
24277
24292
  }
24293
+ const targets = /* @__PURE__ */ new Set();
24278
24294
  for (const entry2 of value.entries) {
24279
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)) {
24280
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");
24281
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);
24282
24303
  }
24283
24304
  return value;
24284
24305
  }
24285
- function applyEntry(entry2) {
24286
- const current = readText(entry2.path);
24306
+ function applyEntry(cwd, entry2) {
24307
+ const target = resolveTransactionTarget(cwd, entry2.path, true);
24308
+ const current = readText(target);
24287
24309
  if (current === entry2.after) return;
24288
24310
  if (current !== entry2.before) {
24289
- 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");
24290
24312
  }
24291
24313
  if (entry2.after === null) {
24292
- if (existsSync3(entry2.path)) unlinkSync2(entry2.path);
24314
+ if (existsSync3(target)) unlinkSync2(target);
24293
24315
  } else {
24294
- atomicWriteFile(entry2.path, entry2.after);
24316
+ atomicWriteFile(target, entry2.after);
24295
24317
  }
24296
24318
  }
24297
24319
  function recoverPendingTransaction(cwd) {
@@ -24303,23 +24325,25 @@ function recoverPendingTransaction(cwd) {
24303
24325
  } catch {
24304
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");
24305
24327
  }
24306
- const transaction2 = validateTransaction(raw);
24307
- for (const entry2 of transaction2.entries) applyEntry(entry2);
24328
+ const transaction2 = validateTransaction(cwd, raw);
24329
+ for (const entry2 of transaction2.entries) applyEntry(cwd, entry2);
24308
24330
  unlinkSync2(txnPath);
24309
24331
  }
24310
24332
  function commitLocalTransaction(cwd, changes) {
24311
24333
  if (changes.length === 0) return;
24312
- const root = `${path4.resolve(freelogDir(cwd))}${path4.sep}`;
24334
+ const root = path4.resolve(freelogDir(cwd));
24313
24335
  const paths = /* @__PURE__ */ new Set();
24314
24336
  const entries = changes.map((change) => {
24315
24337
  const target = path4.resolve(change.path);
24316
- if (!target.startsWith(root) || paths.has(target)) {
24338
+ if (path4.dirname(target) !== root || paths.has(target)) {
24317
24339
  throw new CliError("\u672C\u5730\u4E8B\u52A1\u76EE\u6807\u65E0\u6548", "LOCAL_TXN_INVALID");
24318
24340
  }
24341
+ const relativePath = path4.basename(target);
24342
+ resolveTransactionTarget(cwd, relativePath);
24319
24343
  paths.add(target);
24320
24344
  const before = readText(target);
24321
24345
  return {
24322
- path: target,
24346
+ path: relativePath,
24323
24347
  before,
24324
24348
  beforeSha256: sha256(before),
24325
24349
  after: change.content,
@@ -24329,7 +24353,7 @@ function commitLocalTransaction(cwd, changes) {
24329
24353
  if (entries.length === 0) return;
24330
24354
  atomicWriteFile(transactionFilePath(cwd), `${JSON.stringify({ schemaVersion: 1, entries }, null, 2)}
24331
24355
  `);
24332
- for (const entry2 of entries) applyEntry(entry2);
24356
+ for (const entry2 of entries) applyEntry(cwd, entry2);
24333
24357
  unlinkSync2(transactionFilePath(cwd));
24334
24358
  }
24335
24359
 
@@ -24453,10 +24477,11 @@ var IDENTITY_FILE_RE = /^([1-9]\d*)\.json$/;
24453
24477
  var inputFields = {
24454
24478
  subject: z2.literal("resource"),
24455
24479
  resourceId: z2.string().min(1).optional(),
24480
+ resourceName: z2.string().min(1).optional(),
24456
24481
  name: z2.string().min(1).optional(),
24457
24482
  title: z2.string().min(1).optional(),
24458
24483
  typeCode: z2.string().min(1),
24459
- filePath: z2.string().min(1).optional(),
24484
+ filePath: z2.string().min(1),
24460
24485
  env: z2.enum(["prod", "test", "dev"]).optional()
24461
24486
  };
24462
24487
  var createSchema = z2.object(inputFields).strict();
@@ -24466,9 +24491,10 @@ var storedSchema = z2.object({
24466
24491
  ...inputFields
24467
24492
  }).strict().superRefine((identity, ctx) => {
24468
24493
  const hasResourceId = identity.resourceId !== void 0;
24494
+ const hasResourceName = identity.resourceName !== void 0;
24469
24495
  const hasName = identity.name !== void 0;
24470
24496
  const hasTitle = identity.title !== void 0;
24471
- const bound = hasResourceId || hasName || hasTitle;
24497
+ const bound = hasResourceId || hasResourceName || hasName || hasTitle;
24472
24498
  if (hasResourceId !== hasName) {
24473
24499
  ctx.addIssue({
24474
24500
  code: ZodIssueCode.custom,
@@ -24476,8 +24502,11 @@ var storedSchema = z2.object({
24476
24502
  message: "\u7ED1\u5B9A\u8EAB\u4EFD\u5FC5\u987B\u540C\u65F6\u5305\u542B resourceId \u548C name"
24477
24503
  });
24478
24504
  }
24479
- if (!hasResourceId && !hasName && hasTitle) {
24480
- 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" });
24481
24510
  }
24482
24511
  if (!bound && identity.env !== void 0) {
24483
24512
  ctx.addIssue({
@@ -24493,6 +24522,9 @@ function freelogDir(cwd) {
24493
24522
  function identityFilePath(cwd, n) {
24494
24523
  return path6.join(freelogDir(cwd), `${n}.json`);
24495
24524
  }
24525
+ function identitySequenceFilePath(cwd) {
24526
+ return path6.join(freelogDir(cwd), ".sequence");
24527
+ }
24496
24528
  function assertIdentityNumber(n) {
24497
24529
  if (!Number.isInteger(n) || n < 1) {
24498
24530
  throw new CliError("\u8EAB\u4EFD\u7F16\u53F7\u65E0\u6548", "IDENTITY_INVALID");
@@ -24513,9 +24545,12 @@ function throwZodAsCliError(error2) {
24513
24545
  if (field === "subject") {
24514
24546
  throw new CliError("\u672C\u671F\u53EA\u652F\u6301\u5355\u8D44\u6E90", "IDENTITY_SUBJECT_UNSUPPORTED");
24515
24547
  }
24516
- if (field === "name" || field === "resourceId" || field === "title") {
24548
+ if (field === "name" || field === "resourceName" || field === "resourceId" || field === "title") {
24517
24549
  throw new CliError("\u7ED1\u5B9A\u8EAB\u4EFD\u5B57\u6BB5\u4E0D\u5B8C\u6574", "IDENTITY_BINDING_INVALID");
24518
24550
  }
24551
+ if (field === "filePath") {
24552
+ throw new CliError("\u6BCF\u4EFD\u8D44\u6E90\u72B6\u6001\u5FC5\u987B\u5173\u8054\u672C\u5730\u4EA7\u7269\u8DEF\u5F84", "IDENTITY_ARTIFACT_REQUIRED");
24553
+ }
24519
24554
  throw new CliError("\u8EAB\u4EFD\u5B57\u6BB5\u65E0\u6548", "IDENTITY_INVALID");
24520
24555
  }
24521
24556
  function toStored(data) {
@@ -24523,10 +24558,11 @@ function toStored(data) {
24523
24558
  schemaVersion: 1,
24524
24559
  subject: data.subject,
24525
24560
  ...data.resourceId ? { resourceId: data.resourceId } : {},
24561
+ ...data.resourceName ? { resourceName: data.resourceName } : {},
24526
24562
  ...data.name ? { name: data.name } : {},
24527
24563
  ...data.title ? { title: data.title } : {},
24528
24564
  typeCode: data.typeCode,
24529
- ...data.filePath ? { filePath: data.filePath } : {},
24565
+ filePath: data.filePath,
24530
24566
  ...data.env === "test" || data.env === "dev" ? { env: data.env } : {}
24531
24567
  };
24532
24568
  }
@@ -24542,10 +24578,11 @@ function toDiskObject(identity) {
24542
24578
  schemaVersion: 1,
24543
24579
  subject: identity.subject,
24544
24580
  ...identity.resourceId ? { resourceId: identity.resourceId } : {},
24581
+ ...identity.resourceName ? { resourceName: identity.resourceName } : {},
24545
24582
  ...identity.name ? { name: identity.name } : {},
24546
24583
  ...identity.title ? { title: identity.title } : {},
24547
24584
  typeCode: identity.typeCode,
24548
- ...identity.filePath ? { filePath: identity.filePath } : {},
24585
+ filePath: identity.filePath,
24549
24586
  ...identity.env ? { env: identity.env } : {}
24550
24587
  };
24551
24588
  }
@@ -24569,6 +24606,7 @@ function prepareIdentityUpdate(cwd, n, patch) {
24569
24606
  const identity = normalize({
24570
24607
  subject: parsed.subject ?? current.subject,
24571
24608
  resourceId: parsed.resourceId ?? current.resourceId,
24609
+ resourceName: parsed.resourceName ?? current.resourceName,
24572
24610
  name: parsed.name ?? current.name,
24573
24611
  title: parsed.title ?? current.title,
24574
24612
  typeCode: parsed.typeCode ?? current.typeCode,
@@ -24579,7 +24617,23 @@ function prepareIdentityUpdate(cwd, n, patch) {
24579
24617
  }
24580
24618
  function prepareIdentityCreate(cwd, input2) {
24581
24619
  const numbers = listIdentityNumbers(cwd);
24582
- 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
+ `;
24583
24637
  }
24584
24638
  function parsePatchInput(input2) {
24585
24639
  const parsed = patchSchema.safeParse(input2);
@@ -24625,7 +24679,10 @@ function listIdentities(cwd) {
24625
24679
  function createIdentity(cwd, input2) {
24626
24680
  return withProjectLock(cwd, () => {
24627
24681
  const identity = prepareIdentityCreate(cwd, input2);
24628
- 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
+ ]);
24629
24686
  return identity;
24630
24687
  }, "create-identity");
24631
24688
  }
@@ -24638,7 +24695,7 @@ function updateIdentity(cwd, n, patch) {
24638
24695
  }
24639
24696
 
24640
24697
  // src/local/resolve.ts
24641
- import { existsSync as existsSync7, readdirSync as readdirSync2 } from "fs";
24698
+ import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
24642
24699
 
24643
24700
  // src/local/draft.ts
24644
24701
  import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync4 } from "fs";
@@ -24757,35 +24814,80 @@ function draftSummary(draft) {
24757
24814
  return ["\u5C06\u8986\u76D6\u672C\u5730\u5DE5\u4F5C\u7A3F\uFF1A", ` \u6765\u6E90\uFF1A${source}`, ` \u6587\u4EF6\uFF1A${draft.filename ?? "\u65E0"} sha1=${sha1}`, ` \u5C5E\u6027 ${(draft.customPropertyDescriptors ?? []).length} / \u4F9D\u8D56 ${(draft.dependencies ?? []).length}`].join("\n");
24758
24815
  }
24759
24816
 
24817
+ // src/local/projectPath.ts
24818
+ import path8 from "path";
24819
+ import { existsSync as existsSync7, realpathSync } from "fs";
24820
+ var defaultError = {
24821
+ code: "FILE_PATH_OUTSIDE_PROJECT",
24822
+ message: "\u8DEF\u5F84\u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
24823
+ };
24824
+ function normalizeProjectPath(cwd, rawPath, error2 = defaultError) {
24825
+ if (!rawPath.trim() || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || path8.posix.isAbsolute(rawPath)) {
24826
+ throw new CliError(error2.message, error2.code);
24827
+ }
24828
+ const root = path8.resolve(cwd);
24829
+ const resolved = path8.resolve(root, rawPath);
24830
+ const relative = path8.relative(root, resolved);
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)) {
24851
+ throw new CliError(error2.message, error2.code);
24852
+ }
24853
+ return realTarget;
24854
+ }
24855
+
24760
24856
  // src/local/resolve.ts
24761
- function assertNoDuplicate(identities, key) {
24857
+ function assertNoDuplicate(cwd, identities, key) {
24762
24858
  const seen = /* @__PURE__ */ new Map();
24763
24859
  for (const identity of identities) {
24764
24860
  const value = identity[key];
24765
24861
  if (!value) continue;
24766
- const previous = seen.get(value);
24862
+ const comparable = key === "filePath" ? normalizeProjectPath(cwd, value) : value;
24863
+ const previous = seen.get(comparable);
24767
24864
  if (previous !== void 0) {
24768
24865
  throw new CliError(`\u672C\u5730\u72B6\u6001\u51B2\u7A81\uFF1A${previous}.json \u4E0E ${identity.n}.json \u4F7F\u7528\u540C\u4E00 ${key}`, "IDENTITY_CONFLICT");
24769
24866
  }
24770
- seen.set(value, identity.n);
24867
+ seen.set(comparable, identity.n);
24771
24868
  }
24772
24869
  }
24773
- function matches(identity, selector) {
24870
+ function matches(cwd, identity, selector) {
24774
24871
  if (selector.startsWith("file:")) return `${identity.n}.json` === selector.slice(5);
24775
24872
  if (selector.startsWith("id:")) return identity.resourceId === selector.slice(3);
24776
- if (selector.startsWith("name:")) return identity.name === selector.slice(5) || identity.name === selector.slice(5).split("/").pop();
24777
- if (selector.startsWith("title:")) return identity.title === selector.slice(6);
24778
- return `${identity.n}.json` === selector || identity.resourceId === selector || identity.name === selector || identity.title === selector;
24873
+ if (selector.startsWith("name:")) return identity.resourceName === selector.slice(5);
24874
+ if (selector.startsWith("artifact:")) {
24875
+ return normalizeProjectPath(cwd, identity.filePath) === normalizeProjectPath(
24876
+ cwd,
24877
+ selector.slice("artifact:".length)
24878
+ );
24879
+ }
24880
+ return `${identity.n}.json` === selector || identity.resourceId === selector || identity.resourceName === selector;
24779
24881
  }
24780
24882
  var DRAFT_FILE_RE = /^([1-9]\d*)\.version\.json$/;
24781
24883
  function validateLocalState(cwd) {
24782
24884
  const identities = listIdentities(cwd);
24783
- assertNoDuplicate(identities, "resourceId");
24784
- assertNoDuplicate(identities, "name");
24785
- assertNoDuplicate(identities, "filePath");
24885
+ assertNoDuplicate(cwd, identities, "resourceId");
24886
+ assertNoDuplicate(cwd, identities, "resourceName");
24887
+ assertNoDuplicate(cwd, identities, "filePath");
24786
24888
  const numbers = new Set(identities.map((identity) => identity.n));
24787
24889
  const dir = freelogDir(cwd);
24788
- if (!existsSync7(dir)) return identities;
24890
+ if (!existsSync8(dir)) return identities;
24789
24891
  for (const fileName of readdirSync2(dir)) {
24790
24892
  const match = DRAFT_FILE_RE.exec(fileName);
24791
24893
  if (!match) continue;
@@ -24801,7 +24903,7 @@ function resolveIdentity(cwd, selector) {
24801
24903
  const identities = validateLocalState(cwd);
24802
24904
  if (identities.length === 0) {
24803
24905
  throw new CliError(
24804
- "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u8D44\u6E90\u72B6\u6001\u3002\u65B0\u8D44\u6E90\u8BF7\u5148 init \u540E create\uFF1B\u5DF2\u6709\u8D44\u6E90\u8BF7 bind <\u8D44\u6E90 ID|\u6807\u8BC6\u7B26> --artifact <\u8DEF\u5F84>\u3002",
24906
+ "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u8D44\u6E90\u72B6\u6001\u3002\u65B0\u8D44\u6E90\u53EF\u76F4\u63A5 create --type <\u53F6\u5B50\u7C7B\u578B> --artifact <\u8DEF\u5F84>\uFF0C\u4E5F\u53EF\u5148 init \u518D create\uFF1B\u5DF2\u6709\u8D44\u6E90\u8BF7 bind <\u8D44\u6E90 ID|\u6807\u8BC6\u7B26> --artifact <\u8DEF\u5F84>\u3002",
24805
24907
  "IDENTITY_NOT_FOUND"
24806
24908
  );
24807
24909
  }
@@ -24809,12 +24911,12 @@ function resolveIdentity(cwd, selector) {
24809
24911
  if (identities.length === 1) return identities[0];
24810
24912
  throw new CliError("\u5F53\u524D\u5DE5\u7A0B\u6709\u591A\u4EFD\u8D44\u6E90\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 --resource \u6307\u5B9A\u8D44\u6E90", "IDENTITY_RESOURCE_REQUIRED");
24811
24913
  }
24812
- const matched = identities.filter((identity) => matches(identity, selector));
24914
+ const matched = identities.filter((identity) => matches(cwd, identity, selector));
24813
24915
  if (matched.length === 1) return matched[0];
24814
24916
  if (matched.length === 0) {
24815
24917
  throw new CliError(`\u627E\u4E0D\u5230\u8D44\u6E90\u9009\u62E9\u5668 ${selector}`, "IDENTITY_RESOURCE_NOT_FOUND");
24816
24918
  }
24817
- throw new CliError(`\u8D44\u6E90\u9009\u62E9\u5668 ${selector} \u5339\u914D\u591A\u4EFD\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 file:\u3001id: \u6216 name:`, "IDENTITY_RESOURCE_AMBIGUOUS");
24919
+ throw new CliError(`\u8D44\u6E90\u9009\u62E9\u5668 ${selector} \u5339\u914D\u591A\u4EFD\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 id:\u3001name:\u3001artifact: \u6216 file:`, "IDENTITY_RESOURCE_AMBIGUOUS");
24818
24920
  }
24819
24921
 
24820
24922
  // src/core/tty.ts
@@ -24854,6 +24956,16 @@ async function confirmWrite(preview, yes) {
24854
24956
  }
24855
24957
  return preview;
24856
24958
  }
24959
+ async function confirmDestructive(preview, action, yes, interactive = isInteractive(), ask = confirmQuestion) {
24960
+ if (yes) {
24961
+ return true;
24962
+ }
24963
+ if (!interactive) {
24964
+ throw new CliError(`\u6709\u672A\u63D0\u4EA4\u5DE5\u4F5C\u7A3F\uFF1B\u8BF7\u52A0 --yes \u786E\u8BA4${action}`, "DRAFT_DESTRUCTIVE_NEED_YES");
24965
+ }
24966
+ return ask(`${preview}
24967
+ \u786E\u8BA4${action}\uFF1F`, false);
24968
+ }
24857
24969
 
24858
24970
  // src/domain/create/typePick.ts
24859
24971
  function unwrapList(result) {
@@ -24872,6 +24984,15 @@ function unwrapOne(result) {
24872
24984
  const data = envelope.data ?? result;
24873
24985
  return data && typeof data === "object" && !Array.isArray(data) ? data : void 0;
24874
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
+ }
24875
24996
  function isEnabledResourceLeaf(node) {
24876
24997
  return node.isTerminate === true && node.status === 1 && supportsResourceSubject(node.subjectType);
24877
24998
  }
@@ -24901,6 +25022,28 @@ async function resourceTypeTree(apis) {
24901
25022
  const request2 = apis.resourceTypes ?? ((params) => service_API_default.Resource.resourceTypes(params));
24902
25023
  return unwrapList(await request2({ category: 1, status: 1, subjectType: 1 }));
24903
25024
  }
25025
+ function findTypePath(nodes2, code, ancestors = []) {
25026
+ for (const node of nodes2) {
25027
+ const path20 = [...ancestors, node];
25028
+ if (node.code === code) return path20;
25029
+ if (hasChildren(node)) {
25030
+ const found = findTypePath(node.children, code, path20);
25031
+ if (found) return found;
25032
+ }
25033
+ }
25034
+ return void 0;
25035
+ }
25036
+ async function getTypeHierarchy(code, apis = {}) {
25037
+ const normalized = code.trim();
25038
+ if (!normalized) throw new CliError("\u8BF7\u63D0\u4F9B\u8D44\u6E90\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
25039
+ const path20 = findTypePath(await resourceTypeTree(apis), normalized);
25040
+ if (!path20) throw new CliError(`\u5E73\u53F0\u7C7B\u578B\u6811\u4E0D\u5305\u542B\u5F53\u524D\u7C7B\u578B ${normalized}`, "TYPE_HIERARCHY_NOT_FOUND");
25041
+ const names = path20.map((node) => node.name.trim());
25042
+ if (names.some((name) => !name)) {
25043
+ throw new CliError("\u5E73\u53F0\u7C7B\u578B\u94FE\u5B58\u5728\u7F3A\u5931\u540D\u79F0\uFF0C\u65E0\u6CD5\u5C55\u793A\u5B8C\u6574\u5C42\u7EA7", "TYPE_HIERARCHY_INVALID");
25044
+ }
25045
+ return names;
25046
+ }
24904
25047
  async function listLeafTypes(apis = {}) {
24905
25048
  return flattenLeaves(await resourceTypeTree(apis));
24906
25049
  }
@@ -24921,7 +25064,8 @@ async function getTypeInfo(code, apis = {}) {
24921
25064
  }
24922
25065
  assertPlatformAllowed();
24923
25066
  const request2 = apis.getByCode ?? ((params) => service_API_default.Resource.getResourceTypeInfoByCode(params));
24924
- const info2 = unwrapOne(await request2({ code: normalized }));
25067
+ const rawInfo = unwrapOne(await request2({ code: normalized }));
25068
+ const info2 = rawInfo ? normalizeTypeInfo(rawInfo) : void 0;
24925
25069
  if (!info2?.code) {
24926
25070
  throw new CliError(`\u627E\u4E0D\u5230\u7C7B\u578B ${normalized}`, "TYPE_NOT_FOUND");
24927
25071
  }
@@ -24988,30 +25132,116 @@ async function chooseLeafType(apis = {}) {
24988
25132
  function formatTypeList(items) {
24989
25133
  return items.map((item) => `${item.code} ${item.nameChain ?? item.name}`).join("\n");
24990
25134
  }
25135
+ function formatTypeInfo(item) {
25136
+ const optionalConfig = supportsOptionalConfig(item) ? "\u652F\u6301" : "\u4E0D\u652F\u6301";
25137
+ return `${item.code} ${item.nameChain ?? item.name} \u53EF\u9009\u914D\u7F6E\uFF1A${optionalConfig}`;
25138
+ }
24991
25139
 
24992
- // src/local/projectPath.ts
24993
- import path8 from "path";
24994
- var defaultError = {
24995
- code: "FILE_PATH_OUTSIDE_PROJECT",
24996
- message: "\u8DEF\u5F84\u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
24997
- };
24998
- function normalizeProjectPath(cwd, rawPath, error2 = defaultError) {
24999
- if (!rawPath.trim() || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || path8.posix.isAbsolute(rawPath)) {
25000
- throw new CliError(error2.message, error2.code);
25140
+ // src/domain/version/zip.ts
25141
+ import { createWriteStream, existsSync as existsSync9, lstatSync, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync5 } from "fs";
25142
+ import { tmpdir } from "os";
25143
+ import { randomUUID } from "crypto";
25144
+ import path9 from "path";
25145
+ import { finished } from "stream/promises";
25146
+ import archiver from "archiver";
25147
+ function isThemeOrWidget(typeCode) {
25148
+ return typeCode === "RT001" || typeCode === "RT002";
25149
+ }
25150
+ function assertArtifactPath(typeCode, filePath) {
25151
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25152
+ const themeOrWidget = isThemeOrWidget(typeCode);
25153
+ if (stats?.isDirectory() && !themeOrWidget) {
25154
+ throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25001
25155
  }
25002
- const root = path8.resolve(cwd);
25003
- const resolved = path8.resolve(root, rawPath);
25004
- const relative = path8.relative(root, resolved);
25005
- if (!relative || relative.startsWith("..") || path8.isAbsolute(relative)) {
25006
- throw new CliError(error2.message, error2.code);
25156
+ }
25157
+ function assertArtifactAnchor(typeCode, filePath, cwd) {
25158
+ if (!existsSync9(filePath)) {
25159
+ throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25160
+ }
25161
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25162
+ const stats = statSync(filePath);
25163
+ assertArtifactPath(typeCode, filePath);
25164
+ if (!isThemeOrWidget(typeCode) && !stats.isFile()) {
25165
+ throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
25166
+ }
25167
+ }
25168
+ function assertUploadArtifactReady(typeCode, filePath, cwd) {
25169
+ if (cwd) resolveExistingProjectPath(cwd, filePath);
25170
+ assertArtifactPath(typeCode, filePath);
25171
+ const stats = existsSync9(filePath) ? statSync(filePath) : void 0;
25172
+ if (!stats) {
25173
+ throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${filePath}`, "ARTIFACT_ANCHOR_MISSING");
25174
+ }
25175
+ if (stats.isDirectory() && isThemeOrWidget(typeCode)) {
25176
+ assertZipDirectoryReady(filePath);
25177
+ return;
25178
+ }
25179
+ if (!stats.isFile()) {
25180
+ throw new CliError("\u4EA7\u7269\u5FC5\u987B\u662F\u6587\u4EF6\uFF0C\u6216\u4E3B\u9898/\u63D2\u4EF6\u7684\u975E\u7A7A\u6784\u5EFA\u76EE\u5F55\u3002", "FILE_ARTIFACT_UNSUPPORTED");
25181
+ }
25182
+ }
25183
+ function assertZipDirectoryReady(dir) {
25184
+ if (!existsSync9(dir) || !statSync(dir).isDirectory()) {
25185
+ throw new CliError(`\u6CA1\u6709\u6784\u5EFA\u4EA7\u7269 ${dir}\uFF0C\u8BF7\u5148\u6784\u5EFA`, "ZIP_MISSING");
25186
+ }
25187
+ if (existsSync9(path9.join(dir, ".freelog"))) {
25188
+ throw new CliError("\u4E0D\u8981\u628A\u5DE5\u7A0B\u6839\u5F53\u4EA7\u7269\u76EE\u5F55\uFF0C\u8BF7\u6307\u5B9A dist \u6216 build\u3002", "ZIP_PROJECT_ROOT");
25189
+ }
25190
+ if (readdirSync3(dir).length === 0) {
25191
+ throw new CliError("\u6784\u5EFA\u4EA7\u7269\u662F\u7A7A\u7684\uFF0C\u8BF7\u5148\u6784\u5EFA", "ZIP_EMPTY");
25192
+ }
25193
+ }
25194
+ async function zipDirectoryContents(dir) {
25195
+ assertZipDirectoryReady(dir);
25196
+ const outPath = path9.join(
25197
+ tmpdir(),
25198
+ `freelog-zip-${process.pid}-${randomUUID()}.zip`
25199
+ );
25200
+ const output2 = createWriteStream(outPath);
25201
+ const archive = archiver("zip", { zlib: { level: 9 } });
25202
+ try {
25203
+ archive.on("error", (error2) => output2.destroy(error2));
25204
+ archive.pipe(output2);
25205
+ const appendEntries = (currentDir, prefix = "") => {
25206
+ for (const entry2 of readdirSync3(currentDir).sort((left, right) => left < right ? -1 : left > right ? 1 : 0)) {
25207
+ const full = path9.join(currentDir, entry2);
25208
+ const stats = lstatSync(full);
25209
+ const entryName = prefix ? `${prefix}/${entry2}` : entry2;
25210
+ if (stats.isSymbolicLink()) {
25211
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u4E0D\u80FD\u5305\u542B\u8F6F\u94FE\u63A5\uFF1A${entryName}`, "ZIP_SYMLINK_UNSUPPORTED");
25212
+ }
25213
+ if (stats.isDirectory()) {
25214
+ appendEntries(full, entryName);
25215
+ } else if (stats.isFile()) {
25216
+ archive.append(readFileSync6(full), { name: entryName });
25217
+ } else {
25218
+ throw new CliError(`\u6784\u5EFA\u76EE\u5F55\u5305\u542B\u4E0D\u652F\u6301\u7684\u6761\u76EE\uFF1A${entryName}`, "ZIP_ENTRY_UNSUPPORTED");
25219
+ }
25220
+ }
25221
+ };
25222
+ appendEntries(dir);
25223
+ await archive.finalize();
25224
+ await finished(output2);
25225
+ return outPath;
25226
+ } catch (error2) {
25227
+ if (existsSync9(outPath)) {
25228
+ try {
25229
+ unlinkSync5(outPath);
25230
+ } catch {
25231
+ }
25232
+ }
25233
+ throw error2;
25234
+ }
25235
+ }
25236
+ async function prepareUploadPath(typeCode, filePath, cwd) {
25237
+ assertUploadArtifactReady(typeCode, filePath, cwd);
25238
+ if (isThemeOrWidget(typeCode) && existsSync9(filePath) && statSync(filePath).isDirectory()) {
25239
+ return zipDirectoryContents(filePath);
25007
25240
  }
25008
- return relative.replaceAll("\\", "/");
25241
+ return filePath;
25009
25242
  }
25010
25243
 
25011
25244
  // src/domain/create/create.ts
25012
- function isFixedTemplateType(typeCode) {
25013
- return typeCode === "RT001" || typeCode === "RT002";
25014
- }
25015
25245
  function unwrapData(result) {
25016
25246
  const envelope = result;
25017
25247
  return envelope.data ?? result;
@@ -25068,6 +25298,9 @@ function validateCreateFlags(input2) {
25068
25298
  function resolveTargetIdentity(input2) {
25069
25299
  if (input2.selector) return resolveIdentity(input2.cwd, input2.selector);
25070
25300
  const unbound = input2.identities.filter((identity) => !identity.resourceId);
25301
+ if (input2.artifact !== void 0) {
25302
+ return unbound.find((identity) => identity.filePath === input2.artifact);
25303
+ }
25071
25304
  if (unbound.length === 1) return unbound[0];
25072
25305
  if (unbound.length > 1) throw new CliError("\u5F53\u524D\u5DE5\u7A0B\u6709\u591A\u4EFD\u672A\u7ED1\u5B9A\u8D44\u6E90\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 --resource \u6307\u5B9A\u8D44\u6E90", "IDENTITY_RESOURCE_REQUIRED");
25073
25306
  return void 0;
@@ -25081,7 +25314,8 @@ async function assertOwnShellAvailable(input2) {
25081
25314
  if (!existing.resourceId) {
25082
25315
  return;
25083
25316
  }
25084
- if (existing.userId !== input2.authUserId) {
25317
+ const ownerId = existing.userId ?? existing.ownerId ?? existing.creatorId;
25318
+ if (ownerId !== input2.authUserId) {
25085
25319
  throw new CliError(
25086
25320
  `\u8D44\u6E90\u6388\u6743\u6807\u8BC6 ${input2.name} \u5DF2\u88AB\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002`,
25087
25321
  "CREATE_NAME_TAKEN"
@@ -25093,29 +25327,31 @@ async function assertOwnShellAvailable(input2) {
25093
25327
  );
25094
25328
  }
25095
25329
  function writeCreatedIdentity(input2) {
25096
- const filePath = input2.file;
25330
+ const filePath = input2.file ?? input2.target?.filePath;
25331
+ if (!filePath) throw new CliError("\u65B0\u8D44\u6E90\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "CREATE_ARTIFACT_REQUIRED");
25097
25332
  const patch = {
25098
25333
  resourceId: input2.resourceId,
25334
+ resourceName: input2.resourceName,
25099
25335
  name: input2.name,
25100
25336
  title: input2.title,
25101
25337
  typeCode: input2.typeCode,
25102
- ...filePath ? { filePath } : {},
25338
+ filePath,
25103
25339
  env: input2.env
25104
25340
  };
25105
25341
  return input2.target ? updateIdentity(input2.cwd, input2.target.n, patch) : createIdentity(input2.cwd, {
25106
25342
  subject: "resource",
25107
25343
  resourceId: input2.resourceId,
25344
+ resourceName: input2.resourceName,
25108
25345
  name: input2.name,
25109
25346
  title: input2.title,
25110
25347
  typeCode: input2.typeCode,
25111
- ...filePath ? { filePath } : {},
25348
+ filePath,
25112
25349
  env: input2.env
25113
25350
  });
25114
25351
  }
25115
25352
  async function createResource(input2) {
25116
25353
  assertPlatformAllowed();
25117
25354
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25118
- validateLocalState(input2.cwd);
25119
25355
  const { title, name } = validateCreateFlags(input2);
25120
25356
  const file = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25121
25357
  code: "CREATE_FILE_OUTSIDE",
@@ -25126,6 +25362,7 @@ async function createResource(input2) {
25126
25362
  const target = resolveTargetIdentity({
25127
25363
  cwd: input2.cwd,
25128
25364
  selector: input2.selector,
25365
+ artifact: file,
25129
25366
  identities
25130
25367
  });
25131
25368
  const occupant = file ? identities.find((identity) => identity.filePath === file && identity.n !== target?.n) : void 0;
@@ -25141,18 +25378,17 @@ async function createResource(input2) {
25141
25378
  if (input2.yes && !input2.type && !target?.typeCode) {
25142
25379
  throw new CliError("--yes \u5728\u6CA1\u6709\u5DE5\u7A0B\u7C7B\u578B\u65F6\u5FC5\u987B\u63D0\u4F9B --type", "CREATE_YES_FLAGS");
25143
25380
  }
25144
- const targetTypeCode = target?.typeCode;
25145
- if (!target && isFixedTemplateType(input2.type) && !file) {
25146
- throw new CliError(
25147
- input2.type === "RT001" ? "\u5DF2\u6709\u4E3B\u9898\u5DE5\u7A0B\u8BF7\u901A\u8FC7 --artifact \u6307\u5B9A\u6784\u5EFA\u76EE\u5F55" : "\u5DF2\u6709\u63D2\u4EF6\u5DE5\u7A0B\u8BF7\u901A\u8FC7 --artifact \u6307\u5B9A\u6784\u5EFA\u76EE\u5F55",
25148
- "CREATE_FIXED_TYPE_FILE_REQUIRED"
25149
- );
25381
+ const artifact = file ?? target?.filePath;
25382
+ if (!artifact) {
25383
+ throw new CliError("\u65B0\u8D44\u6E90\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "CREATE_ARTIFACT_REQUIRED");
25150
25384
  }
25151
- if (isFixedTemplateType(targetTypeCode) && input2.type && input2.type !== targetTypeCode) {
25385
+ const targetTypeCode = target?.typeCode;
25386
+ if ((targetTypeCode === "RT001" || targetTypeCode === "RT002") && input2.type && input2.type !== targetTypeCode) {
25152
25387
  throw new CliError("\u4E3B\u9898/\u63D2\u4EF6\u5DE5\u7A0B\u7684\u8D44\u6E90\u7C7B\u578B\u56FA\u5B9A\uFF0C\u4E0D\u80FD\u7528 --type \u6539\u5199", "CREATE_FIXED_TYPE");
25153
25388
  }
25154
25389
  const typeCode = resolveTypeCode(input2.type, target);
25155
25390
  await getTypeInfo(typeCode, input2.apis);
25391
+ assertArtifactAnchor(typeCode, path10.resolve(input2.cwd, artifact), input2.cwd);
25156
25392
  await assertOwnShellAvailable({
25157
25393
  authLoginName: auth.loginName,
25158
25394
  authUserId: auth.userId,
@@ -25175,6 +25411,7 @@ async function createResource(input2) {
25175
25411
  cwd: input2.cwd,
25176
25412
  env: getEnv2(),
25177
25413
  name,
25414
+ resourceName: typeof created.resourceName === "string" && created.resourceName ? created.resourceName : `${auth.loginName}/${name}`,
25178
25415
  typeCode,
25179
25416
  resourceId,
25180
25417
  title,
@@ -25222,6 +25459,10 @@ function createCreateCommand() {
25222
25459
  // src/commands/listing/update.ts
25223
25460
  import { Command as Command5 } from "commander";
25224
25461
 
25462
+ // src/domain/listing/update.ts
25463
+ import { existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
25464
+ import path11 from "path";
25465
+
25225
25466
  // src/domain/version/gates.ts
25226
25467
  function evaluateGates(input2, intent) {
25227
25468
  const latest = input2.latestVersion;
@@ -25263,19 +25504,170 @@ function resolveBoundIdentity(cwd, file) {
25263
25504
  if (!identity.resourceId) {
25264
25505
  throw new CliError("\u8BF7\u5148 create \u6216 bind", "GATE_NO_RESOURCE");
25265
25506
  }
25507
+ const identityEnv = identity.env ?? "prod";
25508
+ const activeEnv2 = getEnv2();
25509
+ if (identityEnv !== activeEnv2) {
25510
+ throw new CliError(
25511
+ `\u8D44\u6E90 ${identity.n}.json \u5C5E\u4E8E ${identityEnv} \u73AF\u5883\uFF0C\u672C\u6B21\u662F ${activeEnv2}\uFF1B\u8BF7\u5207\u6362 --env \u6216\u9009\u62E9\u8BE5\u73AF\u5883\u7684\u8D44\u6E90\u72B6\u6001`,
25512
+ "RESOURCE_ENV_MISMATCH"
25513
+ );
25514
+ }
25266
25515
  return identity;
25267
25516
  }
25517
+ function assertRemoteResourceWritable(input2) {
25518
+ const codes = {
25519
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25520
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER",
25521
+ frozen: input2.codes?.frozen ?? "RESOURCE_FROZEN"
25522
+ };
25523
+ assertRemoteResourceOwned({
25524
+ info: input2.info,
25525
+ resourceId: input2.resourceId,
25526
+ authUserId: input2.authUserId,
25527
+ codes
25528
+ });
25529
+ if (input2.info.status === 2 || input2.info.isFrozen === true) {
25530
+ throw new CliError("\u8D44\u6E90\u5DF2\u88AB\u51BB\u7ED3\uFF0C\u4E0D\u80FD\u4FEE\u6539", codes.frozen);
25531
+ }
25532
+ }
25533
+ function assertRemoteResourceOwned(input2) {
25534
+ const codes = {
25535
+ invalid: input2.codes?.invalid ?? "RESOURCE_WRITE_INFO_INVALID",
25536
+ notOwner: input2.codes?.notOwner ?? "RESOURCE_NOT_OWNER"
25537
+ };
25538
+ if (input2.info.resourceId !== input2.resourceId) {
25539
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25540
+ }
25541
+ const ownerId = input2.info.userId ?? input2.info.ownerId ?? input2.info.creatorId;
25542
+ if (typeof ownerId !== "number" || !Number.isSafeInteger(ownerId)) {
25543
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8D44\u6E90 owner\uFF0C\u62D2\u7EDD\u5199\u5165", codes.invalid);
25544
+ }
25545
+ if (ownerId !== input2.authUserId) {
25546
+ throw new CliError("\u53EA\u80FD\u4FEE\u6539\u81EA\u5DF1\u7684\u8D44\u6E90", codes.notOwner);
25547
+ }
25548
+ }
25549
+
25550
+ // src/platform/unwrap.ts
25551
+ function unwrapData2(result) {
25552
+ const envelope = result;
25553
+ const data = envelope.data ?? result;
25554
+ return data ?? {};
25555
+ }
25556
+ function unwrapFirst(result) {
25557
+ const envelope = result;
25558
+ const data = envelope.data ?? result;
25559
+ if (Array.isArray(data)) {
25560
+ return data[0] ?? {};
25561
+ }
25562
+ return data ?? {};
25563
+ }
25564
+ function unwrapList2(result, fields = ["dataList", "list"]) {
25565
+ const envelope = result;
25566
+ const data = envelope.data ?? result;
25567
+ if (Array.isArray(data)) {
25568
+ return data;
25569
+ }
25570
+ if (data && typeof data === "object") {
25571
+ for (const field of fields) {
25572
+ const list2 = data[field];
25573
+ if (Array.isArray(list2)) {
25574
+ return list2;
25575
+ }
25576
+ }
25577
+ }
25578
+ return [];
25579
+ }
25268
25580
 
25269
25581
  // src/domain/listing/update.ts
25270
25582
  var MAX_TITLE = 100;
25271
25583
  var MAX_INTRO = 200;
25272
25584
  var MAX_TAGS = 20;
25273
25585
  var MAX_TAG_LENGTH = 20;
25586
+ var MAX_COVER_BYTES = 5 * 1024 * 1024;
25587
+ var COVER_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif"]);
25274
25588
  function assertTitle(title) {
25589
+ if (!title) {
25590
+ throw new CliError("\u8BF7\u8F93\u5165\u8D44\u6E90\u6807\u9898", "UPDATE_TITLE_REQUIRED");
25591
+ }
25275
25592
  if (title.length > MAX_TITLE) {
25276
25593
  throw new CliError("\u6807\u9898\u4E0D\u80FD\u8D85\u8FC7100\u4E2A\u5B57\u7B26", "UPDATE_TITLE_LONG");
25277
25594
  }
25278
25595
  }
25596
+ function isAnimatedGif(content) {
25597
+ if (content.subarray(0, 6).toString("ascii") !== "GIF87a" && content.subarray(0, 6).toString("ascii") !== "GIF89a" || content.length < 13) {
25598
+ return true;
25599
+ }
25600
+ let offset = 13;
25601
+ if ((content[10] & 128) !== 0) {
25602
+ offset += 3 * 2 ** ((content[10] & 7) + 1);
25603
+ }
25604
+ let frames = 0;
25605
+ while (offset < content.length) {
25606
+ const marker = content[offset++];
25607
+ if (marker === 59) return frames > 1;
25608
+ if (marker === 44) {
25609
+ frames += 1;
25610
+ if (frames > 1) return true;
25611
+ if (offset + 9 > content.length) return true;
25612
+ const packed = content[offset + 8];
25613
+ offset += 9;
25614
+ if ((packed & 128) !== 0) offset += 3 * 2 ** ((packed & 7) + 1);
25615
+ offset += 1;
25616
+ } else if (marker === 33) {
25617
+ offset += 1;
25618
+ } else {
25619
+ return true;
25620
+ }
25621
+ while (offset < content.length) {
25622
+ const size = content[offset++];
25623
+ if (size === 0) break;
25624
+ offset += size;
25625
+ }
25626
+ }
25627
+ return true;
25628
+ }
25629
+ function assertCoverContent(extension, content) {
25630
+ const isJpeg = content.length >= 3 && content[0] === 255 && content[1] === 216 && content[2] === 255;
25631
+ const isPng = content.length >= 8 && content.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
25632
+ const isGif = content.subarray(0, 6).toString("ascii") === "GIF87a" || content.subarray(0, 6).toString("ascii") === "GIF89a";
25633
+ if ((extension === ".jpg" || extension === ".jpeg") && isJpeg) return;
25634
+ if (extension === ".png" && isPng) return;
25635
+ if (extension === ".gif" && isGif && !isAnimatedGif(content)) return;
25636
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F JPG\u3001PNG \u6216\u9759\u6001 GIF \u56FE\u7247", "UPDATE_COVER_INVALID");
25637
+ }
25638
+ async function uploadCover(cwd, cover, apis) {
25639
+ const normalized = normalizeProjectPath(cwd, cover, {
25640
+ code: "UPDATE_COVER_OUTSIDE",
25641
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25642
+ });
25643
+ const localPath = resolveExistingProjectPath(cwd, path11.resolve(cwd, normalized), {
25644
+ code: "UPDATE_COVER_OUTSIDE",
25645
+ message: "\u5C01\u9762\u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u7A0B\u5185"
25646
+ });
25647
+ if (!existsSync10(localPath)) {
25648
+ throw new CliError(`\u5C01\u9762\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${normalized}`, "UPDATE_COVER_MISSING");
25649
+ }
25650
+ const stats = statSync2(localPath);
25651
+ if (!stats.isFile()) {
25652
+ throw new CliError("\u5C01\u9762\u5FC5\u987B\u662F\u4E00\u4E2A\u56FE\u7247\u6587\u4EF6", "UPDATE_COVER_INVALID");
25653
+ }
25654
+ if (stats.size > MAX_COVER_BYTES) {
25655
+ throw new CliError("\u5C01\u9762\u4E0D\u80FD\u8D85\u8FC7 5MB", "UPDATE_COVER_TOO_LARGE");
25656
+ }
25657
+ const extension = path11.extname(normalized).toLowerCase();
25658
+ if (!COVER_EXTENSIONS.has(extension)) {
25659
+ throw new CliError("\u5C01\u9762\u53EA\u652F\u6301 JPG\u3001PNG \u6216\u9759\u6001 GIF", "UPDATE_COVER_INVALID");
25660
+ }
25661
+ const content = readFileSync7(localPath);
25662
+ assertCoverContent(extension, content);
25663
+ const upload = apis?.uploadImage ?? ((params) => service_API_default.Storage.uploadImage(params));
25664
+ const result = unwrapData2(await upload({ file: content }));
25665
+ const url2 = result.url ?? result.fileUrl ?? result.fileURL;
25666
+ if (typeof url2 !== "string" || !url2.trim()) {
25667
+ throw new CliError("\u5C01\u9762\u4E0A\u4F20\u6210\u529F\u4F46\u5E73\u53F0\u672A\u8FD4\u56DE\u56FE\u7247 URL", "UPDATE_COVER_UPLOAD_INVALID");
25668
+ }
25669
+ return url2;
25670
+ }
25279
25671
  function assertIntro(intro) {
25280
25672
  if (intro.length > MAX_INTRO) {
25281
25673
  throw new CliError("\u7B80\u4ECB\u4E0D\u80FD\u8D85\u8FC7200\u4E2A\u5B57\u7B26", "UPDATE_INTRO_LONG");
@@ -25289,9 +25681,6 @@ function assertTags(tags) {
25289
25681
  if (tag.length > MAX_TAG_LENGTH) {
25290
25682
  throw new CliError("\u5355\u4E2A\u6807\u7B7E\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC720\u4E2A\u5B57\u7B26", "UPDATE_TAG_LONG");
25291
25683
  }
25292
- if (tag.includes("#")) {
25293
- throw new CliError(`\u6807\u7B7E\u4E0D\u80FD\u5305\u542B #\uFF1A${tag}`, "UPDATE_TAG_INVALID");
25294
- }
25295
25684
  }
25296
25685
  if (tags.some((tag, i) => tags.indexOf(tag) !== i)) {
25297
25686
  throw new CliError("\u8BE5\u6807\u7B7E\u5DF2\u6DFB\u52A0", "UPDATE_TAG_DUPLICATE");
@@ -25299,34 +25688,47 @@ function assertTags(tags) {
25299
25688
  }
25300
25689
  async function updateListing(input2) {
25301
25690
  assertPlatformAllowed();
25302
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25303
- if (input2.yes && !input2.title && !input2.intro && !input2.cover && !input2.tags) {
25691
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25692
+ if (input2.yes && input2.title === void 0 && input2.intro === void 0 && input2.cover === void 0 && input2.tags === void 0) {
25304
25693
  throw new CliError("--yes \u4E14\u65E0\u4FEE\u6539\u9879\uFF0C\u62D2\u7EDD\u7A7A\u66F4\u65B0", "UPDATE_NO_FLAGS");
25305
25694
  }
25306
25695
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
25696
+ const info2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
25697
+ const remote = unwrapData2(await info2({ resourceIdOrName: identity.resourceId }));
25698
+ assertRemoteResourceWritable({ info: remote, resourceId: identity.resourceId, authUserId: auth.userId });
25307
25699
  const payload = {
25308
25700
  resourceId: identity.resourceId
25309
25701
  };
25310
- if (input2.title) {
25311
- assertTitle(input2.title);
25312
- payload.resourceTitle = input2.title;
25702
+ const title = input2.title?.trim();
25703
+ if (input2.title !== void 0) {
25704
+ assertTitle(title ?? "");
25705
+ payload.resourceTitle = title;
25313
25706
  }
25314
- if (input2.intro) {
25315
- assertIntro(input2.intro);
25316
- payload.intro = input2.intro;
25707
+ if (input2.intro !== void 0) {
25708
+ const intro = input2.intro.trim();
25709
+ assertIntro(intro);
25710
+ payload.intro = intro;
25317
25711
  }
25318
- if (input2.cover) payload.coverImages = [input2.cover];
25319
- if (input2.tags) {
25320
- const tags = input2.tags.split(",").map((item) => item.trim().replaceAll("#", "")).filter(Boolean);
25321
- assertTags(tags);
25322
- payload.tags = tags;
25712
+ if (input2.cover !== void 0) {
25713
+ if (!input2.cover.trim()) {
25714
+ throw new CliError("\u8BF7\u63D0\u4F9B\u5C01\u9762\u56FE\u7247\u8DEF\u5F84\uFF1B\u672C\u671F\u4E0D\u652F\u6301\u6E05\u7A7A\u5C01\u9762", "UPDATE_COVER_REQUIRED");
25715
+ }
25716
+ payload.coverImages = [await uploadCover(input2.cwd, input2.cover, input2.apis)];
25717
+ }
25718
+ if (input2.tags !== void 0) {
25719
+ const rawTags = input2.tags === "" ? [] : input2.tags.split(",").map((item) => item.trim().replaceAll("#", ""));
25720
+ if (rawTags.some((tag) => !tag)) {
25721
+ throw new CliError("\u6807\u7B7E\u4E0D\u80FD\u5305\u542B\u7A7A\u9879", "UPDATE_TAG_EMPTY");
25722
+ }
25723
+ assertTags(rawTags);
25724
+ payload.tags = rawTags;
25323
25725
  }
25324
25726
  if ("status" in payload) {
25325
25727
  delete payload.status;
25326
25728
  }
25327
25729
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
25328
25730
  await update2(payload);
25329
- if (input2.title) updateIdentity(input2.cwd, identity.n, { title: input2.title });
25731
+ if (title !== void 0) updateIdentity(input2.cwd, identity.n, { title });
25330
25732
  return payload;
25331
25733
  }
25332
25734
 
@@ -25352,27 +25754,13 @@ function createUpdateCommand() {
25352
25754
  }
25353
25755
 
25354
25756
  // src/commands/policy/apply.ts
25355
- import { readFileSync as readFileSync6 } from "fs";
25356
- import path9 from "path";
25757
+ import { readFileSync as readFileSync8 } from "fs";
25758
+ import path12 from "path";
25357
25759
  import { confirm, input } from "@inquirer/prompts";
25358
25760
  import { Command as Command6 } from "commander";
25359
25761
 
25360
- // src/platform/unwrap.ts
25361
- function unwrapData2(result) {
25362
- const envelope = result;
25363
- const data = envelope.data ?? result;
25364
- return data ?? {};
25365
- }
25366
- function unwrapFirst(result) {
25367
- const envelope = result;
25368
- const data = envelope.data ?? result;
25369
- if (Array.isArray(data)) {
25370
- return data[0] ?? {};
25371
- }
25372
- return data ?? {};
25373
- }
25374
-
25375
25762
  // src/domain/policy/list.ts
25763
+ var POLICY_LIST_PAGE_SIZE = 50;
25376
25764
  function infoApi(apis) {
25377
25765
  return apis?.info ?? ((params) => service_API_default.Resource.info(params));
25378
25766
  }
@@ -25401,14 +25789,17 @@ function decoded(value) {
25401
25789
  return value;
25402
25790
  }
25403
25791
  }
25404
- function assertEditable(info2, authUserId) {
25405
- if (info2.status === 2 || info2.isFrozen === true) {
25406
- throw new CliError("\u8D44\u6E90\u5DF2\u88AB\u51BB\u7ED3\uFF0C\u4E0D\u80FD\u4FEE\u6539\u6388\u6743\u7B56\u7565", "POLICY_RESOURCE_FROZEN");
25407
- }
25408
- const ownerId = info2.userId ?? info2.ownerId ?? info2.creatorId;
25409
- if (typeof ownerId === "number" && ownerId !== authUserId) {
25410
- throw new CliError("\u53EA\u80FD\u4FEE\u6539\u81EA\u5DF1\u7684\u8D44\u6E90\u6388\u6743\u7B56\u7565", "POLICY_NOT_OWNER");
25411
- }
25792
+ function assertEditable(info2, authUserId, resourceId) {
25793
+ assertRemoteResourceWritable({
25794
+ info: info2,
25795
+ resourceId,
25796
+ authUserId,
25797
+ codes: {
25798
+ invalid: "POLICY_INFO_INVALID",
25799
+ notOwner: "POLICY_NOT_OWNER",
25800
+ frozen: "POLICY_RESOURCE_FROZEN"
25801
+ }
25802
+ });
25412
25803
  }
25413
25804
  async function loadPolicyContext(input2) {
25414
25805
  assertPlatformAllowed();
@@ -25427,7 +25818,7 @@ async function loadPolicyContext(input2) {
25427
25818
  isLoadPolicyInfo: 1,
25428
25819
  isTranslate: 1
25429
25820
  }));
25430
- if (input2.editable) assertEditable(info2, auth.userId);
25821
+ if (input2.editable) assertEditable(info2, auth.userId, resourceId);
25431
25822
  return {
25432
25823
  resourceId,
25433
25824
  typeCode,
@@ -25438,30 +25829,56 @@ async function loadPolicyContext(input2) {
25438
25829
  function templateFrom(value) {
25439
25830
  if (!value || typeof value !== "object") return void 0;
25440
25831
  const raw = value;
25441
- const id = raw.id ?? raw.templateId ?? raw.policyTemplateId;
25442
- const name = raw.name ?? raw.templateName ?? raw.policyName;
25443
- const defaultValue = raw.defaultValue ?? raw.policyText ?? raw.value;
25832
+ const id = raw.id ?? raw.templateId ?? raw.policyTemplateId ?? raw._id;
25833
+ const name = raw.name ?? raw.templateName ?? raw.policyName ?? raw.title;
25834
+ const defaultValue = raw.defaultValue ?? raw.policyText ?? raw.value ?? raw.template;
25444
25835
  if (typeof id !== "string" || !id || typeof name !== "string" || !name || typeof defaultValue !== "string" || !defaultValue.trim()) {
25445
25836
  return void 0;
25446
25837
  }
25447
25838
  const summaryValue = raw.summary ?? raw.description ?? raw.eventSummary;
25839
+ const fillArgs = Array.isArray(raw.reportUiTemplate) ? raw.reportUiTemplate.flatMap((item) => {
25840
+ if (!item || typeof item !== "object") return [];
25841
+ const field = item;
25842
+ const name2 = field.id;
25843
+ const value2 = field.uiSectionDefaultValue;
25844
+ return typeof name2 === "string" && name2 && (typeof value2 === "string" || typeof value2 === "number") ? [{ name: name2, value: value2 }] : [];
25845
+ }) : [];
25448
25846
  return {
25449
25847
  id,
25450
25848
  name,
25451
25849
  defaultValue,
25850
+ fillArgs,
25452
25851
  ...typeof summaryValue === "string" && summaryValue.trim() ? { summary: summaryValue.trim() } : {}
25453
25852
  };
25454
25853
  }
25854
+ function normalizeCompiledTemplateDsl(contract2) {
25855
+ return contract2.replace(/\bfor\s+public\b/gi, "FOR PUBLIC").replace(/\binitial\b/gi, "Initial");
25856
+ }
25857
+ function compiledContract(result) {
25858
+ const envelope = result;
25859
+ const data = envelope.data ?? result;
25860
+ const contract2 = data && typeof data === "object" ? data.contractNew ?? data.contract : data;
25861
+ if (typeof contract2 !== "string" || !contract2.trim()) {
25862
+ throw new CliError("\u7B56\u7565\u6A21\u677F\u7F16\u8BD1\u7ED3\u679C\u7F3A\u5C11 contractNew", "POLICY_TEMPLATE_COMPILE_INVALID");
25863
+ }
25864
+ return normalizeCompiledTemplateDsl(contract2).trim();
25865
+ }
25455
25866
  async function getPolicyTemplates(input2) {
25456
- const context = await loadPolicyContext(input2);
25867
+ await loadPolicyContext(input2);
25457
25868
  const request2 = input2.apis?.policyTemplates ?? ((params) => service_API_default.Policy.policyTemplates(params));
25458
- const result = unwrapData2(await request2({ resourceTypeCodes4Resource: [context.typeCode] }));
25459
- const rawList = Array.isArray(result.list) ? result.list : Array.isArray(result.dataList) ? result.dataList : Array.isArray(result.templates) ? result.templates : [];
25869
+ const rawList = unwrapList2(await request2({}), ["list", "dataList", "templates"]);
25460
25870
  return rawList.flatMap((item) => {
25461
25871
  const template = templateFrom(item);
25462
25872
  return template ? [template] : [];
25463
25873
  });
25464
25874
  }
25875
+ async function applyPolicyTemplate(input2) {
25876
+ const template = (await getPolicyTemplates(input2)).find((item) => item.id === input2.templateId);
25877
+ if (!template) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25878
+ const request2 = input2.apis?.policyReCompile ?? ((params) => service_API_default.Policy.policyReCompile(params));
25879
+ const policyText = compiledContract(await request2({ _id: template.id, fillArgs: template.fillArgs }));
25880
+ await applyPolicy({ ...input2, policyText });
25881
+ }
25465
25882
  function positiveInteger(value, fallback, name, max) {
25466
25883
  const actual = value ?? fallback;
25467
25884
  if (!Number.isInteger(actual) || actual < 1 || actual > max) {
@@ -25469,10 +25886,39 @@ function positiveInteger(value, fallback, name, max) {
25469
25886
  }
25470
25887
  return actual;
25471
25888
  }
25472
- async function listPolicies(input2) {
25889
+ async function getPolicyList(input2) {
25473
25890
  const context = await loadPolicyContext(input2);
25474
- if (!context.policies.length) return "\u8FD8\u6CA1\u6709\u6388\u6743\u7B56\u7565";
25475
- return [...context.policies].sort((a, b) => b.status - a.status || a.policyName.localeCompare(b.policyName)).map((item) => `${item.policyId} ${item.policyName} ${item.status === 1 ? "on" : "off"}`).join("\n");
25891
+ const typeHierarchy = await getTypeHierarchy(context.typeCode, input2.apis);
25892
+ return {
25893
+ typeHierarchy,
25894
+ policies: [...context.policies].sort((a, b) => b.status - a.status || a.policyName.localeCompare(b.policyName))
25895
+ };
25896
+ }
25897
+ function policyListPage(list2, page) {
25898
+ const pageCount = Math.max(1, Math.ceil(list2.policies.length / POLICY_LIST_PAGE_SIZE));
25899
+ if (!Number.isInteger(page) || page < 1 || page > pageCount) {
25900
+ throw new CliError(`\u7B56\u7565\u9875\u7801\u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5171 ${pageCount} \u9875`, "POLICY_LIST_PAGE");
25901
+ }
25902
+ return {
25903
+ ...list2,
25904
+ page,
25905
+ pageCount,
25906
+ total: list2.policies.length,
25907
+ hasPrevious: page > 1,
25908
+ hasNext: page < pageCount,
25909
+ items: list2.policies.slice((page - 1) * POLICY_LIST_PAGE_SIZE, page * POLICY_LIST_PAGE_SIZE)
25910
+ };
25911
+ }
25912
+ function formatPolicyListPage(page) {
25913
+ const header = [
25914
+ `\u8D44\u6E90\u7C7B\u578B\uFF1A${page.typeHierarchy.join(" / ")}`,
25915
+ `\u7B2C ${page.page}/${page.pageCount} \u9875\uFF0C\u5171 ${page.total} \u6761`
25916
+ ];
25917
+ if (page.items.length === 0) return [...header, "\u8FD8\u6CA1\u6709\u6388\u6743\u7B56\u7565"].join("\n");
25918
+ return [
25919
+ ...header,
25920
+ ...page.items.map((item) => `${item.policyId} ${item.policyName} ${item.status === 1 ? "on" : "off"}`)
25921
+ ].join("\n");
25476
25922
  }
25477
25923
  async function listPolicyTemplates(input2) {
25478
25924
  const page = positiveInteger(input2.page, 1, "--page", Number.MAX_SAFE_INTEGER);
@@ -25531,8 +25977,8 @@ async function setPolicy(input2) {
25531
25977
 
25532
25978
  // src/commands/policy/apply.ts
25533
25979
  function parsePolicy(file) {
25534
- const text = readFileSync6(file, "utf8");
25535
- if (path9.extname(file).toLowerCase() !== ".json") return { policyText: text };
25980
+ const text = readFileSync8(file, "utf8");
25981
+ if (path12.extname(file).toLowerCase() !== ".json") return { policyText: text };
25536
25982
  let parsed;
25537
25983
  try {
25538
25984
  parsed = JSON.parse(text);
@@ -25567,17 +26013,39 @@ function createPolicyApplyCommand() {
25567
26013
 
25568
26014
  // src/commands/policy/list.ts
25569
26015
  import { Command as Command7 } from "commander";
26016
+ async function showPolicyPages(pageFor) {
26017
+ let page = 1;
26018
+ while (true) {
26019
+ const current = pageFor(page);
26020
+ console.log(formatPolicyListPage(current));
26021
+ if (!current.hasPrevious && !current.hasNext) return;
26022
+ if (!isInteractive()) {
26023
+ if (current.hasNext) {
26024
+ console.log(`\u8FD8\u6709 ${current.total - current.page * POLICY_LIST_PAGE_SIZE} \u6761\u7B56\u7565\uFF1B\u8BF7\u5728\u4EA4\u4E92\u7EC8\u7AEF\u8FD0\u884C policy list \u67E5\u770B\u540E\u7EED\u9875`);
26025
+ }
26026
+ return;
26027
+ }
26028
+ const action = await selectQuestion("\u6388\u6743\u7B56\u7565\u5217\u8868", [
26029
+ ...current.hasPrevious ? [{ name: "\u4E0A\u4E00\u9875", value: "__previous__" }] : [],
26030
+ ...current.hasNext ? [{ name: "\u4E0B\u4E00\u9875", value: "__next__" }] : [],
26031
+ { name: "\u9000\u51FA", value: "__exit__" }
26032
+ ]);
26033
+ if (action === "__exit__") return;
26034
+ page += action === "__next__" ? 1 : -1;
26035
+ }
26036
+ }
25570
26037
  function createPolicyListCommand() {
25571
26038
  const command = addSharedOptions(new Command7("list"));
25572
26039
  command.description(
25573
26040
  // i18n: cli.command.policy.list.description
25574
- "\u770B\u5DF2\u6709\u7B56\u7565"
26041
+ "\u770B\u5DF2\u6709\u7B56\u7565\u53CA\u8D44\u6E90\u7C7B\u578B\u5C42\u7EA7"
25575
26042
  ).action(async function() {
25576
26043
  const shared = readSharedOptions(this);
25577
- console.log(await listPolicies({
26044
+ const list2 = await getPolicyList({
25578
26045
  cwd: resolveCwd(shared.cwd),
25579
26046
  file: shared.file
25580
- }));
26047
+ });
26048
+ await showPolicyPages((page) => policyListPage(list2, page));
25581
26049
  });
25582
26050
  return command;
25583
26051
  }
@@ -25629,7 +26097,7 @@ async function chooseTemplate(templates) {
25629
26097
  }
25630
26098
  function createPolicyTemplateCommand() {
25631
26099
  const template = addSharedOptions(new Command9("template")).description("\u6388\u6743\u7B56\u7565\u6A21\u677F");
25632
- addSharedOptions(template.command("list")).description("\u6309\u5F53\u524D\u8D44\u6E90\u7C7B\u578B\u5217\u5168\u90E8\u6A21\u677F").option("--page <n>", "\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB", Number).option("--page-size <n>", "\u6BCF\u9875 1\u2013100 \u6761\uFF0C\u9ED8\u8BA4 20", Number).action(async function(options) {
26100
+ addSharedOptions(template.command("list")).description("\u5217\u5F53\u524D\u6A21\u677F\u5217\u8868").option("--page <n>", "\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB", Number).option("--page-size <n>", "\u6BCF\u9875 1\u2013100 \u6761\uFF0C\u9ED8\u8BA4 20", Number).action(async function(options) {
25633
26101
  const shared = readSharedOptions(this);
25634
26102
  console.log(await listPolicyTemplates({ cwd: resolveCwd(shared.cwd), file: shared.file, page: options.page, pageSize: options.pageSize }));
25635
26103
  });
@@ -25639,7 +26107,7 @@ function createPolicyTemplateCommand() {
25639
26107
  const yes = shared.yes === true;
25640
26108
  const templates = await getPolicyTemplates({ cwd, file: shared.file });
25641
26109
  let item = templateId ? templates.find((candidate) => candidate.id === templateId) : void 0;
25642
- if (!item && templateId) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5C5E\u4E8E\u5F53\u524D\u8D44\u6E90\u7C7B\u578B", "POLICY_TEMPLATE_INVALID");
26110
+ if (!item && templateId) throw new CliError("\u6307\u5B9A\u6A21\u677F\u4E0D\u5728\u5F53\u524D\u6A21\u677F\u5217\u8868\u4E2D", "POLICY_TEMPLATE_INVALID");
25643
26111
  if (!item) {
25644
26112
  if (yes || !process.stdin.isTTY) throw new CliError("--yes \u6216\u975E\u4EA4\u4E92\u6A21\u5F0F\u5FC5\u987B\u63D0\u4F9B templateId", "POLICY_TEMPLATE_ID");
25645
26113
  item = await chooseTemplate(templates);
@@ -25647,7 +26115,7 @@ function createPolicyTemplateCommand() {
25647
26115
  }
25648
26116
  const policyName = options.name ?? item.name;
25649
26117
  if (!yes && !await confirm3({ message: `\u6DFB\u52A0\u5E76\u542F\u7528\u7B56\u7565\u300C${policyName}\u300D\uFF1F`, default: true })) return;
25650
- await applyPolicy({ cwd, file: shared.file, policyName, policyText: item.defaultValue });
26118
+ await applyPolicyTemplate({ cwd, file: shared.file, templateId: item.id, policyName });
25651
26119
  console.log("\u5DF2\u6DFB\u52A0\u5E76\u542F\u7528\u6388\u6743\u7B56\u7565");
25652
26120
  });
25653
26121
  return template;
@@ -25656,22 +26124,22 @@ function createPolicyTemplateCommand() {
25656
26124
  // src/commands/project/bind.ts
25657
26125
  import { Command as Command10 } from "commander";
25658
26126
 
25659
- // src/local/indexFile.ts
25660
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
25661
- import path10 from "path";
25662
- function normalizeFileKey(filePath) {
25663
- return filePath.replaceAll("\\", "/").replace(/^\.\//, "");
25664
- }
25665
-
25666
26127
  // src/domain/bind/bind.ts
26128
+ import { existsSync as existsSync11 } from "fs";
26129
+ import path13 from "path";
25667
26130
  async function bindResource(input2) {
25668
26131
  assertPlatformAllowed();
25669
26132
  const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
25670
- validateLocalState(input2.cwd);
25671
26133
  const filePath = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file, {
25672
26134
  code: "BIND_FILE_OUTSIDE",
25673
26135
  message: "--artifact \u5FC5\u987B\u843D\u5728\u5F53\u524D\u5DE5\u7A0B\u91CC"
25674
26136
  }) : void 0;
26137
+ if (filePath) {
26138
+ const absolute = path13.resolve(input2.cwd, filePath);
26139
+ if (!existsSync11(absolute)) {
26140
+ throw new CliError(`\u672C\u5730\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${absolute}`, "ARTIFACT_ANCHOR_MISSING");
26141
+ }
26142
+ }
25675
26143
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
25676
26144
  const info2 = unwrapData2(
25677
26145
  await infoApi2({
@@ -25685,23 +26153,34 @@ async function bindResource(input2) {
25685
26153
  if (!isResourceSubject(info2.subjectType)) {
25686
26154
  throw new CliError("\u53EA\u80FD bind \u5355\u8D44\u6E90", "BIND_SUBJECT_INVALID");
25687
26155
  }
25688
- if (info2.userId !== auth.userId) {
25689
- throw new CliError("\u53EA\u80FD bind \u81EA\u5DF1\u7684\u8D44\u6E90", "BIND_NOT_OWNER");
25690
- }
25691
26156
  const resourceId = String(info2.resourceId ?? "");
25692
- const name = String(info2.resourceName ?? info2.name ?? "").split("/").pop() ?? "";
26157
+ if (!resourceId) {
26158
+ throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
26159
+ }
26160
+ assertRemoteResourceOwned({
26161
+ info: info2,
26162
+ resourceId,
26163
+ authUserId: auth.userId,
26164
+ codes: {
26165
+ invalid: "BIND_INFO_INVALID",
26166
+ notOwner: "BIND_NOT_OWNER"
26167
+ }
26168
+ });
26169
+ const resourceName = String(info2.resourceName ?? "");
26170
+ const name = (resourceName || String(info2.name ?? "")).split("/").pop() ?? "";
25693
26171
  const title = String(info2.resourceTitle ?? info2.title ?? name);
25694
26172
  const resourceType = info2.resourceType;
25695
26173
  const typeFromArray = Array.isArray(resourceType) ? String(resourceType[0] ?? "") : "";
25696
26174
  const typeCode = String(info2.resourceTypeCode ?? typeFromArray);
25697
- if (!resourceId || !name || !typeCode || !title) {
26175
+ if (!name || !typeCode || !title) {
25698
26176
  throw new CliError("\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u8EAB\u4EFD\u5B57\u6BB5", "BIND_INFO_INVALID");
25699
26177
  }
26178
+ const stableResourceName = resourceName || `${auth.loginName}/${name}`;
25700
26179
  return withProjectLock(input2.cwd, () => {
25701
26180
  const identities = validateLocalState(input2.cwd);
25702
26181
  const byId = identities.find((item) => item.resourceId === resourceId);
25703
26182
  const byFile = filePath ? identities.find(
25704
- (item) => item.filePath !== void 0 && normalizeFileKey(item.filePath) === filePath
26183
+ (item) => normalizeProjectPath(input2.cwd, item.filePath) === filePath
25705
26184
  ) : void 0;
25706
26185
  const unbound = identities.filter((item) => !item.resourceId);
25707
26186
  if (byFile && byId && byFile.n !== byId.n) {
@@ -25711,18 +26190,29 @@ async function bindResource(input2) {
25711
26190
  if (input2.selector && byId && target && target.n !== byId.n) {
25712
26191
  throw new CliError(`\u8D44\u6E90\u5DF2\u7ED1\u5B9A\u5230 ${byId.n}.json`, "BIND_ID_TAKEN");
25713
26192
  }
25714
- if (!target && unbound.length === 1) {
26193
+ if (!target && filePath) {
26194
+ target = unbound.find((item) => item.filePath === filePath);
26195
+ }
26196
+ if (!target && filePath === void 0 && unbound.length === 1) {
25715
26197
  target = unbound[0];
25716
26198
  }
25717
26199
  if (!target && unbound.length > 1) throw new CliError("\u5F53\u524D\u5DE5\u7A0B\u6709\u591A\u4EFD\u672A\u7ED1\u5B9A\u8D44\u6E90\u72B6\u6001\uFF1B\u8BF7\u4F7F\u7528 --resource \u6307\u5B9A\u8D44\u6E90", "IDENTITY_RESOURCE_REQUIRED");
25718
26200
  if (byFile && target?.n !== byFile.n) {
25719
26201
  throw new CliError("\u8DEF\u5F84\u5DF2\u88AB\u5360\u7528", "BIND_PATH_TAKEN");
25720
26202
  }
25721
- if (isThemeOrWidget(typeCode) && !filePath && !target?.filePath) {
25722
- throw new CliError("\u4E3B\u9898/\u63D2\u4EF6 bind \u65F6\u8BF7\u901A\u8FC7 --artifact \u6307\u5B9A\u6784\u5EFA\u76EE\u5F55", "BIND_FIXED_TYPE_FILE_REQUIRED");
26203
+ const resolvedFilePath = filePath ?? target?.filePath;
26204
+ if (!resolvedFilePath) {
26205
+ throw new CliError("\u65B0\u589E\u672C\u5730\u72B6\u6001\u65F6\u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "BIND_ARTIFACT_REQUIRED");
25723
26206
  }
26207
+ assertArtifactAnchor(typeCode, path13.resolve(input2.cwd, resolvedFilePath), input2.cwd);
25724
26208
  const env = getEnv2();
25725
26209
  if (target) {
26210
+ if (target.resourceId && (target.env ?? "prod") !== env) {
26211
+ throw new CliError(
26212
+ `\u4E0D\u80FD\u628A ${target.n}.json\uFF08${target.env ?? "prod"}\uFF09\u6539\u7ED1\u5230 ${env} \u73AF\u5883\uFF1B\u8BF7\u65B0\u5EFA\u6216\u9009\u62E9\u8BE5\u73AF\u5883\u7684\u672C\u5730\u8D44\u6E90\u72B6\u6001`,
26213
+ "BIND_ENV_MISMATCH"
26214
+ );
26215
+ }
25726
26216
  const resourceChanged = target.resourceId !== resourceId;
25727
26217
  const typeChanged = target.typeCode !== typeCode;
25728
26218
  if (target.resourceId && resourceChanged) {
@@ -25733,10 +26223,11 @@ async function bindResource(input2) {
25733
26223
  const discardDraft = target.resourceId && resourceChanged || typeChanged;
25734
26224
  const updated = prepareIdentityUpdate(input2.cwd, target.n, {
25735
26225
  resourceId,
26226
+ resourceName: stableResourceName,
25736
26227
  name,
25737
26228
  title,
25738
26229
  typeCode,
25739
- filePath: filePath ?? target.filePath,
26230
+ filePath: resolvedFilePath,
25740
26231
  env
25741
26232
  });
25742
26233
  commitLocalTransaction(input2.cwd, [
@@ -25748,14 +26239,16 @@ async function bindResource(input2) {
25748
26239
  const created = prepareIdentityCreate(input2.cwd, {
25749
26240
  subject: "resource",
25750
26241
  resourceId,
26242
+ resourceName: stableResourceName,
25751
26243
  name,
25752
26244
  title,
25753
26245
  typeCode,
25754
- filePath,
26246
+ filePath: resolvedFilePath,
25755
26247
  env
25756
26248
  });
25757
26249
  commitLocalTransaction(input2.cwd, [
25758
- { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) }
26250
+ { path: identityFilePath(input2.cwd, created.n), content: serializeIdentity(created) },
26251
+ { path: identitySequenceFilePath(input2.cwd), content: serializeIdentitySequence(created.n) }
25759
26252
  ]);
25760
26253
  return created;
25761
26254
  });
@@ -25768,9 +26261,6 @@ function isCollectionSubject(subjectType) {
25768
26261
  const values = Array.isArray(subjectType) ? subjectType : [subjectType];
25769
26262
  return values.some((value) => Number(value) === 4);
25770
26263
  }
25771
- function isThemeOrWidget(typeCode) {
25772
- return typeCode === "RT001" || typeCode === "RT002";
25773
- }
25774
26264
 
25775
26265
  // src/commands/project/bind.ts
25776
26266
  function createBindCommand() {
@@ -25806,9 +26296,9 @@ import { Command as Command11 } from "commander";
25806
26296
 
25807
26297
  // src/domain/init/scaffold.ts
25808
26298
  import { execFile as execFileCallback } from "child_process";
25809
- import { cpSync, existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync2, mkdtempSync, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync, rmSync } from "fs";
26299
+ import { cpSync, existsSync as existsSync12, lstatSync as lstatSync2, mkdirSync as mkdirSync2, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync9, renameSync, rmSync } from "fs";
25810
26300
  import os4 from "os";
25811
- import path11 from "path";
26301
+ import path14 from "path";
25812
26302
  import { promisify } from "util";
25813
26303
 
25814
26304
  // src/domain/init/templates.ts
@@ -25834,44 +26324,49 @@ function formatTemplateList(items) {
25834
26324
 
25835
26325
  // src/domain/init/scaffold.ts
25836
26326
  var execFile = promisify(execFileCallback);
25837
- function isFixedTemplateType2(typeCode) {
25838
- return typeCode === "RT001" || typeCode === "RT002";
25839
- }
25840
26327
  function resolveTargetDir(input2) {
25841
- return input2.dir ? path11.isAbsolute(input2.dir) ? input2.dir : path11.resolve(input2.cwd, input2.dir) : path11.resolve(input2.cwd);
26328
+ return input2.dir ? path14.isAbsolute(input2.dir) ? input2.dir : path14.resolve(input2.cwd, input2.dir) : path14.resolve(input2.cwd);
25842
26329
  }
25843
26330
  function targetHasOnlyAuthSelector(targetDir) {
25844
- if (!existsSync9(targetDir)) return false;
26331
+ if (!existsSync12(targetDir)) return false;
25845
26332
  let entries;
25846
26333
  try {
25847
- entries = readdirSync3(targetDir);
26334
+ entries = readdirSync4(targetDir);
25848
26335
  } catch {
25849
26336
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u4E0D\u53EF\u8BFB\u53D6\uFF0C\u62D2\u7EDD\u8986\u76D6", "INIT_TARGET_NOT_EMPTY");
25850
26337
  }
25851
26338
  if (entries.length === 0) return false;
25852
26339
  if (entries.length !== 1 || entries[0] !== ".freelog") return false;
25853
- const stateDir = path11.join(targetDir, ".freelog");
25854
- if (!lstatSync(stateDir).isDirectory() || lstatSync(stateDir).isSymbolicLink()) return false;
25855
- const stateEntries = readdirSync3(stateDir);
26340
+ const stateDir = path14.join(targetDir, ".freelog");
26341
+ return stateDirHasOnlyAuthSelector(stateDir);
26342
+ }
26343
+ function stateDirHasOnlyAuthSelector(stateDir) {
26344
+ if (!existsSync12(stateDir) || !lstatSync2(stateDir).isDirectory() || lstatSync2(stateDir).isSymbolicLink()) return false;
26345
+ const stateEntries = readdirSync4(stateDir);
25856
26346
  if (stateEntries.length !== 1 || stateEntries[0] !== "auth") return false;
25857
- const auth = lstatSync(path11.join(stateDir, "auth"));
26347
+ const auth = lstatSync2(path14.join(stateDir, "auth"));
25858
26348
  return auth.isFile() && !auth.isSymbolicLink();
25859
26349
  }
25860
- function assertInitialTarget(targetDir) {
25861
- if (!existsSync9(targetDir)) return { targetExisted: false, preserveAuth: false };
25862
- const entries = readdirSync3(targetDir);
25863
- if (entries.length === 0) return { targetExisted: true, preserveAuth: false };
25864
- if (targetHasOnlyAuthSelector(targetDir)) return { targetExisted: true, preserveAuth: true };
26350
+ function assertInitialTarget(targetDir, adoptExistingProject) {
26351
+ if (!existsSync12(targetDir)) return { targetExisted: false, preserveAuth: false, adoptExistingProject };
26352
+ const entries = readdirSync4(targetDir);
26353
+ if (entries.length === 0) return { targetExisted: true, preserveAuth: false, adoptExistingProject };
26354
+ if (targetHasOnlyAuthSelector(targetDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject };
26355
+ if (adoptExistingProject) {
26356
+ const stateDir = path14.join(targetDir, ".freelog");
26357
+ if (!existsSync12(stateDir)) return { targetExisted: true, preserveAuth: false, adoptExistingProject: true };
26358
+ if (stateDirHasOnlyAuthSelector(stateDir)) return { targetExisted: true, preserveAuth: true, adoptExistingProject: true };
26359
+ }
25865
26360
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u4E0D\u662F\u7A7A\u76EE\u5F55\uFF0C\u62D2\u7EDD\u8986\u76D6", "INIT_TARGET_NOT_EMPTY");
25866
26361
  }
25867
26362
  function assertOnlyAuthSelectorStillThere(targetDir) {
25868
- if (!targetHasOnlyAuthSelector(targetDir)) {
26363
+ if (!stateDirHasOnlyAuthSelector(path14.join(targetDir, ".freelog"))) {
25869
26364
  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");
25870
26365
  }
25871
26366
  }
25872
26367
  function copyDirectoryContents(sourceDir, targetDir) {
25873
- for (const entry2 of readdirSync3(sourceDir)) {
25874
- cpSync(path11.join(sourceDir, entry2), path11.join(targetDir, entry2), { recursive: true, errorOnExist: true, force: false });
26368
+ for (const entry2 of readdirSync4(sourceDir)) {
26369
+ cpSync(path14.join(sourceDir, entry2), path14.join(targetDir, entry2), { recursive: true, errorOnExist: true, force: false });
25875
26370
  }
25876
26371
  }
25877
26372
  function parsePackFilename(stdout) {
@@ -25883,24 +26378,24 @@ function parsePackFilename(stdout) {
25883
26378
  throw new CliError("\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25\uFF1Anpm \u672A\u8FD4\u56DE\u5B89\u88C5\u5305", "TEMPLATE_DOWNLOAD_FAILED");
25884
26379
  }
25885
26380
  function validateRemoteTemplate(packageDir, template) {
25886
- const manifestPath = path11.join(packageDir, "template.manifest.json");
25887
- const templateDir = path11.join(packageDir, "template");
26381
+ const manifestPath = path14.join(packageDir, "template.manifest.json");
26382
+ const templateDir = path14.join(packageDir, "template");
25888
26383
  let manifest;
25889
26384
  try {
25890
- manifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
26385
+ manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
25891
26386
  } catch {
25892
26387
  throw new CliError("\u6A21\u677F\u6821\u9A8C\u5931\u8D25\uFF1A\u7F3A\u5C11\u6216\u65E0\u6CD5\u8BFB\u53D6 template.manifest.json", "TEMPLATE_INVALID");
25893
26388
  }
25894
26389
  const tags = Array.isArray(manifest.tags) ? manifest.tags : [];
25895
- if (manifest.id !== template.id || manifest.npmName !== template.npmName || manifest.version !== template.version || !tags.includes("runtime") || !template.targets.every((target) => tags.includes(target)) || !existsSync9(templateDir)) {
26390
+ if (manifest.id !== template.id || manifest.npmName !== template.npmName || manifest.version !== template.version || !tags.includes("runtime") || !template.targets.every((target) => tags.includes(target)) || !existsSync12(templateDir)) {
25896
26391
  throw new CliError("\u6A21\u677F\u6821\u9A8C\u5931\u8D25\uFF1A\u7EBF\u4E0A\u6A21\u677F\u4E0E\u53D7\u63A7\u6E05\u5355\u4E0D\u4E00\u81F4", "TEMPLATE_INVALID");
25897
26392
  }
25898
26393
  return templateDir;
25899
26394
  }
25900
26395
  async function installRemoteTemplate(input2) {
25901
- const tempDir = mkdtempSync(path11.join(os4.tmpdir(), "freelog-template-"));
25902
- const downloadDir = path11.join(tempDir, "download");
25903
- const extractDir = path11.join(tempDir, "extract");
26396
+ const tempDir = mkdtempSync(path14.join(os4.tmpdir(), "freelog-template-"));
26397
+ const downloadDir = path14.join(tempDir, "download");
26398
+ const extractDir = path14.join(tempDir, "extract");
25904
26399
  mkdirSync2(downloadDir);
25905
26400
  mkdirSync2(extractDir);
25906
26401
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
@@ -25911,78 +26406,86 @@ async function installRemoteTemplate(input2) {
25911
26406
  } catch {
25912
26407
  throw new CliError(`\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25\uFF1A${input2.template.id}`, "TEMPLATE_DOWNLOAD_FAILED");
25913
26408
  }
25914
- const archivePath = path11.join(downloadDir, parsePackFilename(stdout));
26409
+ const archivePath = path14.join(downloadDir, parsePackFilename(stdout));
25915
26410
  try {
25916
26411
  await execFile("tar", ["-xzf", archivePath, "-C", extractDir]);
25917
26412
  } catch {
25918
26413
  throw new CliError(`\u6A21\u677F\u89E3\u5305\u5931\u8D25\uFF1A${input2.template.id}`, "TEMPLATE_EXTRACT_FAILED");
25919
26414
  }
25920
- copyDirectoryContents(validateRemoteTemplate(path11.join(extractDir, "package"), input2.template), input2.targetDir);
26415
+ copyDirectoryContents(validateRemoteTemplate(path14.join(extractDir, "package"), input2.template), input2.targetDir);
25921
26416
  } finally {
25922
26417
  rmSync(tempDir, { recursive: true, force: true });
25923
26418
  }
25924
26419
  }
25925
- function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth) {
26420
+ function commitStaging(stagingDir, targetDir, targetExisted, preserveAuth, adoptExistingProject) {
25926
26421
  if (!targetExisted) {
25927
26422
  renameSync(stagingDir, targetDir);
25928
26423
  return;
25929
26424
  }
25930
26425
  if (preserveAuth) {
25931
26426
  assertOnlyAuthSelectorStillThere(targetDir);
26427
+ } else if (adoptExistingProject) {
26428
+ if (existsSync12(path14.join(targetDir, ".freelog"))) {
26429
+ throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
26430
+ }
26431
+ renameSync(path14.join(stagingDir, ".freelog"), path14.join(targetDir, ".freelog"));
26432
+ rmSync(stagingDir, { recursive: true, force: true });
26433
+ return;
25932
26434
  } else {
25933
- assertInitialTarget(targetDir);
26435
+ assertInitialTarget(targetDir, false);
25934
26436
  }
25935
- for (const entry2 of readdirSync3(stagingDir)) {
25936
- const destination = path11.join(targetDir, entry2);
26437
+ for (const entry2 of readdirSync4(stagingDir)) {
26438
+ const destination = path14.join(targetDir, entry2);
25937
26439
  if (preserveAuth && entry2 === ".freelog") {
25938
- for (const stateEntry of readdirSync3(path11.join(stagingDir, entry2))) {
25939
- const stateDestination = path11.join(destination, stateEntry);
25940
- if (existsSync9(stateDestination)) {
26440
+ for (const stateEntry of readdirSync4(path14.join(stagingDir, entry2))) {
26441
+ const stateDestination = path14.join(destination, stateEntry);
26442
+ if (existsSync12(stateDestination)) {
25941
26443
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
25942
26444
  }
25943
- renameSync(path11.join(stagingDir, entry2, stateEntry), stateDestination);
26445
+ renameSync(path14.join(stagingDir, entry2, stateEntry), stateDestination);
25944
26446
  }
25945
- rmSync(path11.join(stagingDir, entry2), { recursive: true, force: true });
26447
+ rmSync(path14.join(stagingDir, entry2), { recursive: true, force: true });
25946
26448
  continue;
25947
26449
  }
25948
- if (existsSync9(destination)) {
26450
+ if (existsSync12(destination)) {
25949
26451
  throw new CliError("\u76EE\u6807\u76EE\u5F55\u5728\u521D\u59CB\u5316\u671F\u95F4\u88AB\u4FEE\u6539\uFF0C\u672A\u8986\u76D6\u672A\u77E5\u6587\u4EF6", "INIT_TARGET_CHANGED");
25950
26452
  }
25951
- renameSync(path11.join(stagingDir, entry2), destination);
26453
+ renameSync(path14.join(stagingDir, entry2), destination);
25952
26454
  }
25953
26455
  rmSync(stagingDir, { recursive: true, force: true });
25954
26456
  }
25955
26457
  async function initProject(input2) {
25956
26458
  const targetDir = resolveTargetDir(input2);
25957
26459
  return withInitLock(targetDir, async () => {
25958
- const initialTarget = assertInitialTarget(targetDir);
25959
26460
  const shortcut = input2.shortcut;
26461
+ const adoptExistingProject = !shortcut;
26462
+ const initialTarget = assertInitialTarget(targetDir, adoptExistingProject);
25960
26463
  if (shortcut && input2.yes && !input2.template) {
25961
26464
  throw new CliError("init theme / widget \u4F7F\u7528 --yes \u65F6\u5FC5\u987B\u5E26 --template", "INIT_TEMPLATE_REQUIRED");
25962
26465
  }
25963
26466
  if (!shortcut && !input2.typeCode?.trim()) {
25964
26467
  throw new CliError("\u8BF7\u9009\u62E9\u8D44\u6E90\u7C7B\u578B", "INIT_TYPE_REQUIRED");
25965
26468
  }
25966
- const typeCode = shortcut === "theme" ? "RT001" : shortcut === "widget" ? "RT002" : (await (input2.typeValidator ?? ((code) => getTypeInfo(code, input2.typeApis)))(input2.typeCode)).code;
25967
- if (!shortcut && isFixedTemplateType2(typeCode)) {
25968
- throw new CliError(
25969
- typeCode === "RT001" ? "\u4E3B\u9898\u8BF7\u4F7F\u7528 init theme \u521B\u5EFA\u6A21\u677F\u5DE5\u7A0B" : "\u63D2\u4EF6\u8BF7\u4F7F\u7528 init widget \u521B\u5EFA\u6A21\u677F\u5DE5\u7A0B",
25970
- "INIT_TEMPLATE_SHORTCUT_REQUIRED"
25971
- );
26469
+ if (!shortcut && !input2.artifact?.trim()) {
26470
+ throw new CliError("init \u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "INIT_ARTIFACT_REQUIRED");
25972
26471
  }
26472
+ const typeCode = shortcut === "theme" ? "RT001" : shortcut === "widget" ? "RT002" : (await (input2.typeValidator ?? ((code) => getTypeInfo(code, input2.typeApis)))(input2.typeCode)).code;
26473
+ const artifact = shortcut ? "dist" : normalizeProjectPath(targetDir, input2.artifact);
26474
+ if (!shortcut) assertArtifactAnchor(typeCode, path14.resolve(targetDir, artifact), targetDir);
25973
26475
  const template = shortcut && input2.template ? getTemplate(input2.template, shortcut) : void 0;
25974
26476
  if (shortcut && !template) throw new CliError("\u8BF7\u9009\u62E9\u6A21\u677F", "INIT_TEMPLATE_REQUIRED");
25975
- const parent = path11.dirname(targetDir);
26477
+ const parent = path14.dirname(targetDir);
25976
26478
  mkdirSync2(parent, { recursive: true });
25977
- const stagingDir = mkdtempSync(path11.join(parent, `.${path11.basename(targetDir)}.freelog-init-`));
26479
+ const stagingDir = mkdtempSync(path14.join(parent, `.${path14.basename(targetDir)}.freelog-init-`));
25978
26480
  try {
25979
26481
  if (template) await (input2.templateSource ?? installRemoteTemplate)({ template, targetDir: stagingDir });
26482
+ if (shortcut) mkdirSync2(path14.join(stagingDir, artifact), { recursive: true });
25980
26483
  const created = createIdentity(stagingDir, {
25981
26484
  subject: "resource",
25982
26485
  typeCode,
25983
- ...shortcut ? { filePath: "dist" } : {}
26486
+ filePath: artifact
25984
26487
  });
25985
- commitStaging(stagingDir, targetDir, initialTarget.targetExisted, initialTarget.preserveAuth);
26488
+ commitStaging(stagingDir, targetDir, initialTarget.targetExisted, initialTarget.preserveAuth, adoptExistingProject);
25986
26489
  return { ...created, n: created.n };
25987
26490
  } catch (error2) {
25988
26491
  rmSync(stagingDir, { recursive: true, force: true });
@@ -26012,9 +26515,18 @@ async function chooseProjectDir(dir) {
26012
26515
  }
26013
26516
  return selected;
26014
26517
  }
26518
+ async function chooseArtifact(artifact, yes) {
26519
+ if (artifact?.trim()) return artifact;
26520
+ if (yes || !isInteractive()) {
26521
+ throw new CliError("init \u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "INIT_ARTIFACT_REQUIRED");
26522
+ }
26523
+ const selected = (await askInput2("\u672C\u5730\u4EA7\u7269\u6587\u4EF6\u6216\u6784\u5EFA\u76EE\u5F55")).trim();
26524
+ if (!selected) throw new CliError("init \u5FC5\u987B\u901A\u8FC7 --artifact \u5173\u8054\u672C\u5730\u4EA7\u7269", "INIT_ARTIFACT_REQUIRED");
26525
+ return selected;
26526
+ }
26015
26527
  function createInitCommand() {
26016
26528
  const init2 = addSharedOptions(new Command11("init"));
26017
- init2.description("\u53EA\u5EFA\u672C\u5730\u5355\u8D44\u6E90\u5DE5\u7A0B").argument("[dir]", "\u76EE\u6807\u76EE\u5F55").option("--type <leaf-code>", "\u666E\u901A\u8D44\u6E90\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B").option("--resource-type <leaf-code>", "\u5DF2\u5F03\u7528\uFF1A\u8BF7\u6539\u7528 --type").action(async function(dir, options) {
26529
+ init2.description("\u5EFA\u7ACB\u9996\u4EFD\u672C\u5730\u8D44\u6E90\u72B6\u6001").argument("[dir]", "\u76EE\u6807\u76EE\u5F55").option("--type <leaf-code>", "\u666E\u901A\u8D44\u6E90\u6700\u7EC8\u53F6\u5B50\u7C7B\u578B").option("--artifact <path>", "\u8981\u5173\u8054\u7684\u672C\u5730\u4EA7\u7269\u6587\u4EF6\u6216\u6784\u5EFA\u76EE\u5F55").option("--resource-type <leaf-code>", "\u5DF2\u5F03\u7528\uFF1A\u8BF7\u6539\u7528 --type").action(async function(dir, options) {
26018
26530
  if (options.type && options.resourceType) throw new CliError("--type \u4E0E --resource-type \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528", "INIT_TYPE_CONFLICT");
26019
26531
  if (options.resourceType) console.warn("\u8B66\u544A\uFF1A--resource-type \u5DF2\u5F03\u7528\uFF0C\u8BF7\u6539\u7528 --type");
26020
26532
  const shared = sharedOptions(this);
@@ -26023,7 +26535,14 @@ function createInitCommand() {
26023
26535
  requireAuth({ cwd });
26024
26536
  const selected = options.type ?? options.resourceType;
26025
26537
  const type = selected ? await getTypeInfo(selected) : await chooseLeafType();
26026
- const created = await initProject({ cwd, dir: targetDir, typeCode: type.code, typeValidator: async () => type, yes: shared.yes });
26538
+ const created = await initProject({
26539
+ cwd,
26540
+ dir: targetDir,
26541
+ typeCode: type.code,
26542
+ typeValidator: async () => type,
26543
+ artifact: await chooseArtifact(options.artifact, shared.yes),
26544
+ yes: shared.yes
26545
+ });
26027
26546
  console.log(`\u5DF2\u521B\u5EFA\u672C\u5730\u8EAB\u4EFD ${created.n}.json`);
26028
26547
  });
26029
26548
  for (const shortcut of ["theme", "widget"]) {
@@ -26054,6 +26573,7 @@ async function statusProject(input2) {
26054
26573
  if (local?.resourceId) {
26055
26574
  assertPlatformAllowed();
26056
26575
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26576
+ resolveBoundIdentity(input2.cwd, input2.file);
26057
26577
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26058
26578
  const result = await infoApi2({
26059
26579
  resourceIdOrName: local.resourceId,
@@ -26164,7 +26684,7 @@ function createTypeCommand() {
26164
26684
  return;
26165
26685
  }
26166
26686
  const info2 = await getTypeInfo(options.type);
26167
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26687
+ console.log(formatTypeInfo(info2));
26168
26688
  });
26169
26689
  addSharedOptions(type.command("info")).description(
26170
26690
  // i18n: cli.command.type.info.description
@@ -26179,7 +26699,7 @@ function createTypeCommand() {
26179
26699
  throw new CliError("\u8BF7\u63D0\u4F9B\u7C7B\u578B\u7F16\u53F7", "TYPE_CODE_REQUIRED");
26180
26700
  }
26181
26701
  const info2 = await getTypeInfo(code);
26182
- console.log(`${info2.code} ${info2.nameChain ?? info2.name}`);
26702
+ console.log(formatTypeInfo(info2));
26183
26703
  });
26184
26704
  return type;
26185
26705
  }
@@ -26206,6 +26726,9 @@ async function syncResourceTitles(input2) {
26206
26726
  if (!identity.resourceId) return { identity, error: "\u8D44\u6E90\u5C1A\u672A\u7ED1\u5B9A" };
26207
26727
  try {
26208
26728
  const data = unwrapData2(await info2({ resourceIdOrName: identity.resourceId, isLoadLatestVersionInfo: 1 }));
26729
+ if (data.resourceId !== identity.resourceId) {
26730
+ return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6216\u4E0D\u5339\u914D resourceId" };
26731
+ }
26209
26732
  const title = String(data.resourceTitle ?? data.title ?? "");
26210
26733
  if (!title) return { identity, error: "\u5E73\u53F0\u8BE6\u60C5\u7F3A\u5C11\u6807\u9898" };
26211
26734
  return { identity, title };
@@ -26229,13 +26752,301 @@ async function syncResourceTitles(input2) {
26229
26752
  }, "sync-resource-titles");
26230
26753
  }
26231
26754
 
26755
+ // src/local/pendingOperation.ts
26756
+ import { randomUUID as randomUUID2 } from "crypto";
26757
+ import { existsSync as existsSync13, readFileSync as readFileSync10, unlinkSync as unlinkSync6 } from "fs";
26758
+ import path15 from "path";
26759
+ import { z as z4 } from "zod";
26760
+ var pendingVersionSubmitV1Schema = z4.object({
26761
+ schemaVersion: z4.literal(1),
26762
+ operationId: z4.string().uuid(),
26763
+ kind: z4.literal("version-submit"),
26764
+ resourceN: z4.number().int().positive(),
26765
+ resourceId: z4.string().min(1),
26766
+ env: z4.enum(["prod", "test", "dev"]),
26767
+ version: z4.string().min(1),
26768
+ fileSha1: z4.string().min(1),
26769
+ createdAt: z4.string().min(1)
26770
+ }).strict();
26771
+ var pendingVersionSubmitV2Schema = pendingVersionSubmitV1Schema.omit({ schemaVersion: true }).extend({
26772
+ schemaVersion: z4.literal(2),
26773
+ state: z4.enum(["prepared", "sending"])
26774
+ }).strict();
26775
+ function pendingOperationFilePath(cwd) {
26776
+ return path15.join(freelogDir(cwd), ".pending-operation.json");
26777
+ }
26778
+ function invalidPending() {
26779
+ throw new CliError("\u672A\u51B3\u8FDC\u7AEF\u64CD\u4F5C\u8BB0\u5F55\u65E0\u6548\uFF0C\u8BF7\u5148\u5907\u4EFD .freelog \u540E\u5904\u7406", "PENDING_OPERATION_INVALID");
26780
+ }
26781
+ function readPendingOperation(cwd) {
26782
+ const filePath = pendingOperationFilePath(cwd);
26783
+ if (!existsSync13(filePath)) return void 0;
26784
+ let raw;
26785
+ try {
26786
+ raw = JSON.parse(readFileSync10(filePath, "utf8"));
26787
+ } catch {
26788
+ return invalidPending();
26789
+ }
26790
+ const v2 = pendingVersionSubmitV2Schema.safeParse(raw);
26791
+ if (v2.success) return v2.data;
26792
+ const v1 = pendingVersionSubmitV1Schema.safeParse(raw);
26793
+ if (v1.success) {
26794
+ return { ...v1.data, state: "sending" };
26795
+ }
26796
+ return invalidPending();
26797
+ }
26798
+ function createPendingVersionSubmit(input2) {
26799
+ if (readPendingOperation(input2.cwd)) {
26800
+ throw new CliError("\u5B58\u5728\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C\uFF1B\u8BF7\u5148 resource recover", "PENDING_OPERATION_EXISTS");
26801
+ }
26802
+ const pending = {
26803
+ schemaVersion: 2,
26804
+ operationId: randomUUID2(),
26805
+ kind: "version-submit",
26806
+ resourceN: input2.resourceN,
26807
+ resourceId: input2.resourceId,
26808
+ env: input2.env,
26809
+ version: input2.version,
26810
+ fileSha1: input2.fileSha1,
26811
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
26812
+ state: "prepared"
26813
+ };
26814
+ atomicWriteFile(pendingOperationFilePath(input2.cwd), `${JSON.stringify(pending, null, 2)}
26815
+ `);
26816
+ return pending;
26817
+ }
26818
+ function markPendingVersionSubmitSending(cwd) {
26819
+ const pending = readPendingOperation(cwd);
26820
+ if (!pending || pending.state !== "prepared") {
26821
+ throw new CliError("\u672A\u51B3\u7248\u672C\u63D0\u4EA4\u4E0D\u5904\u4E8E\u53EF\u53D1\u9001\u72B6\u6001\uFF0C\u62D2\u7EDD\u8C03\u7528\u5E73\u53F0", "PENDING_OPERATION_STATE_INVALID");
26822
+ }
26823
+ const sending = {
26824
+ ...pending,
26825
+ schemaVersion: 2,
26826
+ state: "sending"
26827
+ };
26828
+ atomicWriteFile(pendingOperationFilePath(cwd), `${JSON.stringify(sending, null, 2)}
26829
+ `);
26830
+ return sending;
26831
+ }
26832
+ function clearPendingOperation(cwd) {
26833
+ const filePath = pendingOperationFilePath(cwd);
26834
+ if (!existsSync13(filePath)) return false;
26835
+ unlinkSync6(filePath);
26836
+ return true;
26837
+ }
26838
+ function assertNoPendingOperation(cwd) {
26839
+ const pending = readPendingOperation(cwd);
26840
+ if (pending) {
26841
+ throw new CliError(
26842
+ `\u5B58\u5728\u672A\u5B8C\u6210\u6536\u5C3E\u7684\u7248\u672C\u63D0\u4EA4\uFF08${pending.resourceId} ${pending.version}\uFF0C${pending.state}\uFF09\uFF1B\u8BF7\u5148 resource recover`,
26843
+ "PENDING_OPERATION_EXISTS"
26844
+ );
26845
+ }
26846
+ }
26847
+
26848
+ // src/domain/resource/recover.ts
26849
+ async function recoverPendingOperation(input2) {
26850
+ return withProjectLock(input2.cwd, () => recoverPendingOperationLocked(input2), "resource-recover");
26851
+ }
26852
+ async function recoverPendingOperationLocked(input2) {
26853
+ assertPlatformAllowed();
26854
+ const pending = readPendingOperation(input2.cwd);
26855
+ if (!pending) return "\u6CA1\u6709\u7ED3\u679C\u672A\u77E5\u7684\u8FDC\u7AEF\u64CD\u4F5C";
26856
+ const activeEnv2 = getEnv2();
26857
+ if (pending.env !== activeEnv2) {
26858
+ throw new CliError(
26859
+ `\u672A\u51B3\u64CD\u4F5C\u5C5E\u4E8E ${pending.env} \u73AF\u5883\uFF0C\u672C\u6B21\u662F ${activeEnv2}\uFF1B\u8BF7\u5207\u6362 --env \u540E\u6062\u590D`,
26860
+ "PENDING_OPERATION_ENV_MISMATCH"
26861
+ );
26862
+ }
26863
+ const identity = readIdentity(input2.cwd, pending.resourceN);
26864
+ if (identity.resourceId !== pending.resourceId || (identity.env ?? "prod") !== pending.env) {
26865
+ throw new CliError("\u672A\u51B3\u64CD\u4F5C\u4E0E\u5F53\u524D N.json \u8EAB\u4EFD\u6216\u73AF\u5883\u4E0D\u4E00\u81F4\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6E05\u7406", "PENDING_OPERATION_LOCAL_MISMATCH");
26866
+ }
26867
+ const draft = readDraft(input2.cwd, identity.n);
26868
+ if (draft?.resourceId && draft.resourceId !== pending.resourceId) {
26869
+ throw new CliError("\u672A\u51B3\u64CD\u4F5C\u4E0E\u5F53\u524D\u5DE5\u4F5C\u7A3F\u4E0D\u4E00\u81F4\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6E05\u7406", "PENDING_OPERATION_LOCAL_MISMATCH");
26870
+ }
26871
+ if (pending.state === "prepared") {
26872
+ if (!input2.apply) {
26873
+ return `\u5C1A\u672A\u53D1\u9001\uFF1A${pending.resourceId} ${pending.version} \u7684\u63D0\u4EA4\u8BF7\u6C42\u5C1A\u672A\u5141\u8BB8\u53D1\u51FA\uFF1B\u5DE5\u4F5C\u7A3F\u5DF2\u4FDD\u7559\u3002\u8FD0\u884C resource recover --apply --yes \u4EC5\u6E05\u7406\u672A\u51B3\u8BB0\u5F55\u540E\u53EF\u91CD\u65B0\u63D0\u4EA4`;
26874
+ }
26875
+ if (!input2.yes) {
26876
+ throw new CliError("\u5E94\u7528\u6062\u590D\u4F1A\u6E05\u7406\u672A\u51B3\u8BB0\u5F55\uFF1B\u8BF7\u4F7F\u7528 resource recover --apply --yes", "RECOVER_APPLY_NEED_YES");
26877
+ }
26878
+ commitLocalTransaction(input2.cwd, [
26879
+ { path: pendingOperationFilePath(input2.cwd), content: null }
26880
+ ]);
26881
+ return `\u5DF2\u5B8C\u6210\u6062\u590D\uFF1A${pending.resourceId} ${pending.version} \u7684\u672A\u51B3\u8BB0\u5F55\u5DF2\u6E05\u7406\uFF0C\u5DE5\u4F5C\u7A3F\u5DF2\u4FDD\u7559`;
26882
+ }
26883
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26884
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26885
+ const info2 = unwrapData2(await infoApi2({ resourceIdOrName: pending.resourceId }));
26886
+ assertRemoteResourceOwned({
26887
+ info: info2,
26888
+ resourceId: pending.resourceId,
26889
+ authUserId: auth.userId,
26890
+ codes: {
26891
+ invalid: "RECOVER_RESOURCE_INFO_INVALID",
26892
+ notOwner: "RECOVER_NOT_OWNER"
26893
+ }
26894
+ });
26895
+ const versionInfoApi = input2.apis?.resourceVersionInfo1 ?? ((params) => service_API_default.Resource.resourceVersionInfo1(params));
26896
+ const versionInfo = unwrapData2(await versionInfoApi({
26897
+ resourceId: pending.resourceId,
26898
+ version: pending.version
26899
+ }));
26900
+ const remoteSha = typeof versionInfo.fileSha1 === "string" ? versionInfo.fileSha1 : "";
26901
+ if (!remoteSha) {
26902
+ if (input2.apply) {
26903
+ throw new CliError("\u7EBF\u4E0A\u5C1A\u672A\u8BC1\u660E\u76EE\u6807\u7248\u672C\u5B58\u5728\uFF0C\u4E0D\u80FD\u5E94\u7528\u6062\u590D", "RECOVER_NOT_CONFIRMED");
26904
+ }
26905
+ return `\u672A\u786E\u8BA4\uFF1A\u7EBF\u4E0A\u4E0D\u5B58\u5728 ${pending.resourceId} \u7684\u7248\u672C ${pending.version}\uFF1B\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4`;
26906
+ }
26907
+ if (remoteSha !== pending.fileSha1) {
26908
+ if (input2.apply) {
26909
+ throw new CliError("\u7EBF\u4E0A\u76EE\u6807\u7248\u672C\u7684\u6587\u4EF6 SHA \u4E0D\u5339\u914D\uFF0C\u4E0D\u80FD\u5E94\u7528\u6062\u590D", "RECOVER_NOT_CONFIRMED");
26910
+ }
26911
+ return `\u51B2\u7A81\uFF1A\u7EBF\u4E0A ${pending.version} \u7684\u6587\u4EF6 SHA \u4E0E\u672A\u51B3\u8BB0\u5F55\u4E0D\u540C\uFF1B\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4`;
26912
+ }
26913
+ if (!input2.apply) {
26914
+ return `\u5DF2\u786E\u8BA4\uFF1A${pending.resourceId} ${pending.version} \u5DF2\u4EE5\u76F8\u540C SHA \u63D0\u4EA4\u3002\u8FD0\u884C resource recover --apply --yes \u6E05\u7406\u672C\u5730\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55`;
26915
+ }
26916
+ if (!input2.yes) {
26917
+ throw new CliError("\u5E94\u7528\u6062\u590D\u4F1A\u5220\u9664\u672C\u5730\u5DE5\u4F5C\u7A3F\uFF1B\u8BF7\u4F7F\u7528 resource recover --apply --yes", "RECOVER_APPLY_NEED_YES");
26918
+ }
26919
+ commitLocalTransaction(input2.cwd, [
26920
+ { path: pendingOperationFilePath(input2.cwd), content: null },
26921
+ { path: draftFilePath(input2.cwd, identity.n), content: null }
26922
+ ]);
26923
+ return `\u5DF2\u5B8C\u6210\u6062\u590D\uFF1A${pending.resourceId} ${pending.version} \u7684\u672C\u5730\u5DE5\u4F5C\u7A3F\u548C\u672A\u51B3\u8BB0\u5F55\u5DF2\u6E05\u7406`;
26924
+ }
26925
+
26926
+ // src/local/diagnostics.ts
26927
+ import { existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
26928
+ var IDENTITY_FILE_RE2 = /^([1-9]\d*)\.json$/;
26929
+ var DRAFT_FILE_RE2 = /^([1-9]\d*)\.version\.json$/;
26930
+ function errorMessage(error2) {
26931
+ return error2 instanceof Error ? error2.message : "\u672A\u77E5\u9519\u8BEF";
26932
+ }
26933
+ function duplicateDiagnostics(cwd, identities) {
26934
+ const result = [];
26935
+ for (const key of ["resourceId", "resourceName", "filePath"]) {
26936
+ const seen = /* @__PURE__ */ new Map();
26937
+ for (const identity of identities) {
26938
+ const raw = identity[key];
26939
+ if (!raw) continue;
26940
+ let value;
26941
+ try {
26942
+ value = key === "filePath" ? normalizeProjectPath(cwd, raw) : raw;
26943
+ } catch (error2) {
26944
+ result.push({ file: `${identity.n}.json`, code: "IDENTITY_PATH_INVALID", message: errorMessage(error2) });
26945
+ continue;
26946
+ }
26947
+ const previous = seen.get(value);
26948
+ if (previous !== void 0) {
26949
+ result.push({
26950
+ file: `${identity.n}.json`,
26951
+ code: "IDENTITY_CONFLICT",
26952
+ message: `\u4E0E ${previous}.json \u4F7F\u7528\u76F8\u540C ${key}`
26953
+ });
26954
+ } else {
26955
+ seen.set(value, identity.n);
26956
+ }
26957
+ }
26958
+ }
26959
+ return result;
26960
+ }
26961
+ function diagnoseLocalState(cwd) {
26962
+ const dir = freelogDir(cwd);
26963
+ if (!existsSync14(dir)) return "\u6CA1\u6709 .freelog \u672C\u5730\u72B6\u6001";
26964
+ let entries;
26965
+ try {
26966
+ entries = readdirSync5(dir).sort((left, right) => left.localeCompare(right, "en"));
26967
+ } catch (error2) {
26968
+ return `\u65E0\u6CD5\u8BFB\u53D6 .freelog\uFF1A${errorMessage(error2)}`;
26969
+ }
26970
+ const lines = [];
26971
+ const diagnostics = [];
26972
+ const identityNumbers = /* @__PURE__ */ new Set();
26973
+ const validIdentities = [];
26974
+ for (const file of entries) {
26975
+ const match = IDENTITY_FILE_RE2.exec(file);
26976
+ if (!match) continue;
26977
+ const n = Number(match[1]);
26978
+ identityNumbers.add(n);
26979
+ try {
26980
+ const identity = readIdentity(cwd, n);
26981
+ validIdentities.push(identity);
26982
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}\uFF0C${identity.typeCode}\uFF0C${identity.filePath}\uFF09`);
26983
+ } catch (error2) {
26984
+ diagnostics.push({ file, code: "IDENTITY_INVALID", message: errorMessage(error2) });
26985
+ }
26986
+ }
26987
+ for (const file of entries) {
26988
+ const match = DRAFT_FILE_RE2.exec(file);
26989
+ if (!match) continue;
26990
+ const n = Number(match[1]);
26991
+ if (!identityNumbers.has(n)) {
26992
+ diagnostics.push({ file, code: "DRAFT_ORPHAN", message: `\u6CA1\u6709\u540C\u53F7 ${n}.json` });
26993
+ continue;
26994
+ }
26995
+ if (!validIdentities.some((identity) => identity.n === n)) {
26996
+ diagnostics.push({ file, code: "DRAFT_IDENTITY_INVALID", message: `\u540C\u53F7 ${n}.json \u5DF2\u635F\u574F\uFF0C\u65E0\u6CD5\u6838\u9A8C\u5DE5\u4F5C\u7A3F` });
26997
+ continue;
26998
+ }
26999
+ try {
27000
+ const draft = readDraft(cwd, n);
27001
+ lines.push(`${file}\uFF1A\u6709\u6548\uFF08${draft?.draftKind ?? "\u65E0"} \u5DE5\u4F5C\u7A3F\uFF09`);
27002
+ } catch (error2) {
27003
+ diagnostics.push({ file, code: "DRAFT_INVALID", message: errorMessage(error2) });
27004
+ }
27005
+ }
27006
+ diagnostics.push(...duplicateDiagnostics(cwd, validIdentities));
27007
+ if (entries.includes("index.json")) {
27008
+ lines.push("index.json\uFF1A\u5386\u53F2\u9057\u7559\u6587\u4EF6\uFF0C\u5F53\u524D CLI \u4E0D\u8BFB\u53D6\u3001\u4E0D\u91CD\u5EFA\u4E5F\u4E0D\u81EA\u52A8\u5220\u9664");
27009
+ }
27010
+ try {
27011
+ const pending = readPendingOperation(cwd);
27012
+ if (pending) lines.push(`.pending-operation.json\uFF1A\u672A\u51B3 ${pending.kind}\uFF08${pending.resourceId} ${pending.version}\uFF09`);
27013
+ } catch (error2) {
27014
+ diagnostics.push({ file: ".pending-operation.json", code: "PENDING_OPERATION_INVALID", message: errorMessage(error2) });
27015
+ }
27016
+ if (entries.includes(".txn.json")) {
27017
+ lines.push(".txn.json\uFF1A\u5B58\u5728\uFF1B\u4E0B\u4E00\u6B21\u6301\u9501\u4E1A\u52A1\u547D\u4EE4\u4F1A\u5C1D\u8BD5\u6062\u590D\uFF0C\u8BCA\u65AD\u547D\u4EE4\u4E0D\u4F1A\u56DE\u653E");
27018
+ }
27019
+ if (lines.length === 0) lines.push("\u6CA1\u6709\u53EF\u8BC6\u522B\u7684\u8D44\u6E90\u8EAB\u4EFD\u6216\u5DE5\u4F5C\u7A3F");
27020
+ if (diagnostics.length === 0) return `\u672C\u5730\u72B6\u6001\u6B63\u5E38\uFF1A
27021
+ ${lines.map((line) => `- ${line}`).join("\n")}`;
27022
+ return [
27023
+ "\u672C\u5730\u72B6\u6001\u5B58\u5728\u95EE\u9898\uFF08\u672A\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\uFF09\uFF1A",
27024
+ ...lines.map((line) => `- ${line}`),
27025
+ ...diagnostics.map((item) => `- ${item.file} [${item.code}]\uFF1A${item.message}`),
27026
+ "\u8BF7\u5148\u5907\u4EFD .freelog\uFF1B\u4FEE\u590D\u6216\u5220\u9664\u660E\u786E\u65E0\u7528\u7684\u5B8C\u6574\u72B6\u6001\u5355\u5143\u540E\u518D\u6267\u884C\u4E1A\u52A1\u547D\u4EE4\u3002"
27027
+ ].join("\n");
27028
+ }
27029
+
26232
27030
  // src/commands/project/resource.ts
26233
27031
  function createResourceCommand() {
26234
27032
  const resource = addSharedOptions(new Command15("resource")).description("\u8D44\u6E90\u672C\u5730\u72B6\u6001\u7EF4\u62A4");
27033
+ resource.command("list").description("\u53EA\u8BFB\u8BCA\u65AD\u672C\u5730\u8D44\u6E90\u72B6\u6001\uFF08\u635F\u574F\u65F6\u4E5F\u53EF\u8FD0\u884C\uFF09").action(function() {
27034
+ const shared = readSharedOptions(this);
27035
+ console.log(diagnoseLocalState(resolveCwd(shared.cwd)));
27036
+ });
26235
27037
  resource.command("sync").description("\u4ECE\u5E73\u53F0\u540C\u6B65\u672C\u5730\u8D44\u6E90\u6807\u9898").action(async function() {
26236
27038
  const shared = readSharedOptions(this);
26237
27039
  console.log(await syncResourceTitles({ cwd: resolveCwd(shared.cwd), selector: shared.file }));
26238
27040
  });
27041
+ resource.command("recover").description("\u5904\u7406\u672A\u51B3\u7248\u672C\u63D0\u4EA4\uFF0C\u7EDD\u4E0D\u91CD\u53D1").option("--apply", "prepared \u4EC5\u6E05 marker\uFF1Bsending \u987B\u8FDC\u7AEF\u7248\u672C\u548C SHA \u5DF2\u8BC1\u660E\u6210\u529F\u624D\u6E05\u7406\u7A3F").action(async function() {
27042
+ const shared = readSharedOptions(this);
27043
+ const options = this.opts();
27044
+ console.log(await recoverPendingOperation({
27045
+ cwd: resolveCwd(shared.cwd),
27046
+ apply: options.apply === true,
27047
+ yes: shared.yes
27048
+ }));
27049
+ });
26239
27050
  return resource;
26240
27051
  }
26241
27052
 
@@ -26264,9 +27075,10 @@ function validateForOnline(info2) {
26264
27075
  }
26265
27076
  async function onlineResource(input2) {
26266
27077
  assertPlatformAllowed();
26267
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27078
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26268
27079
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
26269
27080
  const info2 = await loadInfo(identity.resourceId, input2.apis);
27081
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
26270
27082
  validateForOnline(info2);
26271
27083
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
26272
27084
  await update2({
@@ -26276,8 +27088,10 @@ async function onlineResource(input2) {
26276
27088
  }
26277
27089
  async function offlineResource(input2) {
26278
27090
  assertPlatformAllowed();
26279
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27091
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26280
27092
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27093
+ const info2 = await loadInfo(identity.resourceId, input2.apis);
27094
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
26281
27095
  const update2 = input2.apis?.update ?? ((params) => service_API_default.Resource.update(params));
26282
27096
  await update2({
26283
27097
  resourceId: identity.resourceId,
@@ -26327,7 +27141,7 @@ async function validateOnline(input2) {
26327
27141
  throw new CliError("\u76EE\u524D\u53EA\u652F\u6301 --for online", "VALIDATE_FOR");
26328
27142
  }
26329
27143
  assertPlatformAllowed();
26330
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27144
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26331
27145
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
26332
27146
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26333
27147
  const result = await infoApi2({
@@ -26336,6 +27150,11 @@ async function validateOnline(input2) {
26336
27150
  isLoadPolicyInfo: 1
26337
27151
  });
26338
27152
  const data = result.data ?? result;
27153
+ assertRemoteResourceWritable({
27154
+ info: data,
27155
+ resourceId: identity.resourceId,
27156
+ authUserId: auth.userId
27157
+ });
26339
27158
  validateForOnline(data);
26340
27159
  return "\u53EF\u4EE5\u4E0A\u67B6";
26341
27160
  }
@@ -26619,6 +27438,42 @@ function attrList(cwd, file) {
26619
27438
  );
26620
27439
  return list2.map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
26621
27440
  }
27441
+ function attrReview(cwd, file) {
27442
+ const identity = resolveIdentity(cwd, file);
27443
+ const draft = readDraft(cwd, identity.n);
27444
+ if (!draft) {
27445
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27446
+ }
27447
+ const items = draft.orphanedInputAttrs ?? [];
27448
+ if (items.length === 0) return "\u6CA1\u6709\u5F85\u590D\u6838\u7684\u9644\u52A0\u5C5E\u6027";
27449
+ return [
27450
+ "\u4EE5\u4E0B\u9644\u52A0\u5C5E\u6027\u5DF2\u4E0D\u5728\u5F53\u524D\u6587\u4EF6\u5206\u6790\u7ED3\u679C\u4E2D\uFF0C\u4E0D\u80FD\u63D0\u4EA4\uFF1A",
27451
+ ...items.map((item) => ` ${String(item.key ?? "\uFF08\u65E0\u952E\uFF09")}=${String(item.value ?? "")}`),
27452
+ "\u91CD\u65B0\u4E0A\u4F20\u5305\u542B\u8BE5 key \u7684\u6587\u4EF6\u4F1A\u81EA\u52A8\u6062\u590D\uFF1B\u786E\u8BA4\u4E0D\u518D\u9700\u8981\u53EF\u6267\u884C version attr review discard <key>\u3002"
27453
+ ].join("\n");
27454
+ }
27455
+ async function attrReviewDiscard(cwd, input2) {
27456
+ return withProjectLock(cwd, () => attrReviewDiscardLocked(cwd, input2), "version-attr-review-discard");
27457
+ }
27458
+ async function attrReviewDiscardLocked(cwd, input2) {
27459
+ const identity = resolveIdentity(cwd, input2.file);
27460
+ const draft = readDraft(cwd, identity.n);
27461
+ if (!draft) {
27462
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27463
+ }
27464
+ const key = input2.key.trim();
27465
+ const items = draft.orphanedInputAttrs ?? [];
27466
+ if (!key || !items.some((item) => item.key === key)) {
27467
+ throw new CliError("\u627E\u4E0D\u5230\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", "ATTR_REVIEW_NOT_FOUND");
27468
+ }
27469
+ const preview = `\u5373\u5C06\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027\uFF1A${key}`;
27470
+ if (!await confirmDestructive(preview, "\u4E22\u5F03\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027", input2.yes)) {
27471
+ return "\u5DF2\u53D6\u6D88";
27472
+ }
27473
+ draft.orphanedInputAttrs = items.filter((item) => item.key !== key);
27474
+ writeDraft(cwd, identity.n, draft);
27475
+ return preview;
27476
+ }
26622
27477
 
26623
27478
  // src/commands/version/attr.ts
26624
27479
  function createVersionAttrCommand() {
@@ -26640,242 +27495,463 @@ function createVersionAttrCommand() {
26640
27495
  const shared = readSharedOptions(this);
26641
27496
  console.log(attrList(resolveCwd(shared.cwd), shared.file));
26642
27497
  });
27498
+ const review = addSharedOptions(attr.command("review")).description("\u67E5\u770B\u6216\u5904\u7406\u6587\u4EF6\u5206\u6790\u53D8\u5316\u540E\u7684\u9644\u52A0\u5C5E\u6027");
27499
+ review.action(function() {
27500
+ const shared = readSharedOptions(this);
27501
+ console.log(attrReview(resolveCwd(shared.cwd), shared.file));
27502
+ });
27503
+ addSharedOptions(review.command("discard")).description("\u786E\u8BA4\u4E22\u5F03\u4E00\u9879\u5F85\u590D\u6838\u9644\u52A0\u5C5E\u6027").argument("<key>", "\u9644\u52A0\u5C5E\u6027\u952E").action(async function(key) {
27504
+ const shared = readSharedOptions(this);
27505
+ console.log(await attrReviewDiscard(resolveCwd(shared.cwd), {
27506
+ key,
27507
+ file: shared.file,
27508
+ yes: shared.yes
27509
+ }));
27510
+ });
26643
27511
  return attr;
26644
27512
  }
26645
27513
 
26646
27514
  // src/commands/version/create-version.ts
26647
27515
  import { Command as Command20 } from "commander";
26648
27516
 
26649
- // src/domain/version/submit.ts
26650
- function fieldName(error2) {
26651
- const rec = error2;
26652
- return rec.field;
26653
- }
26654
- function errorDetail(error2) {
26655
- const rec = error2;
26656
- return rec.result?.msg ?? rec.msg ?? rec.message;
26657
- }
26658
- function buildVersionPayload(input2) {
26659
- return {
26660
- resourceId: input2.resourceId,
26661
- version: input2.version,
26662
- fileSha1: input2.draft.fileSha1,
26663
- filename: input2.draft.filename,
26664
- description: input2.description ?? input2.draft.description ?? "",
26665
- customPropertyDescriptors: input2.draft.customPropertyDescriptors ?? [],
26666
- dependencies: input2.draft.dependencies ?? [],
26667
- inputAttrs: input2.draft.inputAttrs ?? [],
26668
- baseUpcastResources: [],
26669
- authExcludedItems: []
26670
- };
26671
- }
26672
- async function submitVersion(input2) {
26673
- assertPlatformAllowed();
26674
- if (!input2.identity.resourceId) {
26675
- throw new CliError("\u8BF7\u5148 create \u6216 bind", "SUBMIT_NO_RESOURCE");
27517
+ // src/domain/version/form/option.ts
27518
+ var MAX_OPTION = 30;
27519
+ var MAX_OPTION_ITEM = 30;
27520
+ var MAX_TEXT_DEFAULT = 140;
27521
+ var MAX_NAME2 = 50;
27522
+ var MAX_REMARK2 = 50;
27523
+ function assertValidName2(name) {
27524
+ if (name.length > MAX_NAME2) {
27525
+ throw new CliError("\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26", "OPTION_NAME_LONG");
26676
27526
  }
26677
- const draft = readDraft(input2.cwd, input2.identity.n);
26678
- if (!draft?.fileSha1) {
26679
- throw new CliError("\u7F3A\u5C11\u6587\u4EF6", "SUBMIT_FILE");
27527
+ }
27528
+ function assertValidRemark2(remark) {
27529
+ if (remark.length > MAX_REMARK2) {
27530
+ throw new CliError("\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26\u3002", "OPTION_REMARK_LONG");
26680
27531
  }
26681
- if (draft.analyzedSha1 !== draft.fileSha1) {
26682
- throw new CliError("\u5F53\u524D\u6587\u4EF6\u5C1A\u672A\u5B8C\u6210\u5C5E\u6027\u5206\u6790\uFF0C\u8BF7\u91CD\u65B0 prepare", "SUBMIT_ANALYSIS_REQUIRED");
27532
+ }
27533
+ function assertOptionValueLength(value) {
27534
+ if (value.length > MAX_TEXT_DEFAULT) {
27535
+ throw new CliError("\u4E0D\u8D85\u8FC7140\u4E2A\u5B57\u7B26", "OPTION_VALUE_LONG");
26683
27536
  }
26684
- if ((draft.orphanedInputAttrs ?? []).length > 0) {
26685
- 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");
27537
+ }
27538
+ function isOption(item) {
27539
+ return item.type === "editableText" || item.type === "select";
27540
+ }
27541
+ async function assertTypeAllowsOption(input2) {
27542
+ if (input2.supportOptionalConfig === true) return;
27543
+ if (input2.supportOptionalConfig === false) {
27544
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
26686
27545
  }
26687
- const payload = buildVersionPayload({
26688
- resourceId: input2.identity.resourceId,
26689
- version: input2.version,
26690
- draft
26691
- });
26692
- const createVersion2 = input2.apis?.createVersion ?? ((params) => service_API_default.Resource.createVersion(params));
26693
- try {
26694
- await createVersion2(payload);
26695
- } catch (error2) {
26696
- const field = fieldName(error2);
26697
- const detail = errorDetail(error2);
26698
- throw new CliError(
26699
- field ? `\u63D0\u4EA4\u5931\u8D25\uFF1A${field}${detail ? `\uFF1A${detail}` : ""}` : detail ? `\u63D0\u4EA4\u5931\u8D25\uFF1A${detail}` : "\u63D0\u4EA4\u5931\u8D25",
26700
- "SUBMIT_FAILED"
26701
- );
27546
+ requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27547
+ const type = await getTypeInfo(input2.typeCode, input2.apis);
27548
+ if (type.supportOptionalConfig !== 2) {
27549
+ throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
26702
27550
  }
26703
- deleteDraft(input2.cwd, input2.identity.n);
26704
27551
  }
26705
-
26706
- // src/domain/version/file.ts
26707
- import { createHash as createHash3 } from "crypto";
26708
- import { existsSync as existsSync11, readFileSync as readFileSync9, statSync as statSync2, unlinkSync as unlinkSync6 } from "fs";
26709
- import path13 from "path";
26710
-
26711
- // src/domain/version/zip.ts
26712
- import { createWriteStream, existsSync as existsSync10, readdirSync as readdirSync4, statSync, unlinkSync as unlinkSync5 } from "fs";
26713
- import { tmpdir } from "os";
26714
- import path12 from "path";
26715
- import { finished } from "stream/promises";
26716
- import archiver from "archiver";
26717
- function isThemeOrWidget2(typeCode) {
26718
- return typeCode === "RT001" || typeCode === "RT002";
27552
+ async function assertDraftOptionsAllowed(input2) {
27553
+ if (!(input2.customPropertyDescriptors ?? []).some((item) => isOption(item))) return;
27554
+ await assertTypeAllowsOption(input2);
26719
27555
  }
26720
- function assertArtifactPath(typeCode, filePath) {
26721
- const stats = existsSync10(filePath) ? statSync(filePath) : void 0;
26722
- const themeOrWidget = isThemeOrWidget2(typeCode);
26723
- if (stats?.isDirectory() && !themeOrWidget) {
26724
- throw new CliError("\u4E0D\u652F\u6301\u6587\u4EF6\u5939\uFF0C\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6587\u4EF6\u3002", "FILE_DIRECTORY_UNSUPPORTED");
27556
+ function optionListFromDraft(draft) {
27557
+ return (draft.customPropertyDescriptors ?? []).filter((item) => isOption(item));
27558
+ }
27559
+ function normalizeMode(mode) {
27560
+ if (mode === void 0) return void 0;
27561
+ if (mode === "\u4E0B\u62C9" || mode === "\u4E0B\u62C9\u5217\u8868" || mode === "select") return "select";
27562
+ if (mode === "\u6587\u672C" || mode === "\u6587\u672C\u8F93\u5165\u6846" || mode === "input") return "editableText";
27563
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27564
+ }
27565
+ function parseCandidates(raw) {
27566
+ const items = (raw ?? "").split("|").map((item) => item.trim()).filter(Boolean);
27567
+ if (items.length === 0) throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27568
+ if (items.length > MAX_OPTION_ITEM) throw new CliError("\u9009\u9879\u4E2A\u6570\u4E0D\u80FD\u8D85\u8FC730\u9879", "OPTION_OPTIONS");
27569
+ for (const item of items) assertOptionValueLength(item);
27570
+ if (items.some((item, index) => items.indexOf(item) !== index)) {
27571
+ throw new CliError("\u8BE5\u9009\u9879\u5DF2\u5B58\u5728", "OPTION_DUPLICATE_OPTION");
26725
27572
  }
26726
- if (themeOrWidget && stats?.isFile() && filePath.toLowerCase().endsWith(".zip")) {
26727
- throw new CliError("\u4E3B\u9898/\u63D2\u4EF6\u8BF7\u6307\u5B9A\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF0C\u4E0D\u8981\u81EA\u5DF1\u6253 zip\u3002", "FILE_THEME_ZIP");
27573
+ return items;
27574
+ }
27575
+ async function optionAdd(cwd, input2) {
27576
+ return withProjectLock(cwd, () => optionAddLocked(cwd, input2), "version-option-add");
27577
+ }
27578
+ async function optionAddLocked(cwd, input2) {
27579
+ const identity = resolveIdentity(cwd, input2.file);
27580
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27581
+ const draft = readDraft(cwd, identity.n) ?? {
27582
+ baseUpcastResources: [],
27583
+ authExcludedItems: []
27584
+ };
27585
+ const parsed = parseLine(input2.line ?? "");
27586
+ if (!parsed.name || !parsed.key) {
27587
+ throw new CliError("\u53EF\u9009\u914D\u7F6E\u9700\u8981\u540D\u79F0\u548C\u952E", "OPTION_FIELDS");
26728
27588
  }
26729
- if (themeOrWidget && stats?.isFile()) {
26730
- throw new CliError("\u4E3B\u9898/\u63D2\u4EF6\u8BF7\u6307\u5B9A\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF0C\u4E0D\u8981\u81EA\u5DF1\u6253 zip\u3002", "FILE_THEME_ZIP");
27589
+ assertValidKey(parsed.key);
27590
+ const list2 = draft.customPropertyDescriptors ?? [];
27591
+ if (optionListFromDraft(draft).length >= MAX_OPTION) {
27592
+ throw new CliError("\u6700\u591A\u53EF\u6DFB\u52A030\u4E2A\u53EF\u9009\u914D\u7F6E", "OPTION_FULL");
26731
27593
  }
26732
- }
26733
- async function zipDirectoryContents(dir) {
26734
- if (!existsSync10(dir) || !statSync(dir).isDirectory()) {
26735
- throw new CliError(`\u6CA1\u6709\u6784\u5EFA\u4EA7\u7269 ${dir}\uFF0C\u8BF7\u5148\u6784\u5EFA`, "ZIP_MISSING");
27594
+ if (list2.some((item) => item.key === parsed.key)) {
27595
+ throw new CliError(`\u952E ${parsed.key} \u5DF2\u5B58\u5728`, "OPTION_DUPLICATE");
26736
27596
  }
26737
- if (existsSync10(path12.join(dir, ".freelog"))) {
26738
- 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");
27597
+ if (list2.some((item) => item.name === parsed.name)) {
27598
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
26739
27599
  }
26740
- const entries = readdirSync4(dir);
26741
- if (entries.length === 0) {
26742
- throw new CliError("\u6784\u5EFA\u4EA7\u7269\u662F\u7A7A\u7684\uFF0C\u8BF7\u5148\u6784\u5EFA", "ZIP_EMPTY");
27600
+ const normalizedMode = normalizeMode(parsed.mode);
27601
+ if (!normalizedMode) {
27602
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
26743
27603
  }
26744
- const outPath = path12.join(
26745
- tmpdir(),
26746
- `freelog-zip-${process.pid}-${Date.now()}.zip`
26747
- );
26748
- const output2 = createWriteStream(outPath);
26749
- const archive = archiver("zip", { zlib: { level: 9 } });
26750
- try {
26751
- archive.on("error", (error2) => output2.destroy(error2));
26752
- archive.pipe(output2);
26753
- for (const entry2 of entries) {
26754
- const full = path12.join(dir, entry2);
26755
- const stats = statSync(full);
26756
- if (stats.isDirectory()) {
26757
- archive.directory(full, entry2);
26758
- } else {
26759
- archive.file(full, { name: entry2 });
26760
- }
26761
- }
26762
- await archive.finalize();
26763
- await finished(output2);
26764
- return outPath;
26765
- } catch (error2) {
26766
- if (existsSync10(outPath)) {
26767
- try {
26768
- unlinkSync5(outPath);
26769
- } catch {
26770
- }
26771
- }
26772
- throw error2;
27604
+ const isSelect = normalizedMode === "select";
27605
+ const isText = normalizedMode === "editableText";
27606
+ if (isSelect && parsed.defaultValue) {
27607
+ throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
26773
27608
  }
26774
- }
26775
- async function prepareUploadPath(typeCode, filePath) {
26776
- assertArtifactPath(typeCode, filePath);
26777
- if (isThemeOrWidget2(typeCode) && existsSync10(filePath) && statSync(filePath).isDirectory()) {
26778
- return zipDirectoryContents(filePath);
27609
+ if (isText && parsed.options) {
27610
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
26779
27611
  }
26780
- return filePath;
26781
- }
26782
-
26783
- // src/domain/version/file.ts
26784
- var ANALYZE_TIMEOUT_MS = 12e4;
26785
- var ANALYZE_POLL_INTERVAL_MS = 200;
26786
- function resolveExistingPath(cwd, raw) {
26787
- if (existsSync11(raw)) {
26788
- return path13.resolve(raw);
27612
+ if (!isSelect && !isText) {
27613
+ throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
26789
27614
  }
26790
- const joined = path13.resolve(cwd, raw);
26791
- if (existsSync11(joined)) {
26792
- return joined;
27615
+ let type = "editableText";
27616
+ let defaultValue = parsed.defaultValue ?? parsed.value ?? "";
27617
+ let candidateItems;
27618
+ assertValidName2(parsed.name);
27619
+ assertValidRemark2(parsed.remark ?? "");
27620
+ if (isSelect) {
27621
+ const options = parseCandidates(parsed.options);
27622
+ type = "select";
27623
+ defaultValue = options[0] ?? "";
27624
+ candidateItems = options;
27625
+ } else {
27626
+ assertOptionValueLength(defaultValue);
26793
27627
  }
26794
- return void 0;
27628
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27629
+ list2.push({
27630
+ name: parsed.name,
27631
+ key: parsed.key,
27632
+ remark: parsed.remark ?? "",
27633
+ defaultValue,
27634
+ type,
27635
+ ...candidateItems ? { candidateItems } : {}
27636
+ });
27637
+ draft.customPropertyDescriptors = list2;
27638
+ writeDraft(cwd, identity.n, draft);
27639
+ return preview;
26795
27640
  }
26796
- function confirmLocalPath(identity, file, yes, cwd) {
26797
- if (file !== void 0) {
26798
- const normalizedFile = normalizeProjectPath(cwd, file);
26799
- const resolved = resolveExistingPath(cwd, normalizedFile);
26800
- if (!resolved) {
26801
- throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${normalizedFile}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
27641
+ async function optionSet(cwd, input2) {
27642
+ return withProjectLock(cwd, () => optionSetLocked(cwd, input2), "version-option-set");
27643
+ }
27644
+ async function optionSetLocked(cwd, input2) {
27645
+ const identity = resolveIdentity(cwd, input2.file);
27646
+ const draft = readDraft(cwd, identity.n);
27647
+ if (!draft) {
27648
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27649
+ }
27650
+ const parsed = parseLine(input2.line ?? "");
27651
+ const found = (draft.customPropertyDescriptors ?? []).find(
27652
+ (item) => isOption(item) && (item.key === parsed.key || item.name === parsed.name)
27653
+ );
27654
+ if (!found) {
27655
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
27656
+ }
27657
+ await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27658
+ assertKeyUnchanged(String(found.key), parsed.key);
27659
+ assertValidName2(parsed.name ?? String(found.name ?? ""));
27660
+ assertValidRemark2(parsed.remark ?? String(found.remark ?? ""));
27661
+ const nextType = normalizeMode(parsed.mode) ?? (String(found.type) === "select" ? "select" : "editableText");
27662
+ const nextDefault = parsed.defaultValue ?? parsed.value;
27663
+ if (nextType === "select" && nextDefault !== void 0) {
27664
+ throw new CliError("\u4E0B\u62C9\u9ED8\u8BA4\u503C\u56FA\u5B9A\u4E3A\u7B2C\u4E00\u9879\uFF0C\u8BF7\u628A\u8981\u9ED8\u8BA4\u7684\u9009\u9879\u653E\u5728\u7B2C\u4E00\u4F4D", "OPTION_SELECT_DEFAULT");
27665
+ }
27666
+ if (nextType === "editableText") {
27667
+ if (parsed.options !== void 0) {
27668
+ throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
26802
27669
  }
26803
- return resolved;
27670
+ if (nextDefault !== void 0) assertOptionValueLength(nextDefault);
26804
27671
  }
26805
- const recorded = identity.filePath ? normalizeProjectPath(cwd, identity.filePath) : void 0;
26806
- const recordedExists = recorded ? resolveExistingPath(cwd, recorded) : void 0;
26807
- if (recordedExists) {
26808
- return recordedExists;
27672
+ if (parsed.name && (draft.customPropertyDescriptors ?? []).some((item) => item.name === parsed.name && item.key !== found.key)) {
27673
+ throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
26809
27674
  }
26810
- if (yes) {
26811
- throw new CliError("\u8BF7 --artifact \u6307\u5B9A\u672C\u5730\u6587\u4EF6\u6216\u76EE\u5F55", "FILE_NEED_FLAG");
27675
+ if (parsed.name) {
27676
+ found.name = parsed.name;
26812
27677
  }
26813
- if (!recorded) {
26814
- throw new CliError("\u8BF7\u6307\u5B9A\u672C\u5730\u6587\u4EF6\u8DEF\u5F84", "FILE_PATH_REQUIRED");
27678
+ if (parsed.remark !== void 0) {
27679
+ found.remark = parsed.remark;
26815
27680
  }
26816
- throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${recorded}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
26817
- }
26818
- async function waitAnalyze(sha1, typeCode, apis, now = Date.now, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
26819
- const filesListInfo2 = apis.filesListInfo ?? ((params) => service_API_default.Storage.filesListInfo(params));
26820
- const started = now();
26821
- while (now() - started <= ANALYZE_TIMEOUT_MS) {
26822
- const info2 = unwrapFirst(await filesListInfo2({ sha1, resourceTypeCode: typeCode }));
26823
- const status = Number(info2.metaAnalyzeStatus ?? info2.status);
26824
- if (status === 2) {
26825
- return info2;
26826
- }
26827
- if (status === 3) {
26828
- throw new CliError("\u5C5E\u6027\u89E3\u6790\u5931\u8D25", "FILE_ANALYZE_FAILED");
27681
+ if (nextType === "select") {
27682
+ const candidates = parsed.options !== void 0 ? parseCandidates(parsed.options) : Array.isArray(found.candidateItems) ? found.candidateItems.map(String) : [];
27683
+ if (candidates.length === 0) {
27684
+ throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
26829
27685
  }
26830
- if (now() - started > ANALYZE_TIMEOUT_MS) {
26831
- break;
27686
+ found.type = "select";
27687
+ found.candidateItems = candidates;
27688
+ found.defaultValue = candidates[0];
27689
+ } else {
27690
+ found.type = "editableText";
27691
+ delete found.candidateItems;
27692
+ if (nextDefault !== void 0) {
27693
+ found.defaultValue = nextDefault;
26832
27694
  }
26833
- await sleep(ANALYZE_POLL_INTERVAL_MS);
26834
27695
  }
26835
- throw new CliError("\u5C5E\u6027\u89E3\u6790\u8D85\u65F6", "FILE_ANALYZE_TIMEOUT");
26836
- }
26837
- function sha1OfFile(uploadPath) {
26838
- return createHash3("sha1").update(readFileSync9(uploadPath)).digest("hex");
27696
+ const preview = await confirmWrite(previewLine(parsed), input2.yes);
27697
+ writeDraft(cwd, identity.n, draft);
27698
+ return preview;
26839
27699
  }
26840
- async function uploadIfNew(uploadPath, sha1, typeCode, apis) {
26841
- const fileIsExist2 = apis.fileIsExist ?? ((params) => service_API_default.Storage.fileIsExist(params));
26842
- const exists2 = unwrapFirst(await fileIsExist2({ sha1 }));
26843
- if (exists2.isExisting || exists2.exist || exists2.data === true) {
26844
- return;
26845
- }
26846
- const upload = apis.uploadFile ?? ((params) => service_API_default.Storage.uploadFile(params));
26847
- await upload({ file: readFileSync9(uploadPath), resourceType: typeCode });
27700
+ function optionRm(cwd, key, file) {
27701
+ return withProjectLock(cwd, () => optionRmLocked(cwd, key, file), "version-option-rm");
26848
27702
  }
26849
- function removeTempZip(uploadPath, localPath) {
26850
- if (uploadPath === localPath || !existsSync11(uploadPath)) {
26851
- return;
27703
+ function optionRmLocked(cwd, key, file) {
27704
+ const identity = resolveIdentity(cwd, file);
27705
+ const draft = readDraft(cwd, identity.n);
27706
+ if (!draft) {
27707
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
26852
27708
  }
26853
- try {
26854
- unlinkSync6(uploadPath);
26855
- } catch {
27709
+ const list2 = draft.customPropertyDescriptors ?? [];
27710
+ const next = list2.filter((item) => !(isOption(item) && item.key === key));
27711
+ if (next.length === list2.length) {
27712
+ throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
26856
27713
  }
27714
+ draft.customPropertyDescriptors = next;
27715
+ writeDraft(cwd, identity.n, draft);
27716
+ return key;
26857
27717
  }
26858
- function writeSha1ToDraft(input2, uploaded, analysis) {
26859
- const draft = readDraft(input2.cwd, input2.identity.n) ?? {
27718
+ function optionList(cwd, file) {
27719
+ const identity = resolveIdentity(cwd, file);
27720
+ const draft = readDraft(cwd, identity.n);
27721
+ return optionListFromDraft(draft ?? {}).map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
27722
+ }
27723
+
27724
+ // src/domain/version/submit.ts
27725
+ function fieldName(error2) {
27726
+ const rec = error2;
27727
+ return rec.field;
27728
+ }
27729
+ function errorDetail(error2) {
27730
+ const rec = error2;
27731
+ return rec.result?.msg ?? rec.msg ?? rec.message;
27732
+ }
27733
+ function buildVersionPayload(input2) {
27734
+ return {
27735
+ resourceId: input2.resourceId,
27736
+ version: input2.version,
27737
+ fileSha1: input2.draft.fileSha1,
27738
+ filename: input2.draft.filename,
27739
+ description: input2.description ?? input2.draft.description ?? "",
27740
+ customPropertyDescriptors: input2.draft.customPropertyDescriptors ?? [],
27741
+ dependencies: input2.draft.dependencies ?? [],
27742
+ inputAttrs: input2.draft.inputAttrs ?? [],
26860
27743
  baseUpcastResources: [],
26861
27744
  authExcludedItems: []
26862
27745
  };
26863
- const hasMetadata = Array.isArray(analysis.metaInfoArray);
26864
- const metadata = hasMetadata ? analysis.metaInfoArray : [];
26865
- const additionalKeys = new Set(
27746
+ }
27747
+ async function submitVersion(input2) {
27748
+ return withProjectLock(input2.cwd, () => submitVersionLocked(input2), "submit-version");
27749
+ }
27750
+ async function submitVersionLocked(input2) {
27751
+ assertPlatformAllowed();
27752
+ if (!input2.identity.resourceId) {
27753
+ throw new CliError("\u8BF7\u5148 create \u6216 bind", "SUBMIT_NO_RESOURCE");
27754
+ }
27755
+ const draft = readDraft(input2.cwd, input2.identity.n);
27756
+ if (!draft?.fileSha1) {
27757
+ throw new CliError("\u7F3A\u5C11\u6587\u4EF6", "SUBMIT_FILE");
27758
+ }
27759
+ if (draft.analyzedSha1 !== draft.fileSha1) {
27760
+ throw new CliError("\u5F53\u524D\u6587\u4EF6\u5C1A\u672A\u5B8C\u6210\u5C5E\u6027\u5206\u6790\uFF0C\u8BF7\u91CD\u65B0 prepare", "SUBMIT_ANALYSIS_REQUIRED");
27761
+ }
27762
+ if ((draft.orphanedInputAttrs ?? []).length > 0) {
27763
+ throw new CliError("\u6587\u4EF6\u5206\u6790\u53D8\u5316\u5F85\u5904\u7406\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u590D\u6838\u9644\u52A0\u5C5E\u6027", "SUBMIT_ORPHANED_INPUT_ATTRS");
27764
+ }
27765
+ await assertDraftOptionsAllowed({
27766
+ cwd: input2.cwd,
27767
+ typeCode: input2.identity.typeCode,
27768
+ customPropertyDescriptors: draft.customPropertyDescriptors,
27769
+ homeDir: input2.homeDir,
27770
+ apis: input2.apis
27771
+ });
27772
+ const payload = buildVersionPayload({
27773
+ resourceId: input2.identity.resourceId,
27774
+ version: input2.version,
27775
+ draft
27776
+ });
27777
+ const createVersion2 = input2.apis?.createVersion ?? ((params) => service_API_default.Resource.createVersion(params));
27778
+ assertNoPendingOperation(input2.cwd);
27779
+ createPendingVersionSubmit({
27780
+ cwd: input2.cwd,
27781
+ resourceN: input2.identity.n,
27782
+ resourceId: input2.identity.resourceId,
27783
+ env: getEnv2(),
27784
+ version: input2.version,
27785
+ fileSha1: draft.fileSha1
27786
+ });
27787
+ markPendingVersionSubmitSending(input2.cwd);
27788
+ try {
27789
+ await createVersion2(payload);
27790
+ } catch (error2) {
27791
+ if (!isDefinitivePlatformRejection(error2)) {
27792
+ throw new CliError(
27793
+ "\u7248\u672C\u63D0\u4EA4\u8BF7\u6C42\u7ED3\u679C\u672A\u77E5\uFF1B\u8BF7\u8FD0\u884C resource recover \u6838\u9A8C\uFF0C\u5207\u52FF\u91CD\u590D\u63D0\u4EA4\u6216\u91CD\u65B0 bump",
27794
+ "SUBMIT_RESULT_UNKNOWN"
27795
+ );
27796
+ }
27797
+ clearPendingOperation(input2.cwd);
27798
+ const field = fieldName(error2);
27799
+ const detail = errorDetail(error2);
27800
+ throw new CliError(
27801
+ field ? `\u63D0\u4EA4\u5931\u8D25\uFF1A${field}${detail ? `\uFF1A${detail}` : ""}` : detail ? `\u63D0\u4EA4\u5931\u8D25\uFF1A${detail}` : "\u63D0\u4EA4\u5931\u8D25",
27802
+ "SUBMIT_FAILED"
27803
+ );
27804
+ }
27805
+ commitLocalTransaction(input2.cwd, [
27806
+ { path: draftFilePath(input2.cwd, input2.identity.n), content: null },
27807
+ { path: pendingOperationFilePath(input2.cwd), content: null }
27808
+ ]);
27809
+ }
27810
+ function isDefinitivePlatformRejection(error2) {
27811
+ if (error2 instanceof CliError) return true;
27812
+ const rec = error2;
27813
+ if (typeof rec.field === "string" || typeof rec.msg === "string" || typeof rec.result?.msg === "string") {
27814
+ return true;
27815
+ }
27816
+ const status = rec.status ?? rec.statusCode ?? rec.response?.status;
27817
+ return typeof status === "number" && status >= 400 && status < 500;
27818
+ }
27819
+
27820
+ // src/domain/version/file.ts
27821
+ import { createHash as createHash3 } from "crypto";
27822
+ import { createReadStream, existsSync as existsSync15, openAsBlob, statSync as statSync3, unlinkSync as unlinkSync7 } from "fs";
27823
+ import path16 from "path";
27824
+ var ANALYZE_TIMEOUT_MS = 12e4;
27825
+ var ANALYZE_POLL_INTERVAL_MS = 200;
27826
+ function resolveExistingPath(cwd, raw) {
27827
+ const joined = path16.resolve(cwd, raw);
27828
+ if (existsSync15(joined)) {
27829
+ return joined;
27830
+ }
27831
+ return void 0;
27832
+ }
27833
+ function confirmLocalPath(identity, file, yes, cwd) {
27834
+ if (file !== void 0) {
27835
+ const normalizedFile = normalizeProjectPath(cwd, file);
27836
+ const resolved = resolveExistingPath(cwd, normalizedFile);
27837
+ if (!resolved) {
27838
+ throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${normalizedFile}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
27839
+ }
27840
+ resolveExistingProjectPath(cwd, resolved);
27841
+ return resolved;
27842
+ }
27843
+ const recorded = identity.filePath ? normalizeProjectPath(cwd, identity.filePath) : void 0;
27844
+ const recordedExists = recorded ? resolveExistingPath(cwd, recorded) : void 0;
27845
+ if (recordedExists) {
27846
+ resolveExistingProjectPath(cwd, recordedExists);
27847
+ return recordedExists;
27848
+ }
27849
+ if (yes) {
27850
+ throw new CliError("\u8BF7 --artifact \u6307\u5B9A\u672C\u5730\u6587\u4EF6\u6216\u76EE\u5F55", "FILE_NEED_FLAG");
27851
+ }
27852
+ if (!recorded) {
27853
+ throw new CliError("\u8BF7\u6307\u5B9A\u672C\u5730\u6587\u4EF6\u8DEF\u5F84", "FILE_PATH_REQUIRED");
27854
+ }
27855
+ throw new CliError(`\u672C\u5730\u6587\u4EF6\u4E0D\u5728\uFF1A${recorded}\u3002\u4E0D\u51C6\u7EED\u7528 sha1`, "FILE_MISSING");
27856
+ }
27857
+ async function waitAnalyze(sha1, typeCode, apis, now = Date.now, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
27858
+ const filesListInfo2 = apis.filesListInfo ?? ((params) => service_API_default.Storage.filesListInfo(params));
27859
+ const started = now();
27860
+ while (now() - started <= ANALYZE_TIMEOUT_MS) {
27861
+ const info2 = unwrapFirst(await filesListInfo2({ sha1, resourceTypeCode: typeCode }));
27862
+ const status = Number(info2.metaAnalyzeStatus ?? info2.status);
27863
+ if (status === 2) {
27864
+ return info2;
27865
+ }
27866
+ if (status === 3) {
27867
+ throw new CliError("\u5C5E\u6027\u89E3\u6790\u5931\u8D25", "FILE_ANALYZE_FAILED");
27868
+ }
27869
+ if (now() - started > ANALYZE_TIMEOUT_MS) {
27870
+ break;
27871
+ }
27872
+ await sleep(ANALYZE_POLL_INTERVAL_MS);
27873
+ }
27874
+ throw new CliError("\u5C5E\u6027\u89E3\u6790\u8D85\u65F6", "FILE_ANALYZE_TIMEOUT");
27875
+ }
27876
+ function fingerprintFile(uploadPath) {
27877
+ const stats = statSync3(uploadPath);
27878
+ if (!stats.isFile()) {
27879
+ throw new CliError("\u4EA7\u7269\u5FC5\u987B\u662F\u53EF\u8BFB\u53D6\u7684\u6587\u4EF6", "FILE_ARTIFACT_UNSUPPORTED");
27880
+ }
27881
+ return { size: stats.size, mtimeMs: stats.mtimeMs, ctimeMs: stats.ctimeMs };
27882
+ }
27883
+ function assertFileUnchanged(uploadPath, expected) {
27884
+ const actual = fingerprintFile(uploadPath);
27885
+ if (actual.size !== expected.size || actual.mtimeMs !== expected.mtimeMs || actual.ctimeMs !== expected.ctimeMs) {
27886
+ throw new CliError(
27887
+ "\u53D1\u5E03\u671F\u95F4\u672C\u5730\u4EA7\u7269\u53D1\u751F\u53D8\u5316\uFF1B\u8BF7\u4FDD\u6301\u4EA7\u7269\u7A33\u5B9A\u540E\u91CD\u8BD5",
27888
+ "FILE_CHANGED_DURING_PUBLISH"
27889
+ );
27890
+ }
27891
+ }
27892
+ async function sha1OfFile(uploadPath) {
27893
+ const fingerprint = fingerprintFile(uploadPath);
27894
+ const hash = createHash3("sha1");
27895
+ await new Promise((resolve, reject) => {
27896
+ const stream4 = createReadStream(uploadPath);
27897
+ stream4.on("data", (chunk) => {
27898
+ hash.update(chunk);
27899
+ });
27900
+ stream4.once("error", reject);
27901
+ stream4.once("end", resolve);
27902
+ });
27903
+ assertFileUnchanged(uploadPath, fingerprint);
27904
+ return { sha1: hash.digest("hex"), fingerprint };
27905
+ }
27906
+ async function uploadIfNew(uploadPath, sha1, fingerprint, typeCode, apis) {
27907
+ const fileIsExist2 = apis.fileIsExist ?? ((params) => service_API_default.Storage.fileIsExist(params));
27908
+ const exists2 = unwrapFirst(await fileIsExist2({ sha1 }));
27909
+ if (exists2.isExisting || exists2.exist || exists2.data === true) {
27910
+ assertFileUnchanged(uploadPath, fingerprint);
27911
+ return;
27912
+ }
27913
+ const upload = apis.uploadFile ?? ((params) => service_API_default.Storage.uploadFile(params));
27914
+ assertFileUnchanged(uploadPath, fingerprint);
27915
+ const file = await openAsBlob(uploadPath);
27916
+ await upload({ file, resourceType: typeCode });
27917
+ assertFileUnchanged(uploadPath, fingerprint);
27918
+ }
27919
+ function removeTempZip(uploadPath, localPath) {
27920
+ if (uploadPath === localPath || !existsSync15(uploadPath)) {
27921
+ return;
27922
+ }
27923
+ try {
27924
+ unlinkSync7(uploadPath);
27925
+ } catch {
27926
+ }
27927
+ }
27928
+ function writeSha1ToDraft(input2, uploaded, analysis) {
27929
+ const draft = readDraft(input2.cwd, input2.identity.n) ?? {
27930
+ baseUpcastResources: [],
27931
+ authExcludedItems: []
27932
+ };
27933
+ const hasMetadata = Array.isArray(analysis.metaInfoArray);
27934
+ const metadata = hasMetadata ? analysis.metaInfoArray : [];
27935
+ const additionalKeys = new Set(
26866
27936
  metadata.filter((item) => Boolean(item) && typeof item === "object").filter((item) => Number(item.insertMode) === 2).map((item) => String(item.key ?? "")).filter(Boolean)
26867
27937
  );
26868
27938
  const previousAttrs = draft.inputAttrs ?? [];
27939
+ const previousOrphans = draft.orphanedInputAttrs ?? [];
26869
27940
  const reuseCurrentAnalysis = !hasMetadata && draft.fileSha1 === uploaded.fileSha1 && draft.analyzedSha1 === uploaded.fileSha1;
26870
- const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? "")));
27941
+ const retainedAttrs = reuseCurrentAnalysis ? previousAttrs : [
27942
+ ...previousAttrs.filter((item) => additionalKeys.has(String(item.key ?? ""))),
27943
+ // 新分析重新出现同 key 时,恢复用户上次保留的值;它不应永远卡在待复核队列。
27944
+ ...previousOrphans.filter((item) => additionalKeys.has(String(item.key ?? "")) && !previousAttrs.some((current) => additionalKeys.has(String(current.key ?? "")) && current.key === item.key))
27945
+ ];
26871
27946
  const newlyOrphaned = reuseCurrentAnalysis ? [] : previousAttrs.filter((item) => !additionalKeys.has(String(item.key ?? "")));
27947
+ const unresolvedOrphans = reuseCurrentAnalysis ? previousOrphans : previousOrphans.filter((item) => !additionalKeys.has(String(item.key ?? "")));
26872
27948
  draft.fileSha1 = uploaded.fileSha1;
26873
27949
  draft.filename = uploaded.filename;
26874
27950
  draft.analyzedSha1 = uploaded.fileSha1;
26875
27951
  draft.inputAttrs = retainedAttrs;
26876
27952
  draft.orphanedInputAttrs = [
26877
- ...draft.orphanedInputAttrs ?? [],
26878
- ...newlyOrphaned.filter((item) => !(draft.orphanedInputAttrs ?? []).some((old) => old.key === item.key))
27953
+ ...unresolvedOrphans,
27954
+ ...newlyOrphaned.filter((item) => !unresolvedOrphans.some((old) => old.key === item.key))
26879
27955
  ];
26880
27956
  const nextDraft = prepareDraft(input2.cwd, input2.identity.n, draft);
26881
27957
  const changes = [{
@@ -26896,13 +27972,14 @@ async function uploadAndAnalyze(input2) {
26896
27972
  async function uploadAndAnalyzeLocked(input2) {
26897
27973
  assertPlatformAllowed();
26898
27974
  const file = input2.file !== void 0 ? normalizeProjectPath(input2.cwd, input2.file) : void 0;
26899
- const recordedFile = input2.identity.filePath ? normalizeProjectPath(input2.cwd, input2.identity.filePath) : void 0;
27975
+ const recordedFile = normalizeProjectPath(input2.cwd, input2.identity.filePath);
26900
27976
  const identity = recordedFile === input2.identity.filePath ? input2.identity : { ...input2.identity, filePath: recordedFile };
26901
27977
  const localPath = confirmLocalPath(identity, file, input2.yes, input2.cwd);
26902
- const uploadPath = await prepareUploadPath(identity.typeCode, localPath);
27978
+ const uploadPath = await prepareUploadPath(identity.typeCode, localPath, input2.cwd);
26903
27979
  try {
26904
- const uploaded = { fileSha1: sha1OfFile(uploadPath), filename: path13.basename(uploadPath) };
26905
- await uploadIfNew(uploadPath, uploaded.fileSha1, identity.typeCode, input2.apis ?? {});
27980
+ const hashed = await sha1OfFile(uploadPath);
27981
+ const uploaded = { fileSha1: hashed.sha1, filename: path16.basename(uploadPath) };
27982
+ await uploadIfNew(uploadPath, uploaded.fileSha1, hashed.fingerprint, identity.typeCode, input2.apis ?? {});
26906
27983
  const analysis = await waitAnalyze(
26907
27984
  uploaded.fileSha1,
26908
27985
  identity.typeCode,
@@ -26910,6 +27987,7 @@ async function uploadAndAnalyzeLocked(input2) {
26910
27987
  input2.now,
26911
27988
  input2.sleep
26912
27989
  );
27990
+ assertFileUnchanged(uploadPath, hashed.fingerprint);
26913
27991
  writeSha1ToDraft(
26914
27992
  {
26915
27993
  ...input2,
@@ -26944,7 +28022,7 @@ async function runCreateVersion(input2) {
26944
28022
  }
26945
28023
  async function runCreateVersionLocked(input2) {
26946
28024
  assertPlatformAllowed();
26947
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28025
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
26948
28026
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
26949
28027
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
26950
28028
  const info2 = unwrapData2(
@@ -26954,12 +28032,28 @@ async function runCreateVersionLocked(input2) {
26954
28032
  })
26955
28033
  );
26956
28034
  const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
28035
+ const existingDraft = readDraft(input2.cwd, identity.n);
28036
+ evaluateGates({ latestVersion, draft: input2.reset ? void 0 : existingDraft }, "create-version");
28037
+ const artifact = resolveArtifactPath(input2.cwd, identity, input2.file, input2.artifact);
28038
+ if (!input2.prepare && !input2.yes) {
28039
+ throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "CREATE_VERSION_NEED_YES");
28040
+ }
26957
28041
  if (input2.reset) {
28042
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28043
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
28044
+ }
28045
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
28046
+ let draft = existingDraft;
28047
+ if (input2.reset && existingDraft) {
28048
+ if (!input2.confirmReset) {
28049
+ throw new CliError("\u8BF7\u5148\u786E\u8BA4\u4E22\u5F03\u5DE5\u4F5C\u7A3F", "RESET_CONFIRMATION_REQUIRED");
28050
+ }
28051
+ if (!await input2.confirmReset(draftSummary(existingDraft))) {
28052
+ return "\u5DF2\u53D6\u6D88";
28053
+ }
26958
28054
  deleteDraft(input2.cwd, identity.n);
28055
+ draft = void 0;
26959
28056
  }
26960
- const draft = readDraft(input2.cwd, identity.n);
26961
- evaluateGates({ latestVersion, draft }, "create-version");
26962
- const artifact = resolveArtifactPath(input2.cwd, identity, input2.file, input2.artifact);
26963
28057
  if (!draft || input2.prepare || artifact) {
26964
28058
  writeDraft(input2.cwd, identity.n, draft ?? emptyDraft());
26965
28059
  }
@@ -26973,9 +28067,6 @@ async function runCreateVersionLocked(input2) {
26973
28067
  });
26974
28068
  return "\u5DF2\u5907\u7A3F\uFF0C\u672A\u63D0\u4EA4";
26975
28069
  }
26976
- if (!input2.yes) {
26977
- throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "CREATE_VERSION_NEED_YES");
26978
- }
26979
28070
  await uploadAndAnalyze({
26980
28071
  cwd: input2.cwd,
26981
28072
  identity,
@@ -26989,6 +28080,7 @@ async function runCreateVersionLocked(input2) {
26989
28080
  isLoadLatestVersionInfo: 1
26990
28081
  })
26991
28082
  );
28083
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
26992
28084
  if (again.latestVersion) {
26993
28085
  throw new CliError(
26994
28086
  `\u8FD9\u4E2A\u8D44\u6E90\u5DF2\u7ECF\u6709\u53D1\u884C\u7248\u672C\u3002\u7EBF\u4E0A latest \u662F ${again.latestVersion}\uFF0C\u8BF7\u7528 update-version\u3002`,
@@ -26999,11 +28091,17 @@ async function runCreateVersionLocked(input2) {
26999
28091
  cwd: input2.cwd,
27000
28092
  identity,
27001
28093
  version: "1.0.0",
28094
+ homeDir: input2.homeDir,
27002
28095
  apis: input2.apis
27003
28096
  });
27004
28097
  return "1.0.0";
27005
28098
  }
27006
28099
 
28100
+ // src/commands/version/draftConfirmation.ts
28101
+ async function confirmDraftDestruction(input2) {
28102
+ return confirmDestructive(input2.summary, input2.action, input2.yes, isInteractive(), confirmQuestion);
28103
+ }
28104
+
27007
28105
  // src/commands/version/create-version.ts
27008
28106
  function createCreateVersionCommand() {
27009
28107
  const command = addSharedOptions(new Command20("create-version"));
@@ -27024,12 +28122,14 @@ function createCreateVersionCommand() {
27024
28122
  "\u672C\u6B21\u4E0A\u4F20\u5E76\u8BB0\u5F55\u7684\u672C\u5730\u6587\u4EF6\u6216\u76EE\u5F55"
27025
28123
  ).action(async function(options) {
27026
28124
  const shared = readSharedOptions(this);
28125
+ const cwd = resolveCwd(shared.cwd);
27027
28126
  const result = await runCreateVersion({
27028
- cwd: resolveCwd(shared.cwd),
28127
+ cwd,
27029
28128
  file: shared.file,
27030
28129
  artifact: options.artifact,
27031
28130
  prepare: options.prepare,
27032
28131
  reset: options.reset,
28132
+ confirmReset: options.reset ? (summary) => confirmDraftDestruction({ summary, yes: shared.yes, action: "\u4E22\u5F03\u5DE5\u4F5C\u7A3F\u5E76\u91CD\u7F6E\u9996\u7248" }) : void 0,
27033
28133
  yes: shared.yes
27034
28134
  });
27035
28135
  console.log(result);
@@ -27042,6 +28142,26 @@ import { Command as Command21 } from "commander";
27042
28142
 
27043
28143
  // src/domain/version/form/dep.ts
27044
28144
  import semver from "semver";
28145
+ async function assertCurrentResourceWritable(input2) {
28146
+ const auth = requireAuth({ cwd: input2.cwd });
28147
+ const resourceId = input2.identity.resourceId;
28148
+ if (!resourceId) {
28149
+ throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
28150
+ }
28151
+ const infoApi2 = input2.apis?.ownInfo ?? ((params) => service_API_default.Resource.info(params));
28152
+ const info2 = unwrapData3(await infoApi2({ resourceIdOrName: resourceId }));
28153
+ assertRemoteResourceWritable({
28154
+ info: info2,
28155
+ resourceId,
28156
+ authUserId: auth.userId,
28157
+ codes: {
28158
+ invalid: "DEP_CURRENT_INFO_INVALID",
28159
+ notOwner: "DEP_CURRENT_NOT_OWNER",
28160
+ frozen: "DEP_CURRENT_RESOURCE_FROZEN"
28161
+ }
28162
+ });
28163
+ return resourceId;
28164
+ }
27045
28165
  function unwrapData3(result) {
27046
28166
  const envelope = result;
27047
28167
  const data = envelope.data ?? result;
@@ -27079,6 +28199,64 @@ function statusLabel(status) {
27079
28199
  }
27080
28200
  return void 0;
27081
28201
  }
28202
+ async function loadDependencyTarget(input2) {
28203
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28204
+ const info2 = unwrapData3(
28205
+ await infoApi2({
28206
+ resourceIdOrName: input2.resourceIdOrName,
28207
+ isLoadLatestVersionInfo: 1,
28208
+ isLoadPolicyInfo: 1
28209
+ })
28210
+ );
28211
+ const resourceId = typeof info2.resourceId === "string" ? info2.resourceId.trim() : "";
28212
+ if (!resourceId || input2.expectedResourceId && resourceId !== input2.expectedResourceId) {
28213
+ throw new CliError("\u5BF9\u65B9\u8D44\u6E90\u4FE1\u606F\u4E0D\u5B8C\u6574\u6216\u4E0E\u8981\u4FEE\u6539\u7684\u4F9D\u8D56\u4E0D\u4E00\u81F4", "DEP_TARGET_INVALID");
28214
+ }
28215
+ const resourceName = typeof info2.resourceName === "string" ? info2.resourceName : typeof info2.name === "string" ? info2.name : "";
28216
+ if (input2.identity.resourceId === resourceId || input2.identity.name && resourceName === input2.identity.name) {
28217
+ throw new CliError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1", "DEP_SELF");
28218
+ }
28219
+ if (Number(info2.subjectType) !== 1) {
28220
+ throw new CliError("\u5BF9\u65B9\u4E0D\u662F\u666E\u901A\u5355\u8D44\u6E90\uFF0C\u4E0D\u52A0", "DEP_NOT_RESOURCE");
28221
+ }
28222
+ const latestVersion = typeof info2.latestVersion === "string" ? info2.latestVersion.trim() : "";
28223
+ if (!latestVersion || !semver.valid(latestVersion)) {
28224
+ throw new CliError("\u5BF9\u65B9\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C", "DEP_NO_VERSION");
28225
+ }
28226
+ if (info2.status === void 0 || !Number.isFinite(Number(info2.status))) {
28227
+ throw new CliError("\u5BF9\u65B9\u8D44\u6E90\u4FE1\u606F\u4E0D\u5B8C\u6574\u6216\u72B6\u6001\u4E0D\u53EF\u7528", "DEP_TARGET_INVALID");
28228
+ }
28229
+ const blocked = statusLabel(Number(info2.status));
28230
+ if (blocked) {
28231
+ throw new CliError(blocked, "DEP_TARGET_STATUS");
28232
+ }
28233
+ if (Array.isArray(info2.baseUpcastResources) && info2.baseUpcastResources.length > 0) {
28234
+ throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
28235
+ }
28236
+ return { info: info2, resourceId, latestVersion };
28237
+ }
28238
+ async function assertDependencyVersionRange(input2) {
28239
+ const listApi = input2.apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
28240
+ const list2 = unwrapData3(await listApi({ resourceId: input2.resourceId }));
28241
+ const rawVersions = list2.dataList ?? list2.list;
28242
+ if (!Array.isArray(rawVersions) || rawVersions.length === 0) {
28243
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28244
+ }
28245
+ const versions = [];
28246
+ for (const item of rawVersions) {
28247
+ const version = typeof item === "object" && item !== null && typeof item.version === "string" ? item.version : "";
28248
+ if (!version || !semver.valid(version)) {
28249
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28250
+ }
28251
+ versions.push(version);
28252
+ }
28253
+ if (!versions.some((version) => semver.eq(version, input2.latestVersion))) {
28254
+ throw new CliError("\u5BF9\u65B9\u7248\u672C\u5217\u8868\u4E0D\u53EF\u7528\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4F9D\u8D56\u8303\u56F4", "DEP_VERSION_LIST_INVALID");
28255
+ }
28256
+ if (!semver.validRange(input2.versionRange) || !semver.maxSatisfying(versions, input2.versionRange)) {
28257
+ throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
28258
+ }
28259
+ }
27082
28260
  function signablePolicies(info2) {
27083
28261
  const policies2 = info2.policies ?? [];
27084
28262
  return policies2.filter((item) => item.status === 1 && item.policyId).map((item) => ({ policyId: item.policyId, policyName: item.policyName }));
@@ -27119,49 +28297,26 @@ async function depAdd(input2) {
27119
28297
  }
27120
28298
  async function depAddLocked(input2) {
27121
28299
  assertPlatformAllowed();
27122
- const identity = resolveIdentity(input2.cwd, input2.file);
28300
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27123
28301
  const draft = readDraft(input2.cwd, identity.n) ?? {
27124
28302
  baseUpcastResources: [],
27125
28303
  authExcludedItems: []
27126
28304
  };
27127
- const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27128
- const info2 = unwrapData3(
27129
- await infoApi2({
27130
- resourceIdOrName: input2.resourceId,
27131
- isLoadLatestVersionInfo: 1,
27132
- isLoadPolicyInfo: 1
27133
- })
27134
- );
27135
- const targetId = String(info2.resourceId ?? input2.resourceId);
27136
- const targetName = String(info2.resourceName ?? info2.name ?? "");
27137
- if (identity.resourceId && (targetId === identity.resourceId || targetName === `${identity.name}`)) {
27138
- throw new CliError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1", "DEP_SELF");
27139
- }
27140
- if (info2.subjectType !== void 0 && Number(info2.subjectType) !== 1) {
27141
- throw new CliError("\u5BF9\u65B9\u4E0D\u662F\u666E\u901A\u5355\u8D44\u6E90\uFF0C\u4E0D\u52A0", "DEP_NOT_RESOURCE");
27142
- }
27143
- const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
27144
- if (!latestVersion) {
27145
- throw new CliError("\u5BF9\u65B9\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C", "DEP_NO_VERSION");
27146
- }
27147
- const status = Number(info2.status ?? 1);
27148
- const blocked = statusLabel(status);
27149
- if (blocked) {
27150
- throw new CliError(blocked, "DEP_TARGET_STATUS");
27151
- }
27152
- const upcast = info2.baseUpcastResources;
27153
- if (Array.isArray(upcast) && upcast.length > 0) {
27154
- throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
27155
- }
27156
- let versionRange = input2.versionRange ?? `^${latestVersion}`;
27157
- const listApi = input2.apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
27158
- const list2 = unwrapData3(await listApi({ resourceId: targetId }));
27159
- const versions = (list2.dataList ?? list2.list ?? []).map((item) => item.version).filter((item) => Boolean(item));
27160
- if (versions.length > 0) {
27161
- if (!semver.validRange(versionRange) || !semver.maxSatisfying(versions, versionRange)) {
27162
- throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
27163
- }
27164
- }
28305
+ const licenseeId = await assertCurrentResourceWritable({ cwd: input2.cwd, identity, apis: input2.apis });
28306
+ const target = await loadDependencyTarget({
28307
+ resourceIdOrName: input2.resourceId,
28308
+ identity,
28309
+ apis: input2.apis
28310
+ });
28311
+ const targetId = target.resourceId;
28312
+ const info2 = target.info;
28313
+ const versionRange = input2.versionRange ?? `^${target.latestVersion}`;
28314
+ await assertDependencyVersionRange({
28315
+ resourceId: targetId,
28316
+ latestVersion: target.latestVersion,
28317
+ versionRange,
28318
+ apis: input2.apis
28319
+ });
27165
28320
  if (identity.resourceId) {
27166
28321
  const cycleApi = input2.apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27167
28322
  const existing2 = (draft.dependencies ?? []).filter((item) => item.resourceId !== targetId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
@@ -27184,16 +28339,13 @@ async function depAddLocked(input2) {
27184
28339
  );
27185
28340
  if (!extractIsAuth(firstAuth, targetId)) {
27186
28341
  const policies2 = signablePolicies(info2);
27187
- if (!identity.resourceId) {
27188
- throw new CliError("\u8BF7\u5148 create \u6216 bind", "DEP_NEED_SHELL");
27189
- }
27190
28342
  const sign = input2.apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27191
28343
  await signWithSelectedPolicy({
27192
28344
  policies: policies2,
27193
28345
  policyId: input2.policyId,
27194
28346
  yes: input2.yes,
27195
28347
  targetId,
27196
- licenseeId: identity.resourceId,
28348
+ licenseeId,
27197
28349
  sign
27198
28350
  });
27199
28351
  }
@@ -27239,7 +28391,8 @@ async function depRange(cwd, resourceId, versionRange, file, apis, options) {
27239
28391
  return withProjectLock(cwd, () => depRangeLocked(cwd, resourceId, versionRange, file, apis, options), "version-dep-range");
27240
28392
  }
27241
28393
  async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options) {
27242
- const identity = resolveIdentity(cwd, file);
28394
+ assertPlatformAllowed();
28395
+ const identity = resolveBoundIdentity(cwd, file);
27243
28396
  const draft = readDraft(cwd, identity.n);
27244
28397
  if (!draft) {
27245
28398
  throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
@@ -27248,39 +28401,27 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27248
28401
  if (!found) {
27249
28402
  throw new CliError(`\u7A3F\u4E0A\u6CA1\u6709\u4F9D\u8D56 ${resourceId}`, "DEP_NOT_FOUND");
27250
28403
  }
27251
- const infoApi2 = apis?.info ?? ((params) => service_API_default.Resource.info(params));
27252
- const info2 = unwrapData3(
27253
- await infoApi2({
27254
- resourceIdOrName: resourceId,
27255
- isLoadLatestVersionInfo: 1,
27256
- isLoadPolicyInfo: 1
27257
- })
27258
- );
27259
- const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
27260
- const status = Number(info2.status ?? 1);
27261
- const blocked = statusLabel(status);
27262
- if (blocked) {
27263
- throw new CliError(blocked, "DEP_TARGET_STATUS");
27264
- }
27265
- const upcast = info2.baseUpcastResources;
27266
- if (Array.isArray(upcast) && upcast.length > 0) {
27267
- throw new CliError("\u5BF9\u65B9\u5B58\u5728\u57FA\u7840\u4E0A\u629B\uFF0C\u672C\u671F\u4E0D\u652F\u6301\u3002", "DEP_UPCAST");
27268
- }
27269
- const listApi = apis?.getVersionListByResourceID ?? ((params) => service_API_default.Resource.getVersionListByResourceID(params));
27270
- const list2 = unwrapData3(await listApi({ resourceId }));
27271
- const versions = (list2.dataList ?? list2.list ?? []).map((item) => item.version).filter((item) => Boolean(item));
27272
- if (versions.length > 0 && latestVersion) {
27273
- if (!semver.validRange(versionRange) || !semver.maxSatisfying(versions, versionRange)) {
27274
- throw new CliError("\u8FD9\u4E2A\u8303\u56F4\u5BF9\u4E0D\u4E0A\u5BF9\u65B9\u5DF2\u53D1\u884C\u7684\u7248\u672C", "DEP_RANGE");
27275
- }
27276
- }
28404
+ const licenseeId = await assertCurrentResourceWritable({ cwd, identity, apis });
28405
+ const target = await loadDependencyTarget({
28406
+ resourceIdOrName: resourceId,
28407
+ expectedResourceId: resourceId,
28408
+ identity,
28409
+ apis
28410
+ });
28411
+ await assertDependencyVersionRange({
28412
+ resourceId: target.resourceId,
28413
+ latestVersion: target.latestVersion,
28414
+ versionRange,
28415
+ apis
28416
+ });
28417
+ const info2 = target.info;
27277
28418
  if (identity.resourceId) {
27278
28419
  const cycleApi = apis?.cycleDependencyCheck ?? ((params) => service_API_default.Resource.cycleDependencyCheck(params));
27279
28420
  const existing = (draft.dependencies ?? []).filter((item) => item.resourceId !== resourceId).map((item) => ({ resourceId: String(item.resourceId), versionRange: String(item.versionRange ?? "*") }));
27280
28421
  const cycle = unwrapData3(
27281
28422
  await cycleApi({
27282
28423
  resourceId: identity.resourceId,
27283
- dependencies: [...existing, { resourceId, versionRange }]
28424
+ dependencies: [...existing, { resourceId: target.resourceId, versionRange }]
27284
28425
  })
27285
28426
  );
27286
28427
  if (cycle.ok === false || cycle.data === false) {
@@ -27288,22 +28429,22 @@ async function depRangeLocked(cwd, resourceId, versionRange, file, apis, options
27288
28429
  }
27289
28430
  }
27290
28431
  const batchAuth3 = apis?.batchAuth ?? ((params) => service_API_default.Resource.batchAuth(params));
27291
- const firstAuth = unwrapData3(await batchAuth3({ resourceIds: resourceId, versionRanges: versionRange }));
27292
- if (!extractIsAuth(firstAuth, resourceId) && identity.resourceId) {
28432
+ const firstAuth = unwrapData3(await batchAuth3({ resourceIds: target.resourceId, versionRanges: versionRange }));
28433
+ if (!extractIsAuth(firstAuth, target.resourceId) && identity.resourceId) {
27293
28434
  const policies2 = signablePolicies(info2);
27294
28435
  const sign = apis?.sign ?? ((params) => service_API_default.Contract.batchCreateContracts(params));
27295
28436
  await signWithSelectedPolicy({
27296
28437
  policies: policies2,
27297
28438
  policyId: options?.policyId,
27298
28439
  yes: options?.yes,
27299
- targetId: resourceId,
27300
- licenseeId: identity.resourceId,
28440
+ targetId: target.resourceId,
28441
+ licenseeId,
27301
28442
  sign
27302
28443
  });
27303
28444
  }
27304
28445
  found.versionRange = versionRange;
27305
28446
  writeDraft(cwd, identity.n, draft);
27306
- return `${resourceId}@${versionRange}`;
28447
+ return `${target.resourceId}@${versionRange}`;
27307
28448
  }
27308
28449
 
27309
28450
  // src/commands/version/dep.ts
@@ -27346,7 +28487,7 @@ import { Command as Command22 } from "commander";
27346
28487
  // src/domain/version/description.ts
27347
28488
  async function updateOnlineDescription(input2) {
27348
28489
  assertPlatformAllowed();
27349
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28490
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27350
28491
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27351
28492
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27352
28493
  const info2 = unwrapData2(
@@ -27355,6 +28496,7 @@ async function updateOnlineDescription(input2) {
27355
28496
  isLoadLatestVersionInfo: 1
27356
28497
  })
27357
28498
  );
28499
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
27358
28500
  if (!info2.latestVersion) {
27359
28501
  throw new CliError(
27360
28502
  "\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C\u3002\u8BF7\u5148\u6309\u53D1\u884C\u7248\u672C create-version \u63D0\u4EA4\u8D44\u6E90\u6587\u4EF6\u3002",
@@ -27393,370 +28535,190 @@ function createVersionDescriptionCommand() {
27393
28535
  }
27394
28536
  const version = await updateOnlineDescription({
27395
28537
  cwd: resolveCwd(shared.cwd),
27396
- file: shared.file,
27397
- version: options.version,
27398
- description: options.description
27399
- });
27400
- console.log(version);
27401
- });
27402
- return command;
27403
- }
27404
-
27405
- // src/domain/version/draftDescription.ts
27406
- function setDraftDescription(cwd, description, file) {
27407
- return withProjectLock(cwd, () => setDraftDescriptionLocked(cwd, description, file), "version-draft-description");
27408
- }
27409
- function setDraftDescriptionLocked(cwd, description, file) {
27410
- const identity = resolveIdentity(cwd, file);
27411
- const draft = readDraft(cwd, identity.n);
27412
- if (!draft) {
27413
- throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F", "DRAFT_MISSING");
27414
- }
27415
- if (!draft.fromVersion) {
27416
- throw new CliError("\u9996\u7248\u7A3F\u4E0D\u80FD\u6539\u63CF\u8FF0\uFF0C\u53D1\u884C\u540E\u7528 version description", "DRAFT_DESCRIPTION_FIRST");
27417
- }
27418
- draft.description = description;
27419
- writeDraft(cwd, identity.n, draft);
27420
- return description;
27421
- }
27422
-
27423
- // src/commands/version/description-draft.ts
27424
- function registerDraftDescription(draft) {
27425
- addSharedOptions(draft.command("description")).description(
27426
- // i18n: cli.command.version.draft.description_edit.description
27427
- "\u53EA\u6539\u5DE5\u4F5C\u7A3F\u63CF\u8FF0"
27428
- ).option(
27429
- "--description <text>",
27430
- // i18n: cli.command.version.draft.description_edit.text
27431
- "\u63CF\u8FF0"
27432
- ).action(function(options) {
27433
- const shared = readSharedOptions(this);
27434
- if (options.description === void 0) {
27435
- throw new CliError("\u8BF7\u63D0\u4F9B --description", "DRAFT_DESCRIPTION_REQUIRED");
27436
- }
27437
- console.log(setDraftDescription(resolveCwd(shared.cwd), options.description, shared.file));
27438
- });
27439
- }
27440
-
27441
- // src/commands/version/draft.ts
27442
- import { Command as Command23 } from "commander";
27443
-
27444
- // src/domain/version/draftDiscard.ts
27445
- function draftDiscard(cwd, file) {
27446
- return withProjectLock(cwd, () => draftDiscardLocked(cwd, file), "version-draft-discard");
27447
- }
27448
- function draftDiscardLocked(cwd, file) {
27449
- const identity = resolveIdentity(cwd, file);
27450
- const draft = readDraft(cwd, identity.n);
27451
- if (!draft) {
27452
- return "\u6CA1\u6709\u5DE5\u4F5C\u7A3F";
27453
- }
27454
- deleteDraft(cwd, identity.n);
27455
- return "\u5DF2\u4E22\u6389\u5DE5\u4F5C\u7A3F";
27456
- }
27457
-
27458
- // src/domain/version/draftPull.ts
27459
- async function draftPull(input2) {
27460
- return withProjectLock(input2.cwd, () => draftPullLocked(input2), "version-draft-pull");
27461
- }
27462
- async function draftPullLocked(input2) {
27463
- assertPlatformAllowed();
27464
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27465
- const identity = resolveBoundIdentity(input2.cwd, input2.file);
27466
- const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27467
- const info2 = unwrapData2(
27468
- await infoApi2({
27469
- resourceIdOrName: identity.resourceId,
27470
- isLoadLatestVersionInfo: 1
27471
- })
27472
- );
27473
- const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
27474
- const existing = readDraft(input2.cwd, identity.n);
27475
- evaluateGates({ latestVersion, draft: existing }, "draft-pull");
27476
- const want = input2.version ?? latestVersion;
27477
- if (!want) {
27478
- throw new CliError("\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C\uFF0C\u8BF7\u5148 create-version", "GATE_USE_CREATE");
27479
- }
27480
- if (existing) {
27481
- const summary = draftSummary(existing);
27482
- if (!input2.yes && existing.fromVersion === want) {
27483
- return `${summary}
27484
- \u7A3F\u5DF2\u6765\u81EA ${want}\uFF0C\u672A\u8986\u76D6`;
27485
- }
27486
- if (!input2.yes) {
27487
- return `${summary}
27488
- \u672A\u8986\u76D6`;
27489
- }
27490
- }
27491
- const versionInfoApi = input2.apis?.resourceVersionInfo1 ?? ((params) => service_API_default.Resource.resourceVersionInfo1(params));
27492
- const versionInfo = unwrapData2(
27493
- await versionInfoApi({
27494
- resourceId: identity.resourceId,
27495
- version: want
27496
- })
27497
- );
27498
- const pulledSha = versionInfo.fileSha1 ? String(versionInfo.fileSha1) : "";
27499
- if (!pulledSha) {
27500
- throw new CliError("\u6CA1\u6709\u8FD9\u4E2A\u7248\u672C", "DRAFT_VERSION_MISSING");
27501
- }
27502
- writeDraft(input2.cwd, identity.n, {
27503
- fromVersion: want,
27504
- fileSha1: pulledSha,
27505
- filename: versionInfo.filename ? String(versionInfo.filename) : void 0,
27506
- description: versionInfo.description ? String(versionInfo.description) : void 0,
27507
- customPropertyDescriptors: Array.isArray(versionInfo.customPropertyDescriptors) ? versionInfo.customPropertyDescriptors : void 0,
27508
- dependencies: Array.isArray(versionInfo.dependencies) ? versionInfo.dependencies.map((item) => ({
27509
- resourceId: String(item.resourceId),
27510
- versionRange: item.versionRange ? String(item.versionRange) : "*"
27511
- })) : void 0,
27512
- inputAttrs: Array.isArray(versionInfo.inputAttrs) ? versionInfo.inputAttrs : void 0,
27513
- baseUpcastResources: [],
27514
- authExcludedItems: []
27515
- });
27516
- return existing ? `${draftSummary(existing)}
27517
- \u5DF2\u8986\u76D6` : `\u5DF2\u62C9 ${want}`;
27518
- }
27519
-
27520
- // src/commands/version/draft.ts
27521
- function createVersionDraftCommand() {
27522
- const draft = addSharedOptions(new Command23("draft"));
27523
- draft.description(
27524
- // i18n: cli.command.version.draft.description
27525
- "\u7BA1\u672C\u5730\u5DE5\u4F5C\u7A3F"
27526
- );
27527
- addSharedOptions(draft.command("pull")).description(
27528
- // i18n: cli.command.version.draft.pull.description
27529
- "\u62C9\u5DF2\u53D1\u53F7\u5199\u5165\u5DE5\u4F5C\u7A3F"
27530
- ).option(
27531
- "--version <ver>",
27532
- // i18n: cli.command.version.draft.pull.version
27533
- "\u5DF2\u53D1\u53F7"
27534
- ).action(async function(options) {
27535
- const shared = readSharedOptions(this);
27536
- const text = await draftPull({
27537
- cwd: resolveCwd(shared.cwd),
27538
- file: shared.file,
27539
- version: options.version,
27540
- yes: shared.yes
27541
- });
27542
- console.log(text);
27543
- });
27544
- addSharedOptions(draft.command("discard")).description(
27545
- // i18n: cli.command.version.draft.discard.description
27546
- "\u4E22\u6389\u5DE5\u4F5C\u7A3F"
27547
- ).action(function() {
27548
- const shared = readSharedOptions(this);
27549
- console.log(draftDiscard(resolveCwd(shared.cwd), shared.file));
27550
- });
27551
- return draft;
27552
- }
27553
-
27554
- // src/commands/version/option.ts
27555
- import { Command as Command24 } from "commander";
27556
-
27557
- // src/domain/version/form/option.ts
27558
- var MAX_OPTION = 30;
27559
- var MAX_OPTION_ITEM = 30;
27560
- var MAX_TEXT_DEFAULT = 140;
27561
- var MAX_NAME2 = 50;
27562
- var MAX_REMARK2 = 50;
27563
- function assertValidName2(name) {
27564
- if (name.length > MAX_NAME2) {
27565
- throw new CliError("\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26", "OPTION_NAME_LONG");
27566
- }
27567
- }
27568
- function assertValidRemark2(remark) {
27569
- if (remark.length > MAX_REMARK2) {
27570
- throw new CliError("\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26\u3002", "OPTION_REMARK_LONG");
27571
- }
27572
- }
27573
- function assertOptionValueLength(value) {
27574
- if (value.length > MAX_TEXT_DEFAULT) {
27575
- throw new CliError("\u4E0D\u8D85\u8FC7140\u4E2A\u5B57\u7B26", "OPTION_VALUE_LONG");
27576
- }
27577
- }
27578
- function isOption(item) {
27579
- return item.type === "editableText" || item.type === "select";
27580
- }
27581
- async function assertTypeAllowsOption(input2) {
27582
- if (input2.supportOptionalConfig === true) return;
27583
- if (input2.supportOptionalConfig === false) {
27584
- throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27585
- }
27586
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27587
- const type = await getTypeInfo(input2.typeCode, input2.apis);
27588
- if (type.supportOptionalConfig !== 2) {
27589
- throw new CliError("\u5F53\u524D\u7C7B\u578B\u4E0D\u652F\u6301\u53EF\u9009\u914D\u7F6E", "OPTION_UNSUPPORTED");
27590
- }
27591
- }
27592
- function optionListFromDraft(draft) {
27593
- return (draft.customPropertyDescriptors ?? []).filter((item) => isOption(item));
27594
- }
27595
- function normalizeMode(mode) {
27596
- if (mode === void 0) return void 0;
27597
- if (mode === "\u4E0B\u62C9" || mode === "\u4E0B\u62C9\u5217\u8868" || mode === "select") return "select";
27598
- if (mode === "\u6587\u672C" || mode === "\u6587\u672C\u8F93\u5165\u6846" || mode === "input") return "editableText";
27599
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27600
- }
27601
- function parseCandidates(raw) {
27602
- const items = (raw ?? "").split("|").map((item) => item.trim()).filter(Boolean);
27603
- if (items.length === 0) throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
27604
- if (items.length > MAX_OPTION_ITEM) throw new CliError("\u9009\u9879\u4E2A\u6570\u4E0D\u80FD\u8D85\u8FC730\u9879", "OPTION_OPTIONS");
27605
- for (const item of items) assertOptionValueLength(item);
27606
- if (items.some((item, index) => items.indexOf(item) !== index)) {
27607
- throw new CliError("\u8BE5\u9009\u9879\u5DF2\u5B58\u5728", "OPTION_DUPLICATE_OPTION");
27608
- }
27609
- return items;
27610
- }
27611
- async function optionAdd(cwd, input2) {
27612
- return withProjectLock(cwd, () => optionAddLocked(cwd, input2), "version-option-add");
27613
- }
27614
- async function optionAddLocked(cwd, input2) {
27615
- const identity = resolveIdentity(cwd, input2.file);
27616
- await assertTypeAllowsOption({ cwd, typeCode: identity.typeCode, ...input2 });
27617
- const draft = readDraft(cwd, identity.n) ?? {
27618
- baseUpcastResources: [],
27619
- authExcludedItems: []
27620
- };
27621
- const parsed = parseLine(input2.line ?? "");
27622
- if (!parsed.name || !parsed.key) {
27623
- throw new CliError("\u53EF\u9009\u914D\u7F6E\u9700\u8981\u540D\u79F0\u548C\u952E", "OPTION_FIELDS");
27624
- }
27625
- assertValidKey(parsed.key);
27626
- const list2 = draft.customPropertyDescriptors ?? [];
27627
- if (optionListFromDraft(draft).length >= MAX_OPTION) {
27628
- throw new CliError("\u6700\u591A\u53EF\u6DFB\u52A030\u4E2A\u53EF\u9009\u914D\u7F6E", "OPTION_FULL");
27629
- }
27630
- if (list2.some((item) => item.key === parsed.key)) {
27631
- throw new CliError(`\u952E ${parsed.key} \u5DF2\u5B58\u5728`, "OPTION_DUPLICATE");
27632
- }
27633
- if (list2.some((item) => item.name === parsed.name)) {
27634
- throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27635
- }
27636
- const normalizedMode = normalizeMode(parsed.mode);
27637
- if (!normalizedMode) {
27638
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27639
- }
27640
- const isSelect = normalizedMode === "select";
27641
- const isText = normalizedMode === "editableText";
27642
- if (isSelect && parsed.defaultValue) {
27643
- 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");
27644
- }
27645
- if (isText && parsed.options) {
27646
- throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
27647
- }
27648
- if (!isSelect && !isText) {
27649
- throw new CliError("\u65B9\u5F0F\u53EA\u80FD\u662F\u6587\u672C\u6216\u4E0B\u62C9", "OPTION_MODE");
27650
- }
27651
- let type = "editableText";
27652
- let defaultValue = parsed.defaultValue ?? parsed.value ?? "";
27653
- let candidateItems;
27654
- assertValidName2(parsed.name);
27655
- assertValidRemark2(parsed.remark ?? "");
27656
- if (isSelect) {
27657
- const options = parseCandidates(parsed.options);
27658
- type = "select";
27659
- defaultValue = options[0] ?? "";
27660
- candidateItems = options;
27661
- } else {
27662
- assertOptionValueLength(defaultValue);
27663
- }
27664
- const preview = await confirmWrite(previewLine(parsed), input2.yes);
27665
- list2.push({
27666
- name: parsed.name,
27667
- key: parsed.key,
27668
- remark: parsed.remark ?? "",
27669
- defaultValue,
27670
- type,
27671
- ...candidateItems ? { candidateItems } : {}
28538
+ file: shared.file,
28539
+ version: options.version,
28540
+ description: options.description
28541
+ });
28542
+ console.log(version);
27672
28543
  });
27673
- draft.customPropertyDescriptors = list2;
27674
- writeDraft(cwd, identity.n, draft);
27675
- return preview;
28544
+ return command;
27676
28545
  }
27677
- async function optionSet(cwd, input2) {
27678
- return withProjectLock(cwd, () => optionSetLocked(cwd, input2), "version-option-set");
28546
+
28547
+ // src/domain/version/draftDescription.ts
28548
+ function setDraftDescription(cwd, description, file) {
28549
+ return withProjectLock(cwd, () => setDraftDescriptionLocked(cwd, description, file), "version-draft-description");
27679
28550
  }
27680
- async function optionSetLocked(cwd, input2) {
27681
- const identity = resolveIdentity(cwd, input2.file);
28551
+ function setDraftDescriptionLocked(cwd, description, file) {
28552
+ const identity = resolveIdentity(cwd, file);
27682
28553
  const draft = readDraft(cwd, identity.n);
27683
28554
  if (!draft) {
27684
- throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
27685
- }
27686
- const parsed = parseLine(input2.line ?? "");
27687
- const found = (draft.customPropertyDescriptors ?? []).find(
27688
- (item) => isOption(item) && (item.key === parsed.key || item.name === parsed.name)
27689
- );
27690
- if (!found) {
27691
- throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
28555
+ throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F", "DRAFT_MISSING");
27692
28556
  }
27693
- assertKeyUnchanged(String(found.key), parsed.key);
27694
- assertValidName2(parsed.name ?? String(found.name ?? ""));
27695
- assertValidRemark2(parsed.remark ?? String(found.remark ?? ""));
27696
- const nextType = normalizeMode(parsed.mode) ?? (String(found.type) === "select" ? "select" : "editableText");
27697
- const nextDefault = parsed.defaultValue ?? parsed.value;
27698
- if (nextType === "select" && nextDefault !== void 0) {
27699
- 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");
28557
+ if (!draft.fromVersion) {
28558
+ throw new CliError("\u9996\u7248\u7A3F\u4E0D\u80FD\u6539\u63CF\u8FF0\uFF0C\u53D1\u884C\u540E\u7528 version description", "DRAFT_DESCRIPTION_FIRST");
27700
28559
  }
27701
- if (nextType === "editableText") {
27702
- if (parsed.options !== void 0) {
27703
- throw new CliError("\u6587\u672C\u65B9\u5F0F\u4E0D\u8981\u5199\u9009\u9879", "OPTION_TEXT_OPTIONS");
28560
+ draft.description = description;
28561
+ writeDraft(cwd, identity.n, draft);
28562
+ return description;
28563
+ }
28564
+
28565
+ // src/commands/version/description-draft.ts
28566
+ function registerDraftDescription(draft) {
28567
+ addSharedOptions(draft.command("description")).description(
28568
+ // i18n: cli.command.version.draft.description_edit.description
28569
+ "\u53EA\u6539\u5DE5\u4F5C\u7A3F\u63CF\u8FF0"
28570
+ ).option(
28571
+ "--description <text>",
28572
+ // i18n: cli.command.version.draft.description_edit.text
28573
+ "\u63CF\u8FF0"
28574
+ ).action(function(options) {
28575
+ const shared = readSharedOptions(this);
28576
+ if (options.description === void 0) {
28577
+ throw new CliError("\u8BF7\u63D0\u4F9B --description", "DRAFT_DESCRIPTION_REQUIRED");
27704
28578
  }
27705
- if (nextDefault !== void 0) assertOptionValueLength(nextDefault);
27706
- }
27707
- if (parsed.name && (draft.customPropertyDescriptors ?? []).some((item) => item.name === parsed.name && item.key !== found.key)) {
27708
- throw new CliError("\u540D\u79F0\u5DF2\u5B58\u5728", "OPTION_NAME_DUPLICATE");
27709
- }
27710
- if (parsed.name) {
27711
- found.name = parsed.name;
27712
- }
27713
- if (parsed.remark !== void 0) {
27714
- found.remark = parsed.remark;
28579
+ console.log(setDraftDescription(resolveCwd(shared.cwd), options.description, shared.file));
28580
+ });
28581
+ }
28582
+
28583
+ // src/commands/version/draft.ts
28584
+ import { Command as Command23 } from "commander";
28585
+
28586
+ // src/domain/version/draftDiscard.ts
28587
+ async function draftDiscard(cwd, file, confirmed = false, confirm4) {
28588
+ return withProjectLock(cwd, () => draftDiscardLocked(cwd, file, confirmed, confirm4), "version-draft-discard");
28589
+ }
28590
+ async function draftDiscardLocked(cwd, file, confirmed, confirm4) {
28591
+ const identity = resolveIdentity(cwd, file);
28592
+ const draft = readDraft(cwd, identity.n);
28593
+ if (!draft) {
28594
+ return "\u6CA1\u6709\u5DE5\u4F5C\u7A3F";
27715
28595
  }
27716
- if (nextType === "select") {
27717
- const candidates = parsed.options !== void 0 ? parseCandidates(parsed.options) : Array.isArray(found.candidateItems) ? found.candidateItems.map(String) : [];
27718
- if (candidates.length === 0) {
27719
- throw new CliError("\u4E0B\u62C9\u81F3\u5C11\u9700\u8981 1 \u4E2A\u9009\u9879", "OPTION_OPTIONS");
28596
+ if (!confirmed) {
28597
+ if (!confirm4) {
28598
+ throw new CliError("\u8BF7\u5148\u786E\u8BA4\u4E22\u5F03\u5DE5\u4F5C\u7A3F", "DRAFT_DISCARD_CONFIRMATION_REQUIRED");
27720
28599
  }
27721
- found.type = "select";
27722
- found.candidateItems = candidates;
27723
- found.defaultValue = candidates[0];
27724
- } else {
27725
- found.type = "editableText";
27726
- delete found.candidateItems;
27727
- if (nextDefault !== void 0) {
27728
- found.defaultValue = nextDefault;
28600
+ if (!await confirm4(draftSummary(draft))) {
28601
+ return "\u5DF2\u53D6\u6D88";
27729
28602
  }
27730
28603
  }
27731
- const preview = await confirmWrite(previewLine(parsed), input2.yes);
27732
- writeDraft(cwd, identity.n, draft);
27733
- return preview;
28604
+ deleteDraft(cwd, identity.n);
28605
+ return "\u5DF2\u4E22\u6389\u5DE5\u4F5C\u7A3F";
27734
28606
  }
27735
- function optionRm(cwd, key, file) {
27736
- return withProjectLock(cwd, () => optionRmLocked(cwd, key, file), "version-option-rm");
28607
+
28608
+ // src/domain/version/draftPull.ts
28609
+ async function draftPull(input2) {
28610
+ return withProjectLock(input2.cwd, () => draftPullLocked(input2), "version-draft-pull");
27737
28611
  }
27738
- function optionRmLocked(cwd, key, file) {
27739
- const identity = resolveIdentity(cwd, file);
27740
- const draft = readDraft(cwd, identity.n);
27741
- if (!draft) {
27742
- throw new CliError("\u6CA1\u6709\u5DE5\u4F5C\u7A3F\uFF0C\u8BF7\u5148 version draft pull", "DRAFT_MISSING");
28612
+ async function draftPullLocked(input2) {
28613
+ assertPlatformAllowed();
28614
+ requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28615
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
28616
+ const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
28617
+ const info2 = unwrapData2(
28618
+ await infoApi2({
28619
+ resourceIdOrName: identity.resourceId,
28620
+ isLoadLatestVersionInfo: 1
28621
+ })
28622
+ );
28623
+ const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
28624
+ const existing = readDraft(input2.cwd, identity.n);
28625
+ evaluateGates({ latestVersion, draft: existing }, "draft-pull");
28626
+ const want = input2.version ?? latestVersion;
28627
+ if (!want) {
28628
+ throw new CliError("\u8FD8\u6CA1\u6709\u53D1\u884C\u7248\u672C\uFF0C\u8BF7\u5148 create-version", "GATE_USE_CREATE");
27743
28629
  }
27744
- const list2 = draft.customPropertyDescriptors ?? [];
27745
- const next = list2.filter((item) => !(isOption(item) && item.key === key));
27746
- if (next.length === list2.length) {
27747
- throw new CliError("\u627E\u4E0D\u5230\u8FD9\u6761\u53EF\u9009\u914D\u7F6E", "OPTION_NOT_FOUND");
28630
+ const versionInfoApi = input2.apis?.resourceVersionInfo1 ?? ((params) => service_API_default.Resource.resourceVersionInfo1(params));
28631
+ const versionInfo = unwrapData2(
28632
+ await versionInfoApi({
28633
+ resourceId: identity.resourceId,
28634
+ version: want
28635
+ })
28636
+ );
28637
+ const pulledSha = versionInfo.fileSha1 ? String(versionInfo.fileSha1) : "";
28638
+ if (!pulledSha) {
28639
+ throw new CliError("\u6CA1\u6709\u8FD9\u4E2A\u7248\u672C", "DRAFT_VERSION_MISSING");
27748
28640
  }
27749
- draft.customPropertyDescriptors = next;
27750
- writeDraft(cwd, identity.n, draft);
27751
- return key;
28641
+ if (existing && !input2.yes) {
28642
+ const summary = draftSummary(existing);
28643
+ if (!input2.confirmOverwrite) {
28644
+ return existing.fromVersion === want ? `${summary}
28645
+ \u7A3F\u5DF2\u6765\u81EA ${want}\uFF0C\u672A\u8986\u76D6` : `${summary}
28646
+ \u672A\u8986\u76D6`;
28647
+ }
28648
+ if (!await input2.confirmOverwrite(summary, want)) {
28649
+ return "\u5DF2\u53D6\u6D88";
28650
+ }
28651
+ }
28652
+ writeDraft(input2.cwd, identity.n, {
28653
+ fromVersion: want,
28654
+ fileSha1: pulledSha,
28655
+ filename: versionInfo.filename ? String(versionInfo.filename) : void 0,
28656
+ description: versionInfo.description ? String(versionInfo.description) : void 0,
28657
+ customPropertyDescriptors: Array.isArray(versionInfo.customPropertyDescriptors) ? versionInfo.customPropertyDescriptors : void 0,
28658
+ dependencies: Array.isArray(versionInfo.dependencies) ? versionInfo.dependencies.map((item) => ({
28659
+ resourceId: String(item.resourceId),
28660
+ versionRange: item.versionRange ? String(item.versionRange) : "*"
28661
+ })) : void 0,
28662
+ inputAttrs: Array.isArray(versionInfo.inputAttrs) ? versionInfo.inputAttrs : void 0,
28663
+ baseUpcastResources: [],
28664
+ authExcludedItems: []
28665
+ });
28666
+ return existing ? `${draftSummary(existing)}
28667
+ \u5DF2\u8986\u76D6` : `\u5DF2\u62C9 ${want}`;
27752
28668
  }
27753
- function optionList(cwd, file) {
27754
- const identity = resolveIdentity(cwd, file);
27755
- const draft = readDraft(cwd, identity.n);
27756
- return optionListFromDraft(draft ?? {}).map((item) => `${item.key}=${item.defaultValue ?? ""} ${item.name ?? ""}`).join("\n");
28669
+
28670
+ // src/commands/version/draft.ts
28671
+ function createVersionDraftCommand() {
28672
+ const draft = addSharedOptions(new Command23("draft"));
28673
+ draft.description(
28674
+ // i18n: cli.command.version.draft.description
28675
+ "\u7BA1\u672C\u5730\u5DE5\u4F5C\u7A3F"
28676
+ );
28677
+ addSharedOptions(draft.command("pull")).description(
28678
+ // i18n: cli.command.version.draft.pull.description
28679
+ "\u62C9\u5DF2\u53D1\u53F7\u5199\u5165\u5DE5\u4F5C\u7A3F"
28680
+ ).option(
28681
+ "--version <ver>",
28682
+ // i18n: cli.command.version.draft.pull.version
28683
+ "\u5DF2\u53D1\u53F7"
28684
+ ).action(async function(options) {
28685
+ const shared = readSharedOptions(this);
28686
+ const cwd = resolveCwd(shared.cwd);
28687
+ const text = await draftPull({
28688
+ cwd,
28689
+ file: shared.file,
28690
+ version: options.version,
28691
+ yes: shared.yes,
28692
+ confirmOverwrite: (summary, version) => confirmDraftDestruction({
28693
+ summary,
28694
+ yes: shared.yes,
28695
+ action: `\u7528\u7EBF\u4E0A\u7248\u672C ${version} \u8986\u76D6\u672C\u5730\u5DE5\u4F5C\u7A3F`
28696
+ })
28697
+ });
28698
+ console.log(text);
28699
+ });
28700
+ addSharedOptions(draft.command("discard")).description(
28701
+ // i18n: cli.command.version.draft.discard.description
28702
+ "\u4E22\u6389\u5DE5\u4F5C\u7A3F"
28703
+ ).action(async function() {
28704
+ const shared = readSharedOptions(this);
28705
+ const cwd = resolveCwd(shared.cwd);
28706
+ console.log(await draftDiscard(
28707
+ cwd,
28708
+ shared.file,
28709
+ shared.yes,
28710
+ (summary) => confirmDraftDestruction({
28711
+ summary,
28712
+ yes: shared.yes,
28713
+ action: "\u4E22\u5F03\u5DE5\u4F5C\u7A3F"
28714
+ })
28715
+ ));
28716
+ });
28717
+ return draft;
27757
28718
  }
27758
28719
 
27759
28720
  // src/commands/version/option.ts
28721
+ import { Command as Command24 } from "commander";
27760
28722
  function createVersionOptionCommand() {
27761
28723
  const option = addSharedOptions(new Command24("option"));
27762
28724
  option.description("\u6539\u7A3F\u4E0A\u7684\u53EF\u9009\u914D\u7F6E");
@@ -27791,6 +28753,16 @@ function createVersionOptionCommand() {
27791
28753
  import { Command as Command25 } from "commander";
27792
28754
 
27793
28755
  // src/domain/version/setPath.ts
28756
+ import { existsSync as existsSync16, statSync as statSync4 } from "fs";
28757
+ import path17 from "path";
28758
+ function sameOrNestedPath(parent, candidate) {
28759
+ const relative = path17.relative(parent, candidate);
28760
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path17.sep}`) && !path17.isAbsolute(relative);
28761
+ }
28762
+ function artifactsOverlap(first, second) {
28763
+ if (first.path === second.path) return true;
28764
+ return (first.isDirectory || second.isDirectory) && (sameOrNestedPath(first.path, second.path) || sameOrNestedPath(second.path, first.path));
28765
+ }
27794
28766
  function setIdentityFilePath(cwd, input2) {
27795
28767
  return withProjectLock(cwd, () => {
27796
28768
  const artifact = input2.artifact !== void 0 ? normalizeProjectPath(cwd, input2.artifact) : void 0;
@@ -27798,6 +28770,24 @@ function setIdentityFilePath(cwd, input2) {
27798
28770
  throw new CliError("\u8BF7\u63D0\u4F9B --artifact", "SET_ARTIFACT_REQUIRED");
27799
28771
  }
27800
28772
  const identity = resolveIdentity(cwd, input2.file);
28773
+ const absoluteArtifact = path17.resolve(cwd, artifact);
28774
+ assertArtifactAnchor(identity.typeCode, absoluteArtifact, cwd);
28775
+ const nextArtifact = {
28776
+ path: resolveExistingProjectPath(cwd, absoluteArtifact),
28777
+ isDirectory: statSync4(absoluteArtifact).isDirectory()
28778
+ };
28779
+ const occupant = validateLocalState(cwd).find((candidate) => {
28780
+ if (candidate.n === identity.n) return false;
28781
+ const candidatePath = path17.resolve(cwd, normalizeProjectPath(cwd, candidate.filePath));
28782
+ if (!existsSync16(candidatePath)) return false;
28783
+ return artifactsOverlap(nextArtifact, {
28784
+ path: resolveExistingProjectPath(cwd, candidatePath),
28785
+ isDirectory: statSync4(candidatePath).isDirectory()
28786
+ });
28787
+ });
28788
+ if (occupant) {
28789
+ throw new CliError(`\u4EA7\u7269\u8DEF\u5F84 ${artifact} \u5DF2\u88AB ${occupant.n}.json \u5360\u7528`, "SET_ARTIFACT_OCCUPIED");
28790
+ }
27801
28791
  const updated = prepareIdentityUpdate(cwd, identity.n, { filePath: artifact });
27802
28792
  commitLocalTransaction(cwd, [
27803
28793
  { path: identityFilePath(cwd, updated.n), content: serializeIdentity(updated) }
@@ -27846,10 +28836,11 @@ function showLocal(cwd, file) {
27846
28836
  async function showOnline(input2) {
27847
28837
  assertPlatformAllowed();
27848
28838
  requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27849
- const identity = resolveIdentity(input2.cwd, input2.file);
27850
- if (!identity.resourceId) {
28839
+ const local = resolveIdentity(input2.cwd, input2.file);
28840
+ if (!local.resourceId) {
27851
28841
  return "\u672C\u5730\u8FD8\u6CA1\u6709 resourceId";
27852
28842
  }
28843
+ const identity = resolveBoundIdentity(input2.cwd, input2.file);
27853
28844
  const draft = readDraft(input2.cwd, identity.n);
27854
28845
  const hint = draft ? "\u672C\u5730\u6709\u672A\u63D0\u4EA4\u7684\u7248\u672C\u5DE5\u4F5C\u7A3F\uFF0C\u67E5\u770B\u8BF7 version show --local\n" : "";
27855
28846
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
@@ -27922,7 +28913,7 @@ async function runUpdateVersion(input2) {
27922
28913
  }
27923
28914
  async function runUpdateVersionLocked(input2) {
27924
28915
  assertPlatformAllowed();
27925
- requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
28916
+ const auth = requireAuth({ cwd: input2.cwd, homeDir: input2.homeDir });
27926
28917
  const identity = resolveBoundIdentity(input2.cwd, input2.file);
27927
28918
  const infoApi2 = input2.apis?.info ?? ((params) => service_API_default.Resource.info(params));
27928
28919
  const info2 = unwrapData2(
@@ -27932,13 +28923,42 @@ async function runUpdateVersionLocked(input2) {
27932
28923
  })
27933
28924
  );
27934
28925
  const latestVersion = info2.latestVersion ? String(info2.latestVersion) : void 0;
28926
+ const existingDraft = readDraft(input2.cwd, identity.n);
28927
+ const source = input2.reuseVersion ?? latestVersion;
28928
+ evaluateGates({ latestVersion, draft: input2.reset ? void 0 : existingDraft, reuseVersion: input2.reuseVersion }, "update-version");
28929
+ const artifact = resolveArtifactPath(input2.cwd, identity, input2.file, input2.artifact);
28930
+ if (!input2.reset && input2.yes && existingDraft?.fromVersion && source && existingDraft.fromVersion !== source) {
28931
+ throw new CliError(
28932
+ `\u7A3F\u6765\u81EA ${existingDraft.fromVersion}\u3001\u5E95\u662F ${source}\u3002\u8BF7 version draft pull \u6216 --reuse-version`,
28933
+ "UPDATE_VERSION_MISMATCH"
28934
+ );
28935
+ }
28936
+ const next = resolveNextVersion(latestVersion, input2.version, input2.bump);
28937
+ if (!semver2.gt(next, latestVersion)) {
28938
+ throw new CliError(
28939
+ `\u7EBF\u4E0A\u5DF2\u7ECF\u662F ${latestVersion}\uFF0C\u4E0D\u80FD\u53D1 ${next}`,
28940
+ "UPDATE_VERSION_NOT_GREATER"
28941
+ );
28942
+ }
28943
+ if (!input2.yes) {
28944
+ throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "UPDATE_VERSION_NEED_YES");
28945
+ }
27935
28946
  if (input2.reset) {
28947
+ const localPath = confirmLocalPath(identity, artifact, input2.yes, input2.cwd);
28948
+ assertUploadArtifactReady(identity.typeCode, localPath, input2.cwd);
28949
+ }
28950
+ assertRemoteResourceWritable({ info: info2, resourceId: identity.resourceId, authUserId: auth.userId });
28951
+ let draft = existingDraft;
28952
+ if (input2.reset && existingDraft) {
28953
+ if (!input2.confirmReset) {
28954
+ throw new CliError("\u8BF7\u5148\u786E\u8BA4\u4E22\u5F03\u5DE5\u4F5C\u7A3F", "RESET_CONFIRMATION_REQUIRED");
28955
+ }
28956
+ if (!await input2.confirmReset(draftSummary(existingDraft))) {
28957
+ return "\u5DF2\u53D6\u6D88";
28958
+ }
27936
28959
  deleteDraft(input2.cwd, identity.n);
28960
+ draft = void 0;
27937
28961
  }
27938
- let draft = readDraft(input2.cwd, identity.n);
27939
- const source = input2.reuseVersion ?? latestVersion;
27940
- evaluateGates({ latestVersion, draft, reuseVersion: input2.reuseVersion }, "update-version");
27941
- const artifact = resolveArtifactPath(input2.cwd, identity, input2.file, input2.artifact);
27942
28962
  if (input2.yes && draft && !draft.fromVersion && input2.reuseVersion === void 0) {
27943
28963
  throw new CliError("\u8BF7\u5148 version draft pull --yes", "UPDATE_VERSION_FIRST_DRAFT");
27944
28964
  }
@@ -27958,16 +28978,6 @@ async function runUpdateVersionLocked(input2) {
27958
28978
  "UPDATE_VERSION_MISMATCH"
27959
28979
  );
27960
28980
  }
27961
- const next = resolveNextVersion(latestVersion, input2.version, input2.bump);
27962
- if (!semver2.gt(next, latestVersion)) {
27963
- throw new CliError(
27964
- `\u7EBF\u4E0A\u5DF2\u7ECF\u662F ${latestVersion}\uFF0C\u4E0D\u80FD\u53D1 ${next}`,
27965
- "UPDATE_VERSION_NOT_GREATER"
27966
- );
27967
- }
27968
- if (!input2.yes) {
27969
- throw new CliError("\u63D0\u4EA4\u8BF7\u52A0 --yes", "UPDATE_VERSION_NEED_YES");
27970
- }
27971
28981
  await uploadAndAnalyze({
27972
28982
  cwd: input2.cwd,
27973
28983
  identity,
@@ -27982,6 +28992,7 @@ async function runUpdateVersionLocked(input2) {
27982
28992
  isLoadLatestVersionInfo: 1
27983
28993
  })
27984
28994
  );
28995
+ assertRemoteResourceWritable({ info: again, resourceId: identity.resourceId, authUserId: auth.userId });
27985
28996
  const currentLatest = String(again.latestVersion ?? latestVersion);
27986
28997
  if (!semver2.gt(next, currentLatest)) {
27987
28998
  throw new CliError(
@@ -27993,6 +29004,7 @@ async function runUpdateVersionLocked(input2) {
27993
29004
  cwd: input2.cwd,
27994
29005
  identity,
27995
29006
  version: next,
29007
+ homeDir: input2.homeDir,
27996
29008
  apis: input2.apis
27997
29009
  });
27998
29010
  return next;
@@ -28044,14 +29056,16 @@ function createUpdateVersionCommand() {
28044
29056
  "\u672C\u6B21\u4E0A\u4F20\u5E76\u8BB0\u5F55\u7684\u672C\u5730\u6587\u4EF6\u6216\u76EE\u5F55"
28045
29057
  ).action(async function(options) {
28046
29058
  const shared = readSharedOptions(this);
29059
+ const cwd = resolveCwd(shared.cwd);
28047
29060
  const result = await runUpdateVersion({
28048
- cwd: resolveCwd(shared.cwd),
29061
+ cwd,
28049
29062
  file: shared.file,
28050
29063
  artifact: options.artifact,
28051
29064
  version: options.version,
28052
29065
  bump: options.bump,
28053
29066
  reuseVersion: options.reuseVersion,
28054
29067
  reset: options.reset,
29068
+ confirmReset: options.reset ? (summary) => confirmDraftDestruction({ summary, yes: shared.yes, action: "\u4E22\u5F03\u5DE5\u4F5C\u7A3F\u5E76\u91CD\u7F6E\u66F4\u65B0\u7248\u672C" }) : void 0,
28055
29069
  yes: shared.yes
28056
29070
  });
28057
29071
  console.log(result);
@@ -28112,26 +29126,26 @@ function createSubCommands() {
28112
29126
  }
28113
29127
 
28114
29128
  // src/core/usageDocs.ts
28115
- import { existsSync as existsSync12 } from "fs";
28116
- import path14 from "path";
29129
+ import { existsSync as existsSync17 } from "fs";
29130
+ import path18 from "path";
28117
29131
  import { fileURLToPath } from "url";
28118
29132
  function usageDocsPath() {
28119
- const moduleDir = path14.dirname(fileURLToPath(import.meta.url));
28120
- const bundledPath = path14.resolve(moduleDir, "../docs/README.md");
28121
- if (existsSync12(bundledPath)) {
29133
+ const moduleDir = path18.dirname(fileURLToPath(import.meta.url));
29134
+ const bundledPath = path18.resolve(moduleDir, "../docs/README.md");
29135
+ if (existsSync17(bundledPath)) {
28122
29136
  return bundledPath;
28123
29137
  }
28124
- return path14.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
29138
+ return path18.resolve(moduleDir, "../../../../docs/\u4E00\u671F/\u4EA7\u54C1\u65B9\u6848/\u4F7F\u7528/README.md");
28125
29139
  }
28126
29140
 
28127
29141
  // src/core/packageVersion.ts
28128
- import { readFileSync as readFileSync10 } from "fs";
28129
- import path15 from "path";
29142
+ import { readFileSync as readFileSync11 } from "fs";
29143
+ import path19 from "path";
28130
29144
  import { fileURLToPath as fileURLToPath2 } from "url";
28131
29145
  function packageVersion() {
28132
- const moduleDir = path15.dirname(fileURLToPath2(import.meta.url));
28133
- const packageJson = path15.resolve(moduleDir, "../..", "package.json");
28134
- const value = JSON.parse(readFileSync10(packageJson, "utf8"));
29146
+ const moduleDir = path19.dirname(fileURLToPath2(import.meta.url));
29147
+ const packageJson = path19.resolve(moduleDir, "../..", "package.json");
29148
+ const value = JSON.parse(readFileSync11(packageJson, "utf8"));
28135
29149
  return typeof value.version === "string" ? value.version : "unknown";
28136
29150
  }
28137
29151
 
@@ -28156,7 +29170,14 @@ function createProgram() {
28156
29170
  const cwdFlag = findOptionValue(actionCommand ?? thisCommand, "cwd");
28157
29171
  const cwd = resolveCwd(typeof cwdFlag === "string" ? cwdFlag : void 0);
28158
29172
  setAuthSearchCwd(cwd);
28159
- await chooseResourceWhenNeeded(actionCommand ?? thisCommand, cwd);
29173
+ const command = actionCommand ?? thisCommand;
29174
+ if (!isResourceFreeCommand(command)) {
29175
+ withProjectLock(cwd, () => void 0, "preselect-resource");
29176
+ }
29177
+ if (!isPendingOperationReadAllowed(command)) {
29178
+ withProjectLock(cwd, () => assertNoPendingOperation(cwd), "pending-operation-gate");
29179
+ }
29180
+ await chooseResourceWhenNeeded(command, cwd);
28160
29181
  });
28161
29182
  for (const command of Object.values(createSubCommands())) {
28162
29183
  program.addCommand(command);
@@ -28164,6 +29185,10 @@ function createProgram() {
28164
29185
  return program;
28165
29186
  }
28166
29187
  var RESOURCE_FREE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "init", "template", "type", "resource", "create", "bind"]);
29188
+ var PENDING_OPERATION_READ_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "template", "type", "status", "show", "validate", "list", "recover"]);
29189
+ function isPendingOperationReadAllowed(command) {
29190
+ return PENDING_OPERATION_READ_COMMANDS.has(command.name());
29191
+ }
28167
29192
  async function chooseResourceWhenNeeded(command, cwd) {
28168
29193
  if (isResourceFreeCommand(command)) return;
28169
29194
  const selector = findOptionValue(command, "resource");
@@ -28194,7 +29219,7 @@ function resourceChoiceLabel(cwd, identity) {
28194
29219
  return [
28195
29220
  `${identity.n}.json`,
28196
29221
  `\u6807\u9898=${identity.title ?? "\uFF08\u672A\u540C\u6B65\u6807\u9898\uFF09"}`,
28197
- `\u6807\u8BC6=${identity.name ?? "\u672A\u7ED1\u5B9A"}`,
29222
+ `\u6807\u8BC6=${identity.resourceName ?? identity.name ?? "\u672A\u7ED1\u5B9A"}`,
28198
29223
  `ID=${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}`,
28199
29224
  `\u7C7B\u578B=${identity.typeCode}`,
28200
29225
  `\u4EA7\u7269=${identity.filePath ?? "\u672A\u8BBE\u7F6E"}`,
@@ -28202,7 +29227,15 @@ function resourceChoiceLabel(cwd, identity) {
28202
29227
  ].join(" ");
28203
29228
  }
28204
29229
  function resourceSelectorExamples(identities) {
28205
- return identities.map((identity) => `file:${identity.n}.json\uFF08${identity.title ?? "\u672A\u540C\u6B65\u6807\u9898"}\uFF1B${identity.name ?? "\u672A\u7ED1\u5B9A"}\uFF1B${identity.resourceId ?? "\u672A\u7ED1\u5B9A"}\uFF09`).join("\u3001");
29230
+ return identities.map((identity) => {
29231
+ const selectors = [
29232
+ ...identity.resourceId ? [`id:${identity.resourceId}`] : [],
29233
+ ...identity.resourceName ? [`name:${identity.resourceName}`] : [],
29234
+ `artifact:${identity.filePath}`,
29235
+ `file:${identity.n}.json`
29236
+ ];
29237
+ return selectors.join(" | ");
29238
+ }).join("\uFF1B");
28206
29239
  }
28207
29240
  function isResourceFreeCommand(command) {
28208
29241
  let current = command;