ente-cli 0.4.91 → 0.4.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -545,3 +545,20 @@ Gmail applies native word/operator matching; arbitrary mid-name/address substrin
545
545
  and alias expansion are not guaranteed. IMAP applies native address-header
546
546
  substring matching with UIDVALIDITY-bound continuation. Neither source proves
547
547
  complete historical coverage; retain alternatives and ask on unresolved matches.
548
+
549
+ ### Typed entity fields
550
+
551
+ Entity contact and business values are typed field values. Use, for example:
552
+
553
+ ```sh
554
+ ente entity create "Jane Doe" --type person --space Personal --field '{"label":"E-Mail","type":"email","value":"jane@example.com"}'
555
+ ente entity update "Jane Doe" --field '{"label":"Job title","type":"job-title","value":"Architect"}'
556
+ ente entity field add "Jane Doe" --label "Mobilnummer" --type phone --value "+49 1512 3456789"
557
+ ente entity field update <valueId> --value "replacement"
558
+ ente entity field remove <valueId>
559
+ ```
560
+
561
+ `--field` repeats JSON objects. Omitted labels stay unchanged; a supplied label
562
+ replaces that field's values; `"value":null` removes them. Entity `--attr` is
563
+ retained only to report the moved-to-fields error. Activity/task/list attributes
564
+ are unchanged. Entity reads expose field/value IDs, labels, types and text.
package/dist/SKILL.md CHANGED
@@ -161,11 +161,14 @@ ente query "QM system export" [--space <name>] [--entity <name>] [--since <epoch
161
161
 
162
162
  # Entities
163
163
  ente entity list [--query <text>] [--type person|org|project|topic|place|object] [--space <name>] [--limit <count>] [--projection compact|full] [--archived] [--json]
164
- ente entity create "Jane Doe" --type person [--pin "Acme Corp"] [--share-parents --parent-space-grant-fingerprint <token>] [--alias "JD"] [--attr email=jane@acme.com] [--tag vip] [--description "CTO at Acme"] [--json]
164
+ ente entity create "Jane Doe" --type person [--pin "Acme Corp"] [--share-parents --parent-space-grant-fingerprint <token>] [--alias "JD"] [--field '{"label":"E-Mail","type":"email","value":"jane@acme.com"}'] [--tag vip] [--description "CTO at Acme"] [--json]
165
165
  ente entity create "Leistungsphase 1" --type topic --space "Studio" --parent "Building A" --exclusive-parent [--json]
166
166
  ente entity duplicate <name-or-id> --space <space> [--name "New project"] [--include-exclusive-children] [--json]
167
167
  ente entity get "Jane Doe" [--limit 20] [--hours-back <hours>] [--min-count 5] [--projection compact|full] [--json]
168
- ente entity update "Jane Doe" [--name "Jane A. Doe"] [--alias-add "Jane"] [--alias-remove "JD"] [--attr role=CEO] [--tag vip] [--description "…"] [--json]
168
+ ente entity update "Jane Doe" [--name "Jane A. Doe"] [--alias-add "Jane"] [--alias-remove "JD"] [--field '{"label":"Job title","type":"job-title","value":"CEO"}'] [--tag vip] [--description "…"] [--json]
169
+ ente entity field add "Jane Doe" --label "Mobilnummer" --type phone --value "+49 1512 3456789" [--json]
170
+ ente entity field update <valueId> --value "new text" [--json]
171
+ ente entity field remove <valueId> [--json]
169
172
  ente entity rename "Lorey Georg" "Georg Lorey" # change display name
170
173
  ente entity pin "Jane Doe" "Acme Corp" [--role "student"] [--share-parents --parent-space-grant-fingerprint <token>] [--json]
171
174
  ente entity unpin "Jane Doe" "Acme Corp" [--json]
@@ -744,3 +747,9 @@ Gmail uses native word/operator matching, without guaranteed arbitrary substring
744
747
  or alias expansion. IMAP uses native address-header substring search and checks
745
748
  UIDVALIDITY on continuation. Never claim complete historical coverage or send
746
749
  from a recipient lookup; disclose choices and ask on unresolved alternatives.
750
+
751
+ Entity values use typed `fields`; entity `--attr` and attribute payloads are legacy errors.
752
+ `entity update --field` merges only supplied labels. Repeat JSON objects to store
753
+ several values under one label; use `"value":null` to remove that label's values.
754
+ A field ID selects an existing definition; a new label is owned by the acting
755
+ space. Activity, task and list `--attr` options keep their existing behavior.
package/dist/cli.mjs CHANGED
@@ -3800,7 +3800,7 @@ async function runCli(cli2, argv, context) {
3800
3800
  }
3801
3801
 
3802
3802
  // ente/version.ts
3803
- var VERSION = "0.4.91";
3803
+ var VERSION = "0.4.92";
3804
3804
 
3805
3805
  // ente/branding.ts
3806
3806
  var NPM_REGISTRY = "https://registry.npmjs.org";
@@ -29755,16 +29755,45 @@ function isSourceCreatedAtInput(value) {
29755
29755
  return true;
29756
29756
  }
29757
29757
 
29758
+ // ../packages/shared/src/attribute-names.ts
29759
+ var STORABLE_ATTRIBUTE_KEY = /^[\x20-\x23\x25-\x7E][\x20-\x7E]{0,1023}$/;
29760
+ var ENCODED_PREFIX = "~";
29761
+ var MAX_STORAGE_KEY_LENGTH = 1024;
29762
+ var CONTROL_CHARACTER = /[\u0000-\u001F\u007F-\u009F]/;
29763
+ function isStorableAttributeKey(key) {
29764
+ return STORABLE_ATTRIBUTE_KEY.test(key);
29765
+ }
29766
+ function needsEncoding(name3) {
29767
+ return !isStorableAttributeKey(name3) || name3.startsWith(ENCODED_PREFIX);
29768
+ }
29769
+ function escapeCharacter(character) {
29770
+ if (character === "%") return "%25";
29771
+ if (character === "~") return "%7e";
29772
+ const code3 = character.codePointAt(0);
29773
+ if (code3 >= 32 && code3 <= 126) return character;
29774
+ return encodeURIComponent(character).toLowerCase();
29775
+ }
29776
+ function attributeStorageKey(name3) {
29777
+ if (name3.length === 0 || CONTROL_CHARACTER.test(name3)) return void 0;
29778
+ if (!needsEncoding(name3)) return name3;
29779
+ let key = ENCODED_PREFIX;
29780
+ try {
29781
+ for (const character of name3) key += escapeCharacter(character);
29782
+ } catch {
29783
+ return void 0;
29784
+ }
29785
+ return key.length <= MAX_STORAGE_KEY_LENGTH ? key : void 0;
29786
+ }
29787
+
29758
29788
  // ../packages/shared/src/attribute-keys.ts
29759
- var ATTRIBUTE_KEY_PATTERN = /^[\x20-\x23\x25-\x7E][\x20-\x7E]{0,1023}$/;
29789
+ var ATTRIBUTE_KEY_PATTERN = STORABLE_ATTRIBUTE_KEY;
29760
29790
  var ATTRIBUTE_KEY_RULE = "Attribute keys must use 1-1024 printable ASCII characters and must not start with $";
29761
29791
  var SHOWN_KEY_CHARACTERS = 64;
29762
- function isStorableAttributeKey(key) {
29763
- return ATTRIBUTE_KEY_PATTERN.test(key);
29792
+ function shownKey(key) {
29793
+ return key.length > SHOWN_KEY_CHARACTERS ? `${key.slice(0, SHOWN_KEY_CHARACTERS)}\u2026` : key;
29764
29794
  }
29765
29795
  function attributeKeyError(key) {
29766
- const shown = key.length > SHOWN_KEY_CHARACTERS ? `${key.slice(0, SHOWN_KEY_CHARACTERS)}\u2026` : key;
29767
- return `${ATTRIBUTE_KEY_RULE}: ${JSON.stringify(shown)}`;
29796
+ return `${ATTRIBUTE_KEY_RULE}: ${JSON.stringify(shownKey(key))}`;
29768
29797
  }
29769
29798
  function addedAttributeKeys(patch) {
29770
29799
  return Object.entries(patch).filter(([, value]) => value !== null).map(([key]) => key);
@@ -29776,6 +29805,11 @@ var ATTRIBUTE_STRING_PATCH_JSON_SCHEMA = {
29776
29805
  patternProperties: { [ATTRIBUTE_KEY_PATTERN.source]: NULLABLE_STRING_JSON_SCHEMA },
29777
29806
  additionalProperties: { type: "null" }
29778
29807
  };
29808
+ var ENTITY_ATTRIBUTE_NAME_RULE = "Entity attribute names must be nonempty, contain no control characters and encode to at most 1024 characters";
29809
+ var ENTITY_ATTRIBUTE_NAME_JSON_SCHEMA = { type: "string", minLength: 1, maxLength: 1024, pattern: "^[^\\u0000-\\u001F\\u007F-\\u009F]+$" };
29810
+ var entityAttributeName = external_exports.string().refine((name3) => attributeStorageKey(name3) !== void 0, {
29811
+ error: (issue2) => `${ENTITY_ATTRIBUTE_NAME_RULE}: ${JSON.stringify(shownKey(String(issue2.input)))}`
29812
+ }).meta(ENTITY_ATTRIBUTE_NAME_JSON_SCHEMA);
29779
29813
  var attributeKey = external_exports.string().regex(ATTRIBUTE_KEY_PATTERN, { error: (issue2) => attributeKeyError(String(issue2.input)) });
29780
29814
  var attributeStringPatch = external_exports.record(external_exports.string(), external_exports.string().nullable()).superRefine((patch, ctx) => {
29781
29815
  for (const key of addedAttributeKeys(patch))
@@ -31180,7 +31214,7 @@ function parseOperationInput(operation, schema, raw) {
31180
31214
  return parsed.data;
31181
31215
  }
31182
31216
  function attributeKeyDetail(issues) {
31183
- return issues.flatMap((issue2) => issue2.code === "invalid_key" ? issue2.issues : [issue2]).find((issue2) => issue2.message.startsWith(ATTRIBUTE_KEY_RULE))?.message;
31217
+ return issues.flatMap((issue2) => issue2.code === "invalid_key" ? issue2.issues : [issue2]).find((issue2) => issue2.message.startsWith(ATTRIBUTE_KEY_RULE) || issue2.message.startsWith(ENTITY_ATTRIBUTE_NAME_RULE))?.message;
31184
31218
  }
31185
31219
  function parseOperationNumberParameter(operation, field, raw) {
31186
31220
  if (!/^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?$/.test(raw) || !Number.isFinite(Number(raw)))
@@ -32843,6 +32877,34 @@ function parseAgentIconInput(raw) {
32843
32877
  return { icon: input2.icon };
32844
32878
  }
32845
32879
 
32880
+ // ../packages/shared/src/entity-emails.ts
32881
+ var EMAIL_LABELS = "work|private|personal|home|business|office";
32882
+ var ENTITY_EMAIL_KEY_RE = new RegExp(
32883
+ `^(?:e[-_ ]?mails?(?:[-_ ]?address(?:es)?)?(?:[-_ ]?\\d{1,2})?|(?:${EMAIL_LABELS})[-_ ]e[-_ ]?mail|e[-_ ]?mail[-_ ](?:${EMAIL_LABELS}))$`,
32884
+ "i"
32885
+ );
32886
+
32887
+ // ../packages/shared/src/unicode-case-fold.ts
32888
+ var exceptions = { "\xB5": "\u03BC", "\xDF": "ss", "\u0149": "\u02BCn", "\u017F": "s", "\u01F0": "j\u030C", "\u0345": "\u03B9", "\u0390": "\u03B9\u0308\u0301", "\u03B0": "\u03C5\u0308\u0301", "\u03C2": "\u03C3", "\u03D0": "\u03B2", "\u03D1": "\u03B8", "\u03D5": "\u03C6", "\u03D6": "\u03C0", "\u03F0": "\u03BA", "\u03F1": "\u03C1", "\u03F5": "\u03B5", "\u0587": "\u0565\u0582", "\u13F8": "\u13F0", "\u13F9": "\u13F1", "\u13FA": "\u13F2", "\u13FB": "\u13F3", "\u13FC": "\u13F4", "\u13FD": "\u13F5", "\u1C80": "\u0432", "\u1C81": "\u0434", "\u1C82": "\u043E", "\u1C83": "\u0441", "\u1C84": "\u0442", "\u1C85": "\u0442", "\u1C86": "\u044A", "\u1C87": "\u0463", "\u1C88": "\uA64B", "\u1E96": "h\u0331", "\u1E97": "t\u0308", "\u1E98": "w\u030A", "\u1E99": "y\u030A", "\u1E9A": "a\u02BE", "\u1E9B": "\u1E61", "\u1F50": "\u03C5\u0313", "\u1F52": "\u03C5\u0313\u0300", "\u1F54": "\u03C5\u0313\u0301", "\u1F56": "\u03C5\u0313\u0342", "\u1F80": "\u1F00\u03B9", "\u1F81": "\u1F01\u03B9", "\u1F82": "\u1F02\u03B9", "\u1F83": "\u1F03\u03B9", "\u1F84": "\u1F04\u03B9", "\u1F85": "\u1F05\u03B9", "\u1F86": "\u1F06\u03B9", "\u1F87": "\u1F07\u03B9", "\u1F90": "\u1F20\u03B9", "\u1F91": "\u1F21\u03B9", "\u1F92": "\u1F22\u03B9", "\u1F93": "\u1F23\u03B9", "\u1F94": "\u1F24\u03B9", "\u1F95": "\u1F25\u03B9", "\u1F96": "\u1F26\u03B9", "\u1F97": "\u1F27\u03B9", "\u1FA0": "\u1F60\u03B9", "\u1FA1": "\u1F61\u03B9", "\u1FA2": "\u1F62\u03B9", "\u1FA3": "\u1F63\u03B9", "\u1FA4": "\u1F64\u03B9", "\u1FA5": "\u1F65\u03B9", "\u1FA6": "\u1F66\u03B9", "\u1FA7": "\u1F67\u03B9", "\u1FB2": "\u1F70\u03B9", "\u1FB3": "\u03B1\u03B9", "\u1FB4": "\u03AC\u03B9", "\u1FB6": "\u03B1\u0342", "\u1FB7": "\u03B1\u0342\u03B9", "\u1FBE": "\u03B9", "\u1FC2": "\u1F74\u03B9", "\u1FC3": "\u03B7\u03B9", "\u1FC4": "\u03AE\u03B9", "\u1FC6": "\u03B7\u0342", "\u1FC7": "\u03B7\u0342\u03B9", "\u1FD2": "\u03B9\u0308\u0300", "\u1FD3": "\u03B9\u0308\u0301", "\u1FD6": "\u03B9\u0342", "\u1FD7": "\u03B9\u0308\u0342", "\u1FE2": "\u03C5\u0308\u0300", "\u1FE3": "\u03C5\u0308\u0301", "\u1FE4": "\u03C1\u0313", "\u1FE6": "\u03C5\u0342", "\u1FE7": "\u03C5\u0308\u0342", "\u1FF2": "\u1F7C\u03B9", "\u1FF3": "\u03C9\u03B9", "\u1FF4": "\u03CE\u03B9", "\u1FF6": "\u03C9\u0342", "\u1FF7": "\u03C9\u0342\u03B9", "\uAB70": "\u13A0", "\uAB71": "\u13A1", "\uAB72": "\u13A2", "\uAB73": "\u13A3", "\uAB74": "\u13A4", "\uAB75": "\u13A5", "\uAB76": "\u13A6", "\uAB77": "\u13A7", "\uAB78": "\u13A8", "\uAB79": "\u13A9", "\uAB7A": "\u13AA", "\uAB7B": "\u13AB", "\uAB7C": "\u13AC", "\uAB7D": "\u13AD", "\uAB7E": "\u13AE", "\uAB7F": "\u13AF", "\uAB80": "\u13B0", "\uAB81": "\u13B1", "\uAB82": "\u13B2", "\uAB83": "\u13B3", "\uAB84": "\u13B4", "\uAB85": "\u13B5", "\uAB86": "\u13B6", "\uAB87": "\u13B7", "\uAB88": "\u13B8", "\uAB89": "\u13B9", "\uAB8A": "\u13BA", "\uAB8B": "\u13BB", "\uAB8C": "\u13BC", "\uAB8D": "\u13BD", "\uAB8E": "\u13BE", "\uAB8F": "\u13BF", "\uAB90": "\u13C0", "\uAB91": "\u13C1", "\uAB92": "\u13C2", "\uAB93": "\u13C3", "\uAB94": "\u13C4", "\uAB95": "\u13C5", "\uAB96": "\u13C6", "\uAB97": "\u13C7", "\uAB98": "\u13C8", "\uAB99": "\u13C9", "\uAB9A": "\u13CA", "\uAB9B": "\u13CB", "\uAB9C": "\u13CC", "\uAB9D": "\u13CD", "\uAB9E": "\u13CE", "\uAB9F": "\u13CF", "\uABA0": "\u13D0", "\uABA1": "\u13D1", "\uABA2": "\u13D2", "\uABA3": "\u13D3", "\uABA4": "\u13D4", "\uABA5": "\u13D5", "\uABA6": "\u13D6", "\uABA7": "\u13D7", "\uABA8": "\u13D8", "\uABA9": "\u13D9", "\uABAA": "\u13DA", "\uABAB": "\u13DB", "\uABAC": "\u13DC", "\uABAD": "\u13DD", "\uABAE": "\u13DE", "\uABAF": "\u13DF", "\uABB0": "\u13E0", "\uABB1": "\u13E1", "\uABB2": "\u13E2", "\uABB3": "\u13E3", "\uABB4": "\u13E4", "\uABB5": "\u13E5", "\uABB6": "\u13E6", "\uABB7": "\u13E7", "\uABB8": "\u13E8", "\uABB9": "\u13E9", "\uABBA": "\u13EA", "\uABBB": "\u13EB", "\uABBC": "\u13EC", "\uABBD": "\u13ED", "\uABBE": "\u13EE", "\uABBF": "\u13EF", "\uFB00": "ff", "\uFB01": "fi", "\uFB02": "fl", "\uFB03": "ffi", "\uFB04": "ffl", "\uFB05": "st", "\uFB06": "st", "\uFB13": "\u0574\u0576", "\uFB14": "\u0574\u0565", "\uFB15": "\u0574\u056B", "\uFB16": "\u057E\u0576", "\uFB17": "\u0574\u056D" };
32889
+ function unicodeCaseFold(value) {
32890
+ return Array.from(value.toLowerCase(), (char) => exceptions[char] ?? char).join("");
32891
+ }
32892
+
32893
+ // ../packages/shared/src/entity-fields.ts
32894
+ var FIELD_TYPE_VALUES = ["text", "email", "phone", "url", "postal-address", "date", "salutation", "job-title", "json"];
32895
+ var fieldTypeValidator = v2.union(...FIELD_TYPE_VALUES.map((type) => v2.literal(type)));
32896
+ var fieldTypeSchema = external_exports.enum(FIELD_TYPE_VALUES);
32897
+ var MAX_ENTITY_FIELD_VALUES = 1e3;
32898
+ var MAX_FIELD_VALUE_BYTES = 16 * 1024;
32899
+ var MAX_FIELD_LABEL_CHARACTERS = 100;
32900
+ var LEGACY_ENTITY_ATTRIBUTES_ERROR = "Legacy entity attributes moved to fields. Update your client: use addFieldValue, updateFieldValue, removeFieldValue, or createEntity/updateEntity with fields.";
32901
+ function fieldLabelKey(label) {
32902
+ return unicodeCaseFold(label.normalize("NFKC")).replace(/[\s\p{Pd}_.]+/gu, "");
32903
+ }
32904
+ var fieldLabelSchema = external_exports.string().trim().refine((value) => [...value].length >= 1 && [...value].length <= MAX_FIELD_LABEL_CHARACTERS && !!fieldLabelKey(value), "Field labels must contain 1\u2013100 characters and more than separators");
32905
+ var fieldInputSchema = external_exports.strictObject({ label: fieldLabelSchema, type: fieldTypeSchema.optional(), value: external_exports.string().nullable() });
32906
+ var externalFieldValueSchema = external_exports.strictObject({ valueId: external_exports.string(), fieldId: external_exports.string(), label: external_exports.string(), type: fieldTypeSchema, value: external_exports.string() });
32907
+
32846
32908
  // ../packages/shared/src/read-result-dtos.ts
32847
32909
  var id7 = external_exports.string().min(1);
32848
32910
  var icon = external_exports.enum([.../* @__PURE__ */ new Set([...entityCustomIconIds, ...activityCustomIconIds, ...legacyCustomIconIds])]);
@@ -32858,7 +32920,11 @@ var entityReadFields = {
32858
32920
  customIconColor: external_exports.enum(customIconColorIds).optional(),
32859
32921
  exclusiveParent: external_exports.boolean().optional(),
32860
32922
  descriptionPlainText: external_exports.string().optional(),
32923
+ fields: external_exports.array(externalFieldValueSchema).optional(),
32861
32924
  attributes: external_exports.record(external_exports.string(), external_exports.json()).optional(),
32925
+ attributeEncodedKeys: external_exports.array(external_exports.string()).optional(),
32926
+ attributeLabels: external_exports.record(external_exports.string(), external_exports.string()).optional(),
32927
+ entityFieldsPhase: external_exports.enum(["legacy", "planning", "bridge"]).optional(),
32862
32928
  tags: external_exports.array(external_exports.string()).optional(),
32863
32929
  aliases: external_exports.array(external_exports.string()).optional(),
32864
32930
  createdBy: id7,
@@ -32867,7 +32933,18 @@ var entityReadFields = {
32867
32933
  archived: external_exports.boolean(),
32868
32934
  deletedAt: external_exports.number().optional()
32869
32935
  };
32870
- var publicEntityReadDto = external_exports.strictObject(entityReadFields);
32936
+ function withEntityReadPhase(schema) {
32937
+ return schema.superRefine((raw, ctx) => {
32938
+ const value = raw;
32939
+ const transitional = value.entityFieldsPhase !== void 0;
32940
+ if (transitional ? value.attributes === void 0 || value.fields !== void 0 : value.fields === void 0 || value.attributes !== void 0)
32941
+ ctx.addIssue({ code: "custom", message: "Entity read shape does not match its migration phase" });
32942
+ }).meta({ anyOf: [
32943
+ { type: "object", required: ["fields"], properties: { fields: { type: "array" }, attributes: { not: {} }, entityFieldsPhase: { not: {} } }, additionalProperties: true },
32944
+ { type: "object", required: ["attributes", "entityFieldsPhase"], properties: { attributes: { type: "object" }, entityFieldsPhase: { type: "string", enum: ["legacy", "planning", "bridge"] }, fields: { not: {} } }, additionalProperties: true }
32945
+ ] });
32946
+ }
32947
+ var publicEntityReadDto = withEntityReadPhase(external_exports.strictObject(entityReadFields));
32871
32948
  var activityReadFields = {
32872
32949
  _id: id7,
32873
32950
  title: external_exports.string().optional(),
@@ -32890,13 +32967,13 @@ var activityReadFields = {
32890
32967
  attachments: external_exports.array(external_exports.strictObject({ storageId: id7, name: external_exports.string(), mimeType: external_exports.string(), sizeBytes: external_exports.number().optional() })).optional()
32891
32968
  };
32892
32969
  var publicActivityReadDto = external_exports.strictObject(activityReadFields);
32893
- var selectPublicEntityRead = external_exports.object(entityReadFields);
32970
+ var selectPublicEntityRead = withEntityReadPhase(external_exports.object(entityReadFields));
32894
32971
  var selectPublicActivityRead = external_exports.object(activityReadFields);
32895
32972
 
32896
32973
  // ../packages/shared/src/entity-read-operation-results.ts
32897
32974
  var id8 = external_exports.string().min(1);
32898
32975
  var entityReadDocument = publicEntityReadDto;
32899
- var entityReadRichDocument = publicEntityReadDto.extend({ displayName: external_exports.string(), parentNames: external_exports.array(external_exports.string()), parentDetails: external_exports.array(readParentDetail) });
32976
+ var entityReadRichDocument = withEntityReadPhase(external_exports.strictObject({ ...publicEntityReadDto.shape, displayName: external_exports.string(), parentNames: external_exports.array(external_exports.string()), parentDetails: external_exports.array(readParentDetail) }));
32900
32977
  var entityReadActivity = publicActivityReadDto;
32901
32978
  var parentDetail = readParentDetail;
32902
32979
  var space = external_exports.strictObject({ spaceId: id8, spaceName: external_exports.string(), memberEmails: external_exports.array(external_exports.string()), scope: external_exports.string().optional() });
@@ -32910,10 +32987,10 @@ var entityReadFullSearch = external_exports.strictObject({ entities: external_ex
32910
32987
  var entityReadFullDetail = external_exports.strictObject({ entity: entityReadRichDocument, pins: external_exports.array(pin), children: external_exports.array(child), childrenTruncated: external_exports.boolean(), aggregationMode: external_exports.enum(["includeDirectChildren", "directOnly"]), activities: external_exports.array(entityReadActivity), openTasks: external_exports.array(task), spaces: external_exports.array(space), descriptionPlainText: external_exports.string().optional() }).nullable();
32911
32988
  var selectSpace = external_exports.object(space.shape);
32912
32989
  var selectList = external_exports.object({ ...entityReadFullList.shape, entities: external_exports.array(external_exports.object({ entity: selectPublicEntityRead, ...matchFields })), spaces: external_exports.record(external_exports.string(), selectSpace) });
32913
- var selectRichEntity = selectPublicEntityRead.extend({ displayName: external_exports.string(), parentNames: external_exports.array(external_exports.string()), parentDetails: external_exports.array(readParentDetail) });
32990
+ var selectRichEntity = withEntityReadPhase(external_exports.object({ ...selectPublicEntityRead.shape, displayName: external_exports.string(), parentNames: external_exports.array(external_exports.string()), parentDetails: external_exports.array(readParentDetail) }));
32914
32991
  var selectSearch = external_exports.object({ ...entityReadFullSearch.shape, entities: external_exports.array(external_exports.object({ entity: selectRichEntity, ...matchFields, pins: external_exports.array(pin), matchedViaParent: external_exports.strictObject({ entityId: id8, name: external_exports.string() }).optional() })), spaces: external_exports.record(external_exports.string(), selectSpace) });
32915
32992
  var selectDetail = external_exports.object({ ...entityReadFullDetail.unwrap().shape, entity: selectRichEntity, activities: external_exports.array(selectPublicActivityRead), spaces: external_exports.array(selectSpace) }).nullable();
32916
- var compactEntity = entityReadRichDocument.pick({ _id: true, name: true, displayName: true, parentNames: true, parentDetails: true, type: true, descriptionPlainText: true, tags: true, aliases: true, lastActivityAt: true }).partial({ displayName: true, parentNames: true, parentDetails: true });
32993
+ var compactEntity = withEntityReadPhase(external_exports.strictObject(entityReadRichDocument.shape).pick({ _id: true, name: true, displayName: true, parentNames: true, parentDetails: true, type: true, descriptionPlainText: true, tags: true, aliases: true, lastActivityAt: true, fields: true, attributes: true, entityFieldsPhase: true }).partial({ displayName: true, parentNames: true, parentDetails: true }));
32917
32994
  var compactActivity = entityReadActivity.pick({ _id: true, shortId: true, title: true, type: true, contentPlainText: true, deadline: true, completed: true, tags: true, createdAt: true, updatedAt: true, spaceId: true });
32918
32995
  var compactList = entityReadFullList.extend({ entities: external_exports.array(external_exports.strictObject({ entity: compactEntity, ...matchFields })), spaces: external_exports.record(external_exports.string(), compactSpace) });
32919
32996
  var compactSearch = entityReadFullSearch.extend({ entities: external_exports.array(external_exports.strictObject({ entity: compactEntity, ...matchFields, pins: external_exports.array(pin), matchedViaParent: external_exports.strictObject({ entityId: id8, name: external_exports.string() }).optional() })), spaces: external_exports.record(external_exports.string(), compactSpace) });
@@ -32960,7 +33037,8 @@ var ENTITY_CREATE_INPUT_SCHEMA = {
32960
33037
  spaceId: { type: "string", minLength: 1 },
32961
33038
  description: { type: "string" },
32962
33039
  tags: { type: "array", items: { type: "string" } },
32963
- attributes: { type: "object", propertyNames: ATTRIBUTE_KEY_JSON_SCHEMA },
33040
+ fields: { type: "array", maxItems: MAX_ENTITY_FIELD_VALUES, items: { type: "object", properties: { label: { type: "string" }, type: { type: "string", enum: FIELD_TYPE_VALUES }, value: { anyOf: [{ type: "string" }, { type: "null" }] } }, required: ["label", "value"], additionalProperties: false } },
33041
+ attributes: { description: LEGACY_ENTITY_ATTRIBUTES_ERROR },
32964
33042
  aliases: { type: "array", items: { type: "string" } },
32965
33043
  ...ENTITY_PARENT_INPUT_PROPERTIES
32966
33044
  },
@@ -32972,6 +33050,7 @@ function isEntityType(value) {
32972
33050
  }
32973
33051
  var invalid = (message2) => ({ ok: false, message: message2 });
32974
33052
  function parseEntityCreateInput(args, name3 = "entities_create") {
33053
+ if (Object.hasOwn(args, "attributes")) return invalid(LEGACY_ENTITY_ATTRIBUTES_ERROR);
32975
33054
  if (args.parentEntityId !== void 0 && (typeof args.parentEntityId !== "string" || !args.parentEntityId.trim()))
32976
33055
  return invalid("parentEntityId must be a non-empty string");
32977
33056
  if (args.exclusiveParent !== void 0 && typeof args.exclusiveParent !== "boolean") return invalid("exclusiveParent must be a boolean");
@@ -32980,7 +33059,9 @@ function parseEntityCreateInput(args, name3 = "entities_create") {
32980
33059
  if (args.type !== void 0 && !isEntityType(args.type)) return invalid(`${name3} type must be one of: ${ENTITY_TYPE_VALUES.join(", ")}`);
32981
33060
  const tags = optionalStringArray(args.tags), aliases = optionalStringArray(args.aliases);
32982
33061
  const description = typeof args.description === "string" ? args.description : void 0;
32983
- const attributes2 = isInputRecord(args.attributes) ? args.attributes : void 0;
33062
+ const fieldResult = fieldInputSchema.array().safeParse(args.fields);
33063
+ if (args.fields !== void 0 && !fieldResult.success) return invalid("fields must contain label, optional type, and value");
33064
+ const fields3 = fieldResult.success ? fieldResult.data : void 0;
32984
33065
  const spaceIds = optionalStringArray(args.spaceIds);
32985
33066
  return { ok: true, value: {
32986
33067
  name: args.name.trim(),
@@ -32989,21 +33070,24 @@ function parseEntityCreateInput(args, name3 = "entities_create") {
32989
33070
  ...isEntityType(args.type) ? { type: args.type } : {},
32990
33071
  ...description !== void 0 ? { description } : {},
32991
33072
  ...tags ? { tags } : {},
32992
- ...attributes2 ? { attributes: attributes2 } : {},
33073
+ ...fields3 ? { fields: fields3 } : {},
32993
33074
  ...aliases ? { aliases } : {},
32994
33075
  ...typeof args.spaceId === "string" && args.spaceId ? { spaceId: args.spaceId } : {},
32995
33076
  ...spaceIds?.length ? { spaceIds } : {}
32996
33077
  } };
32997
33078
  }
32998
33079
  function parseEntityUpdateInput(args, name3 = "entities_update") {
33080
+ if (Object.hasOwn(args, "attributes")) return invalid(LEGACY_ENTITY_ATTRIBUTES_ERROR);
32999
33081
  if (typeof args.entityId !== "string") return invalid(`${name3} requires string entityId`);
33000
33082
  const tags = optionalStringArray(args.tags), aliases = optionalStringArray(args.aliases);
33001
- const attributes2 = isInputRecord(args.attributes) ? args.attributes : void 0;
33083
+ const fieldResult = fieldInputSchema.array().safeParse(args.fields);
33084
+ if (args.fields !== void 0 && !fieldResult.success) return invalid("fields must contain label, optional type, and value");
33085
+ const fields3 = fieldResult.success ? fieldResult.data : void 0;
33002
33086
  return { ok: true, value: {
33003
33087
  entityId: args.entityId,
33004
33088
  ...typeof args.name === "string" ? { name: args.name } : {},
33005
33089
  ...tags ? { tags } : {},
33006
- ...attributes2 ? { attributes: attributes2 } : {},
33090
+ ...fields3 ? { fields: fields3 } : {},
33007
33091
  ...typeof args.description === "string" ? { description: args.description } : {},
33008
33092
  ...aliases ? { aliases } : {}
33009
33093
  } };
@@ -33012,13 +33096,16 @@ function parseEntityUpdateInput(args, name3 = "entities_update") {
33012
33096
  // ../packages/shared/src/entity-write-operations.ts
33013
33097
  var id9 = external_exports.string().min(1).max(128).refine((value) => value.trim() === value, "Copy IDs unchanged");
33014
33098
  var name2 = external_exports.string().trim().min(1);
33015
- var fields = { name: name2.optional(), description: external_exports.string().optional(), tags: external_exports.array(external_exports.string()).optional(), attributes: external_exports.record(attributeKey, external_exports.json()).optional(), aliases: external_exports.array(external_exports.string()).optional() };
33099
+ var fields = { name: name2.optional(), description: external_exports.string().optional(), tags: external_exports.array(external_exports.string()).optional(), fields: external_exports.array(fieldInputSchema).max(MAX_ENTITY_FIELD_VALUES).optional(), attributes: external_exports.unknown().optional(), aliases: external_exports.array(external_exports.string()).optional() };
33016
33100
  var create = external_exports.strictObject({ ...fields, name: name2, type: external_exports.enum(ENTITY_TYPE_VALUES).default("person"), spaceId: id9.optional(), spaceIds: external_exports.array(id9).min(1).optional(), parentEntityId: id9.optional(), exclusiveParent: external_exports.boolean().optional() }).refine((value) => value.spaceId !== void 0 || value.spaceIds !== void 0, { path: ["spaceId"], message: "Select spaceId or nonempty spaceIds" }).refine((value) => !value.exclusiveParent || value.parentEntityId !== void 0, { path: ["parentEntityId"], message: "exclusiveParent requires parentEntityId" });
33017
33101
  var entityWriteInputs = {
33018
33102
  createEntity: create,
33019
33103
  updateEntity: external_exports.strictObject({ entityId: id9, spaceId: id9.optional(), ...fields }),
33020
33104
  updateEntityName: external_exports.strictObject({ entityId: id9, spaceId: id9.optional(), name: name2 }),
33021
33105
  duplicateEntity: external_exports.strictObject({ entityId: id9, spaceId: id9, name: name2.optional(), includeExclusiveChildren: external_exports.boolean().default(false) }),
33106
+ addFieldValue: external_exports.strictObject({ entityId: id9, fieldId: id9.optional(), label: fieldLabelSchema.optional(), type: fieldTypeSchema.optional(), value: external_exports.string() }).refine((value) => !!value.fieldId !== !!value.label && (!value.fieldId || value.type === void 0), { message: "Select fieldId, or label with optional type" }),
33107
+ updateFieldValue: external_exports.strictObject({ valueId: id9, value: external_exports.string() }),
33108
+ removeFieldValue: external_exports.strictObject({ valueId: id9 }),
33022
33109
  archiveEntity: external_exports.strictObject({ entityId: id9 }),
33023
33110
  restoreEntity: external_exports.strictObject({ entityId: id9, grantMissingParentAccess: external_exports.boolean().optional(), parentSpaceGrantFingerprint: external_exports.string().trim().min(1).optional() })
33024
33111
  };
@@ -33026,6 +33113,7 @@ function parserMismatch(operation) {
33026
33113
  throw new OperationInputError(operation, [{ path: [], reason: "custom" }]);
33027
33114
  }
33028
33115
  function parseEntityWriteRequest(operation, raw) {
33116
+ rejectLegacyEntityAttributes(operation, raw);
33029
33117
  switch (operation) {
33030
33118
  case "createEntity": {
33031
33119
  const input2 = parseOperationInput(operation, entityWriteInputs.createEntity, raw);
@@ -33043,6 +33131,12 @@ function parseEntityWriteRequest(operation, raw) {
33043
33131
  if (!parseEntityUpdateInput(input2, operation).ok) return parserMismatch(operation);
33044
33132
  return { operation, input: input2 };
33045
33133
  }
33134
+ case "addFieldValue":
33135
+ return { operation, input: parseOperationInput(operation, entityWriteInputs.addFieldValue, raw) };
33136
+ case "updateFieldValue":
33137
+ return { operation, input: parseOperationInput(operation, entityWriteInputs.updateFieldValue, raw) };
33138
+ case "removeFieldValue":
33139
+ return { operation, input: parseOperationInput(operation, entityWriteInputs.removeFieldValue, raw) };
33046
33140
  case "duplicateEntity":
33047
33141
  return { operation, input: parseOperationInput(operation, entityWriteInputs.duplicateEntity, raw) };
33048
33142
  case "archiveEntity":
@@ -33051,6 +33145,13 @@ function parseEntityWriteRequest(operation, raw) {
33051
33145
  return { operation, input: parseOperationInput(operation, entityWriteInputs.restoreEntity, raw) };
33052
33146
  }
33053
33147
  }
33148
+ function rejectLegacyEntityAttributes(operation, raw) {
33149
+ if (raw && typeof raw === "object" && Object.hasOwn(raw, "attributes")) {
33150
+ const error62 = new OperationInputError(operation, [{ path: ["attributes"], reason: "custom" }]);
33151
+ error62.message = LEGACY_ENTITY_ATTRIBUTES_ERROR;
33152
+ throw error62;
33153
+ }
33154
+ }
33054
33155
 
33055
33156
  // ../packages/shared/src/entity-duplication.ts
33056
33157
  var MAX_DUPLICATED_ENTITIES = 100;
@@ -33065,7 +33166,10 @@ var entityWriteResults = {
33065
33166
  updateEntityName: success2,
33066
33167
  duplicateEntity: external_exports.strictObject({ entityId, duplicatedCount: external_exports.number().int().min(1).max(MAX_DUPLICATED_ENTITIES) }),
33067
33168
  archiveEntity: success2,
33068
- restoreEntity: success2
33169
+ restoreEntity: success2,
33170
+ addFieldValue: external_exports.strictObject({ valueId: entityId, added: external_exports.boolean() }),
33171
+ updateFieldValue: success2,
33172
+ removeFieldValue: success2
33069
33173
  };
33070
33174
  var nativeEntityWriteResults = {
33071
33175
  ...entityWriteResults,
@@ -33074,6 +33178,9 @@ var nativeEntityWriteResults = {
33074
33178
  updateEntityName: entityWriteResults.updateEntityName.extend({ message: external_exports.string() })
33075
33179
  };
33076
33180
  var publicCliEntityWriteResults = {
33181
+ addFieldValue: entityWriteResults.addFieldValue,
33182
+ updateFieldValue: success2,
33183
+ removeFieldValue: success2,
33077
33184
  createEntity: entityWriteResults.createEntity.extend({ pinFailures: external_exports.array(external_exports.strictObject({ target: external_exports.string(), error: external_exports.string(), outcome: external_exports.enum(["rejected", "unknown", "committed-unvalidated"]).optional() })).optional(), pinSucceeded: external_exports.array(external_exports.string()).optional() }),
33078
33185
  duplicateEntity: entityWriteResults.duplicateEntity,
33079
33186
  updateEntity: success2.extend({ entityId }),
@@ -33098,6 +33205,8 @@ function projectEntityWriteResult(operation, raw) {
33098
33205
  return checked2(entityWriteResults.createEntity, { entityId: checked2(entityId, raw) });
33099
33206
  case "duplicateEntity":
33100
33207
  return checked2(entityWriteResults.duplicateEntity, raw);
33208
+ case "addFieldValue":
33209
+ return checked2(entityWriteResults.addFieldValue, raw);
33101
33210
  default:
33102
33211
  if (raw !== null) throw new OperationResultError(operation, "Invalid entity write result");
33103
33212
  return checked2(success2, { success: true });
@@ -33137,7 +33246,7 @@ var selectDetail2 = external_exports.object({
33137
33246
  }).nullable();
33138
33247
  var compactActivity2 = publicActivityRecordDto.pick({ _id: true, shortId: true, title: true, type: true, contentPlainText: true, deadline: true, completed: true, tags: true, createdAt: true, updatedAt: true, spaceId: true, createdBy: true, deletedAt: true });
33139
33248
  var compactDetail2 = publicActivityDetailDto.omit({ attributes: true, attachments: true }).extend({
33140
- entityDetails: external_exports.array(publicEntityReadDto.pick({ _id: true, name: true, type: true, displayName: true, parentNames: true, parentDetails: true }).nullable())
33249
+ entityDetails: external_exports.array(external_exports.strictObject(publicEntityReadDto.shape).pick({ _id: true, name: true, type: true, displayName: true, parentNames: true, parentDetails: true }).nullable())
33141
33250
  });
33142
33251
  var pageFields = { hasMore: external_exports.boolean(), nextCursor: external_exports.string().optional() };
33143
33252
  var activityReadFullPage = external_exports.strictObject({ activities: external_exports.array(publicActivityRecordDto), ...pageFields });
@@ -33883,7 +33992,8 @@ var EnteClient = class _EnteClient {
33883
33992
  return this.rest.post("/api/external/entities", body);
33884
33993
  }
33885
33994
  updateEntity(id17, body) {
33886
- return this.rest.patch("/api/external/entities", body, { id: id17 });
33995
+ const input2 = parseEntityWriteRequest("updateEntity", body !== null && typeof body === "object" && !Array.isArray(body) ? { ...body, entityId: id17 } : body).input;
33996
+ return this.rest.patch("/api/external/entities", input2);
33887
33997
  }
33888
33998
  archiveEntity(id17) {
33889
33999
  return this.rest.delete("/api/external/entities", { id: id17 });
@@ -34240,9 +34350,8 @@ function formatEntityDetail(data) {
34240
34350
  if (e.hiddenFromGraph) lines.push("Graph: hidden");
34241
34351
  if (e.aliases && e.aliases.length) lines.push(`Aliases: ${e.aliases.join(", ")}`);
34242
34352
  if (e.tags && e.tags.length) lines.push(`Tags: ${e.tags.join(", ")}`);
34243
- if (e.attributes && Object.keys(e.attributes).length) {
34244
- for (const [k, v3] of Object.entries(e.attributes)) lines.push(` ${k}: ${String(v3)}`);
34245
- }
34353
+ if (e.entityFieldsPhase) for (const [label, value] of Object.entries(e.attributes ?? {})) lines.push(` ${label}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
34354
+ for (const field of e.fields ?? []) lines.push(` ${field.label} (${field.type}): ${field.value} [${field.valueId}]`);
34246
34355
  if (data.descriptionPlainText) {
34247
34356
  lines.push("");
34248
34357
  lines.push(truncate(data.descriptionPlainText, 200));
@@ -36355,7 +36464,6 @@ var entitiesAddAlias = makeFunctionReference(
36355
36464
  var entitiesRemoveAlias = makeFunctionReference(
36356
36465
  "entities:removeAlias"
36357
36466
  );
36358
- var entitiesSetAttribute = makeFunctionReference("entities:setAttribute");
36359
36467
  var entitiesSetDescriptionFromMarkdown = makeFunctionReference("entities:setDescriptionFromMarkdown");
36360
36468
  var entitiesDeleteEntity = makeFunctionReference(
36361
36469
  "entities:deleteEntity"
@@ -36409,7 +36517,7 @@ var EntityList = class extends ClientCommand {
36409
36517
  });
36410
36518
  }
36411
36519
  };
36412
- var EntityCreate = class extends ProfileCommand {
36520
+ var EntityCreate = class extends ClientCommand {
36413
36521
  static paths = [["entity", "create"]];
36414
36522
  static usage = Command.Usage({ category: "Entity", description: "Create a new entity" });
36415
36523
  name = options_exports.String();
@@ -36425,15 +36533,17 @@ var EntityCreate = class extends ProfileCommand {
36425
36533
  description: "Exact grantFingerprint returned by the confirmation-required error"
36426
36534
  });
36427
36535
  alias = options_exports.Array("--alias", { description: "Alternative names" });
36428
- attr = options_exports.Array("--attr", { description: "Attributes as key=value pairs" });
36536
+ attr = options_exports.Array("--attr", { description: "Legacy; use --field instead" });
36537
+ field = options_exports.Array("--field", { description: 'Repeat JSON {"label":"E-Mail","type":"email","value":"a@example.com"}' });
36429
36538
  tag = options_exports.Array("--tag", { description: "Tags (repeatable)" });
36430
36539
  description = options_exports.String("--description", { description: "Description text" });
36431
36540
  async run() {
36541
+ if (this.attr !== void 0) rejectLegacyEntityAttributes("createEntity", { attributes: this.attr });
36432
36542
  requireEntitySelector("createEntity", this.space, "spaceId");
36433
36543
  if (this.parent !== void 0) requireEntitySelector("createEntity", this.parent, "parentEntityId");
36434
36544
  const core = parseEntityWriteRequest("createEntity", { name: this.name, type: this.type, spaceId: "pending-space-resolution", parentEntityId: this.parent === void 0 ? void 0 : "pending-parent-resolution", exclusiveParent: this.exclusiveParent }).input;
36435
36545
  if (this.parent && this.pin?.length) throw new OperationInputError("createEntity", [{ path: ["parentEntityId"], reason: "custom" }]);
36436
- const attributes2 = parseOperationAttrs("createEntity", this.attr);
36546
+ const fields3 = parseCliFields("createEntity", this.field);
36437
36547
  const type = core.type;
36438
36548
  const caller = await resolveCaller();
36439
36549
  const spaceId = await resolveSpaceByNameC(caller, this.space);
@@ -36442,13 +36552,11 @@ var EntityCreate = class extends ProfileCommand {
36442
36552
  spaceId,
36443
36553
  name: core.name,
36444
36554
  type,
36555
+ fields: fields3,
36445
36556
  ...parentEntityId ? { parentEntityId, exclusiveParent: this.exclusiveParent } : {}
36446
36557
  }));
36447
36558
  if (this.alias) for (const a of this.alias) await caller.mutation(entitiesAddAlias, { id: entityId2, alias: a });
36448
36559
  if (this.tag) for (const t of this.tag) await caller.mutation(entitiesAddTag, { id: entityId2, tag: t });
36449
- for (const [key, value] of Object.entries(attributes2)) {
36450
- await caller.mutation(entitiesSetAttribute, { id: entityId2, key, value });
36451
- }
36452
36560
  if (this.description) {
36453
36561
  await caller.mutation(entitiesSetDescriptionFromMarkdown, { entityId: entityId2, markdown: this.description });
36454
36562
  }
@@ -36517,22 +36625,24 @@ var EntityGet = class extends ClientCommand {
36517
36625
  this.emitText(jsonWithSpaces({ data }, spaces), () => withFooter(formatEntityDetail({ ...data, activities: textActivities }), spaces));
36518
36626
  }
36519
36627
  };
36520
- var EntityUpdate = class extends ProfileCommand {
36628
+ var EntityUpdate = class extends ClientCommand {
36521
36629
  static paths = [["entity", "update"]];
36522
36630
  static usage = Command.Usage({ category: "Entity", description: "Update an entity" });
36523
36631
  nameOrId = options_exports.String();
36524
36632
  name = options_exports.String("--name", { description: "Rename the entity" });
36525
36633
  aliasAdd = options_exports.String("--alias-add", { description: "Add an alias" });
36526
36634
  aliasRemove = options_exports.String("--alias-remove", { description: "Remove an alias" });
36527
- attr = options_exports.Array("--attr", { description: "Set attributes as key=value pairs" });
36635
+ attr = options_exports.Array("--attr", { description: "Legacy; use --field instead" });
36636
+ field = options_exports.Array("--field", { description: 'Repeat JSON {"label":"E-Mail","type":"email","value":"a@example.com"}; null removes the label' });
36528
36637
  tag = options_exports.Array("--tag", { description: "Replace the tag set (repeatable)" });
36529
36638
  description = options_exports.String("--description", { description: "Update description" });
36530
36639
  async run() {
36640
+ if (this.attr !== void 0) rejectLegacyEntityAttributes("updateEntity", { attributes: this.attr });
36531
36641
  requireEntitySelector("updateEntity", this.nameOrId, "entityId");
36532
- const validated = parseEntityWriteRequest("updateEntity", { entityId: "pending-entity-resolution", name: this.name, description: this.description, tags: this.tag, attributes: parseOperationAttrs("updateEntity", this.attr) }).input;
36642
+ const validated = parseEntityWriteRequest("updateEntity", { entityId: "pending-entity-resolution", name: this.name, description: this.description, tags: this.tag, fields: parseCliFields("updateEntity", this.field) }).input;
36533
36643
  const caller = await resolveCaller();
36534
36644
  const entityId2 = await resolveEntityC(caller, this.nameOrId);
36535
- const newAttrs = validated.attributes ?? {};
36645
+ const fields3 = validated.fields;
36536
36646
  const needsCurrent = !!this.aliasAdd || !!this.aliasRemove || this.tag && this.tag.length > 0;
36537
36647
  const current = needsCurrent ? await caller.query(entitiesFindById, { id: entityId2 }) : void 0;
36538
36648
  if (needsCurrent && !current) throw new Error(`Entity not found: ${entityId2}`);
@@ -36549,10 +36659,8 @@ var EntityUpdate = class extends ProfileCommand {
36549
36659
  await caller.mutation(entitiesRemoveAlias, { id: entityId2, alias: this.aliasRemove });
36550
36660
  didSomething = true;
36551
36661
  }
36552
- if (Object.keys(newAttrs).length) {
36553
- for (const [key, value] of Object.entries(newAttrs)) {
36554
- await caller.mutation(entitiesSetAttribute, { id: entityId2, key, value });
36555
- }
36662
+ if (fields3?.length) {
36663
+ await this.client().entityWrite("updateEntity", { entityId: entityId2, fields: fields3 });
36556
36664
  didSomething = true;
36557
36665
  }
36558
36666
  if (this.tag && this.tag.length) {
@@ -36568,7 +36676,7 @@ var EntityUpdate = class extends ProfileCommand {
36568
36676
  }
36569
36677
  if (!didSomething) {
36570
36678
  throw new Error(
36571
- "Nothing to update. Specify --name, --alias-add, --alias-remove, --attr, --tag, or --description."
36679
+ "Nothing to update. Specify --name, --alias-add, --alias-remove, --field, --tag, or --description."
36572
36680
  );
36573
36681
  }
36574
36682
  const spaces = await spacesFooterFromMemberships(caller);
@@ -36853,10 +36961,63 @@ var EntityHtmlUpdate = class extends ProfileCommand {
36853
36961
  ));
36854
36962
  }
36855
36963
  };
36964
+ function parseCliFields(operation, values) {
36965
+ if (values === void 0) return void 0;
36966
+ return values.map((value, index) => {
36967
+ let raw;
36968
+ try {
36969
+ raw = JSON.parse(value);
36970
+ } catch {
36971
+ throw new OperationInputError(operation, [{ path: ["fields", String(index)], reason: "custom" }]);
36972
+ }
36973
+ const parsed = fieldInputSchema.safeParse(raw);
36974
+ if (!parsed.success) throw new OperationInputError(operation, [{ path: ["fields", String(index)], reason: "custom" }]);
36975
+ return parsed.data;
36976
+ });
36977
+ }
36978
+ var EntityFieldAdd = class extends ClientCommand {
36979
+ static paths = [["entity", "field", "add"]];
36980
+ static usage = Command.Usage({ category: "Entity", description: "Add a typed entity field value" });
36981
+ nameOrId = options_exports.String();
36982
+ fieldId = options_exports.String("--field-id", { description: "Existing field ID; alternative to --label" });
36983
+ label = options_exports.String("--label", { description: "Field label in the acting space" });
36984
+ type = options_exports.String("--type", { description: "Field type; inferred for a new label when omitted" });
36985
+ value = options_exports.String("--value", { required: true, description: "Value text" });
36986
+ async run() {
36987
+ requireEntitySelector("addFieldValue", this.nameOrId, "entityId");
36988
+ const request = parseEntityWriteRequest("addFieldValue", { entityId: "pending-entity-resolution", fieldId: this.fieldId, label: this.label, type: this.type, value: this.value });
36989
+ const client = this.client();
36990
+ const entityId2 = await resolveEntity(client, this.nameOrId);
36991
+ const result = await client.entityWrite("addFieldValue", { ...request.input, entityId: entityId2 });
36992
+ this.emitText(result, () => `Value ${result.data.valueId} ${result.data.added ? "added" : "already exists"}`);
36993
+ }
36994
+ };
36995
+ var EntityFieldUpdate = class extends ClientCommand {
36996
+ static paths = [["entity", "field", "update"]];
36997
+ static usage = Command.Usage({ category: "Entity", description: "Edit a typed entity field value" });
36998
+ valueId = options_exports.String();
36999
+ value = options_exports.String("--value", { required: true, description: "Replacement value text" });
37000
+ async run() {
37001
+ const result = await this.client().entityWrite("updateFieldValue", { valueId: this.valueId, value: this.value });
37002
+ this.emitText(result, () => "Field value updated");
37003
+ }
37004
+ };
37005
+ var EntityFieldRemove = class extends ClientCommand {
37006
+ static paths = [["entity", "field", "remove"]];
37007
+ static usage = Command.Usage({ category: "Entity", description: "Remove a typed entity field value" });
37008
+ valueId = options_exports.String();
37009
+ async run() {
37010
+ const result = await this.client().entityWrite("removeFieldValue", { valueId: this.valueId });
37011
+ this.emitText(result, () => "Field value removed");
37012
+ }
37013
+ };
36856
37014
  var ENTITY_COMMANDS = [
36857
37015
  EntityShowCorrection,
36858
37016
  EntityList,
36859
37017
  EntityCreate,
37018
+ EntityFieldAdd,
37019
+ EntityFieldUpdate,
37020
+ EntityFieldRemove,
36860
37021
  EntityDuplicate,
36861
37022
  EntityGet,
36862
37023
  EntityUpdate,