@aliyunrds/ctxdb 1.0.9-beta.1 → 1.0.9

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
@@ -653,6 +653,63 @@ Output semantics worth knowing:
653
653
  incomplete by design (deleted endpoints are omitted); relation items always
654
654
  keep denormalized endpoint names.
655
655
 
656
+ ### Knowledge graph create, update and delete
657
+
658
+ These six commands operate on entities and relations **inside a knowledge base**.
659
+ `kb create/update/delete` manages the knowledge base itself; `memory entity`
660
+ manages memory entities. Graph mutations use the existing DATA API credentials
661
+ selected by `--agent` / `CTXDB_AGENT`, and require graph write permission.
662
+
663
+ | Command | DATA API |
664
+ |---|---|
665
+ | `kb entity-create` / `kb entity-update` / `kb entity-delete` | POST / PUT / DELETE `/v1/knowledge/entities` |
666
+ | `kb relation-create` / `kb relation-update` / `kb relation-delete` | POST / PUT / DELETE `/v1/knowledge/entity-relations` |
667
+
668
+ Every command requires `--kb=<kb-id>` (an ID, not a name). Updates and deletes
669
+ require `--entity-id` or `--relation-id`; names, positional locators and bulk
670
+ selectors are not accepted. Find IDs with `entities` and `relations --verbose`.
671
+ Creates return the new ID; entity renaming preserves that ID and updates incident
672
+ relation names. Relation updates cannot change the endpoints or relation ID.
673
+
674
+ ```sh
675
+ ctxdb kb entity-create --kb=<kb-id> --name=ContextDB --type=Product \
676
+ --description='Context database' --aliases-json='["ctxdb"]' --json
677
+ ctxdb kb entity-get --kb=<kb-id> --entity-id=<id> --verbose --json
678
+ ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=ContextDB --type=Product \
679
+ --description='' --aliases-json='[]' --json
680
+
681
+ ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<a> --target-entity-id=<b> \
682
+ --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json
683
+ ctxdb kb relations --kb=<kb-id> --verbose --json
684
+ ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> \
685
+ --relation-type=CALLS --description='' --keywords-json='["api"]' --weight=0.5 --json
686
+
687
+ # Irreversible: delete a relation; both endpoint entities remain.
688
+ ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --json
689
+ # Irreversible: delete an entity and ALL its incoming/outgoing relations.
690
+ ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --json
691
+ ```
692
+
693
+ Create and update require every editable field shown above. **Update is a full
694
+ replacement**, so read the current values before editing. Explicitly use
695
+ `--description=''` or `--aliases-json='[]'` / `--keywords-json='[]'` to clear a
696
+ field. Arrays must contain strings; `--weight` must be finite, with the server
697
+ enforcing its 0–1 range and other business limits.
698
+
699
+ Deletion requires the bare `--yes` switch in both terminals and scripts; without
700
+ it, or with `--yes=false`, the CLI exits 2 without sending a delete request.
701
+ The CLI does not prompt interactively. Entity deletion returns the server's
702
+ `deleted`, `deleted_entity_count` and `deleted_relation_count`. Relation deletion
703
+ returns `deleted` and its IDs; its API does not return counts. A `deleted: false`
704
+ result is printed and exits 1, never reported as successful deletion.
705
+
706
+ Default output preserves the complete mutation `data`; `--json` formats that as
707
+ JSON and `--raw` retains the Box envelope. HTTP and business errors (including
708
+ HTTP 200 with a failing `errorCode`) exit nonzero even with `--raw`. Missing IDs,
709
+ malformed lists and incomplete arguments exit 2 with usage. A second delete of
710
+ an already deleted ID returns the server's not-found error; mutations are not
711
+ automatically retried after an uncertain response.
712
+
656
713
  ### Knowledge review commands
657
714
 
658
715
  `kb review` covers the Knowledge Review DATA APIs: review-queue handling
package/dist/cli/main.js CHANGED
@@ -4164,6 +4164,139 @@ var kbAclSubjects = (args) => kbAcl("subjects", args);
4164
4164
  var kbAclSet = (args) => kbAcl("set", args);
4165
4165
  var kbAclDelete = (args) => kbAcl("delete", args);
4166
4166
 
4167
+ // src/cli/kb-graph-cli.ts
4168
+ var GRAPH_MUTATION_USAGE = {
4169
+ "entity-create": "ctxdb kb entity-create --kb=<kb-id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
4170
+ "entity-update": "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
4171
+ "entity-delete": "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes",
4172
+ "relation-create": "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<id> --target-entity-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
4173
+ "relation-update": "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
4174
+ "relation-delete": "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes"
4175
+ };
4176
+ var ENTITY_FIELDS = ["name", "type", "description", "aliases-json"];
4177
+ var RELATION_FIELDS = ["relation-type", "description", "keywords-json", "weight"];
4178
+ function stringFlag2(args, name, allowEmpty = false) {
4179
+ const value = args.flags[name];
4180
+ if (typeof value !== "string" || !allowEmpty && !value.trim()) {
4181
+ throw new Error(`--${name} requires ${allowEmpty ? "a string value (use --description='' to clear)" : "a non-empty value"}`);
4182
+ }
4183
+ return value;
4184
+ }
4185
+ function stringArrayFlag(args, name) {
4186
+ const text = stringFlag2(args, name);
4187
+ let value;
4188
+ try {
4189
+ value = JSON.parse(text);
4190
+ } catch {
4191
+ throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
4192
+ }
4193
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
4194
+ throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
4195
+ }
4196
+ return value;
4197
+ }
4198
+ function parseMutation(args, resource, action) {
4199
+ const idFlag = `${resource}-id`;
4200
+ const allowed = /* @__PURE__ */ new Set([
4201
+ "kb",
4202
+ "agent",
4203
+ "json",
4204
+ "raw",
4205
+ ...action === "delete" ? [idFlag, "yes"] : [
4206
+ ...resource === "entity" ? ENTITY_FIELDS : RELATION_FIELDS,
4207
+ ...action === "update" ? [idFlag] : resource === "relation" ? ["source-entity-id", "target-entity-id"] : []
4208
+ ]
4209
+ ]);
4210
+ if (args.positional.length) throw new Error("use explicit ID flags; positional locators are not supported");
4211
+ for (const [flag, value] of Object.entries(args.flags)) {
4212
+ if (!allowed.has(flag)) throw new Error(`unknown --${flag}`);
4213
+ if (["yes", "json", "raw"].includes(flag) && value !== true) {
4214
+ throw new Error(`--${flag} is a boolean switch; do not supply a value`);
4215
+ }
4216
+ }
4217
+ if (args.flags.agent !== void 0) stringFlag2(args, "agent");
4218
+ const knowledge_base_id = stringFlag2(args, "kb").trim();
4219
+ const locator = action === "create" ? {} : { [`${resource}_id`]: stringFlag2(args, idFlag).trim() };
4220
+ if (action === "delete") {
4221
+ if (args.flags.yes !== true) {
4222
+ throw new Error(`deletion is irreversible${resource === "entity" ? " and deletes all incoming/outgoing relations" : ""}; pass --yes to confirm`);
4223
+ }
4224
+ return { knowledge_base_id, ...locator };
4225
+ }
4226
+ if (resource === "entity") {
4227
+ return {
4228
+ knowledge_base_id,
4229
+ ...locator,
4230
+ entity_name: stringFlag2(args, "name"),
4231
+ entity_type: stringFlag2(args, "type"),
4232
+ description: stringFlag2(args, "description", true),
4233
+ aliases: stringArrayFlag(args, "aliases-json")
4234
+ };
4235
+ }
4236
+ const weight = Number(stringFlag2(args, "weight"));
4237
+ if (!Number.isFinite(weight)) throw new Error("--weight must be a finite number");
4238
+ return {
4239
+ knowledge_base_id,
4240
+ ...locator,
4241
+ ...action === "create" ? {
4242
+ source_entity_id: stringFlag2(args, "source-entity-id").trim(),
4243
+ target_entity_id: stringFlag2(args, "target-entity-id").trim()
4244
+ } : {},
4245
+ relation_type: stringFlag2(args, "relation-type"),
4246
+ description: stringFlag2(args, "description", true),
4247
+ keywords: stringArrayFlag(args, "keywords-json"),
4248
+ weight
4249
+ };
4250
+ }
4251
+ async function mutateGraph(args, resource, action) {
4252
+ const command = `${resource}-${action}`;
4253
+ const usage2 = `usage: ${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]
4254
+ `;
4255
+ if (args.flags.help || args.flags.h) {
4256
+ process.stdout.write(usage2);
4257
+ return 0;
4258
+ }
4259
+ let payload;
4260
+ try {
4261
+ payload = parseMutation(args, resource, action);
4262
+ } catch (error) {
4263
+ process.stderr.write(`${error.message}
4264
+ ${usage2}`);
4265
+ return 2;
4266
+ }
4267
+ const { client } = buildContext(args);
4268
+ const path = resource === "entity" ? "/v1/knowledge/entities" : "/v1/knowledge/entity-relations";
4269
+ const response = action === "create" ? await client.postJson(path, payload) : action === "update" ? await client.putJson(path, payload) : await client.delete(path, payload);
4270
+ let data;
4271
+ try {
4272
+ data = unwrapReviewData(response, `kb ${command}`);
4273
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
4274
+ throw new CtxdbError(`kb ${command} returned no mutation result`);
4275
+ }
4276
+ const result = data;
4277
+ if (typeof result[`${resource}_id`] !== "string" || !result[`${resource}_id`] || result.knowledge_base_id !== payload.knowledge_base_id || action !== "create" && result[`${resource}_id`] !== payload[`${resource}_id`]) {
4278
+ throw new CtxdbError(`kb ${command} returned an invalid mutation locator`);
4279
+ }
4280
+ if (action === "delete") {
4281
+ if (typeof result.deleted !== "boolean") throw new CtxdbError(`kb ${command} returned no deletion status`);
4282
+ if (resource === "entity" && ["deleted_entity_count", "deleted_relation_count"].some(
4283
+ (key) => !Number.isSafeInteger(result[key]) || result[key] < 0
4284
+ )) throw new CtxdbError(`kb ${command} returned invalid deletion counts`);
4285
+ }
4286
+ } catch (error) {
4287
+ const requestId = response?.requestId;
4288
+ throw new CtxdbError(`${error.message}${typeof requestId === "string" ? ` (RequestId: ${requestId})` : ""}`);
4289
+ }
4290
+ printResult(args.flags.raw ? response : data, !!args.flags.json);
4291
+ return action === "delete" && data.deleted !== true ? 1 : 0;
4292
+ }
4293
+ var kbEntityCreate = (args) => mutateGraph(args, "entity", "create");
4294
+ var kbEntityUpdate = (args) => mutateGraph(args, "entity", "update");
4295
+ var kbEntityDelete = (args) => mutateGraph(args, "entity", "delete");
4296
+ var kbRelationCreate = (args) => mutateGraph(args, "relation", "create");
4297
+ var kbRelationUpdate = (args) => mutateGraph(args, "relation", "update");
4298
+ var kbRelationDelete = (args) => mutateGraph(args, "relation", "delete");
4299
+
4167
4300
  // src/cli/promotion-cli.ts
4168
4301
  var GENERATE_USAGE = "usage: ctxdb memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--wait|--no-wait] [--agent=<name>] [--json]";
4169
4302
  var LIST_USAGE = "usage: ctxdb memory promotion list [--status=<generating|pending_review|approving|approved|rejected|generation_failed>] [--page=N] [--page-size=N] [--sort-by=quality_score] [--sort-order=asc|desc] [--raw] [--agent=<name>] [--json]";
@@ -4244,7 +4377,7 @@ ${GENERATE_USAGE}`, 2);
4244
4377
  if (!topic || topic.trim() === "") fail(GENERATE_USAGE, 2);
4245
4378
  const ctx = buildContext(args);
4246
4379
  const body = { topic };
4247
- const kbId = stringFlag2(args, "knowledge-base-id");
4380
+ const kbId = stringFlag3(args, "knowledge-base-id");
4248
4381
  if (kbId !== void 0) body.knowledge_base_id = kbId;
4249
4382
  const resp = await ctx.client.postJson("/v1/memory_promotions/generate", body);
4250
4383
  let data = unwrapBox(resp, "memory promotion generate");
@@ -4277,7 +4410,7 @@ ${GENERATE_USAGE}`, 2);
4277
4410
  async function promotionList(args) {
4278
4411
  if (showHelp(args, LIST_USAGE)) return 0;
4279
4412
  const params = {};
4280
- const status2 = stringFlag2(args, "status");
4413
+ const status2 = stringFlag3(args, "status");
4281
4414
  if (status2 !== void 0) {
4282
4415
  if (!PROMOTION_STATUSES.includes(status2)) {
4283
4416
  fail(`invalid --status: ${status2} (expected one of ${PROMOTION_STATUSES.join("|")})
@@ -4289,7 +4422,7 @@ ${LIST_USAGE}`, 2);
4289
4422
  if (page !== void 0) params.page = page;
4290
4423
  const pageSize = intFlag(args, "page-size", LIST_USAGE);
4291
4424
  if (pageSize !== void 0) params.page_size = pageSize;
4292
- const sortBy = stringFlag2(args, "sort-by");
4425
+ const sortBy = stringFlag3(args, "sort-by");
4293
4426
  if (sortBy !== void 0) {
4294
4427
  if (sortBy !== "quality_score") {
4295
4428
  fail(`invalid --sort-by: ${sortBy} (expected quality_score)
@@ -4297,7 +4430,7 @@ ${LIST_USAGE}`, 2);
4297
4430
  }
4298
4431
  params.sort_by = sortBy;
4299
4432
  }
4300
- const sortOrder = stringFlag2(args, "sort-order");
4433
+ const sortOrder = stringFlag3(args, "sort-order");
4301
4434
  if (sortOrder !== void 0) {
4302
4435
  if (sortOrder !== "asc" && sortOrder !== "desc") {
4303
4436
  fail(`invalid --sort-order: ${sortOrder} (expected asc|desc)
@@ -4339,7 +4472,7 @@ async function promotionReject(args) {
4339
4472
  const promotionId = requireIdFlag(args, "promotion-id", REJECT_USAGE);
4340
4473
  const ctx = buildContext(args);
4341
4474
  const body = { promotion_id: promotionId };
4342
- const reason = stringFlag2(args, "reason");
4475
+ const reason = stringFlag3(args, "reason");
4343
4476
  if (reason !== void 0) body.reason = reason;
4344
4477
  const resp = await ctx.client.postJson("/v1/memory_promotions/reject", body);
4345
4478
  emit(args, resp, "memory promotion reject", (data) => data);
@@ -4347,17 +4480,17 @@ async function promotionReject(args) {
4347
4480
  }
4348
4481
  async function promotionBatchAction(args) {
4349
4482
  if (showHelp(args, BATCH_ACTION_USAGE)) return 0;
4350
- const action = stringFlag2(args, "action");
4483
+ const action = stringFlag3(args, "action");
4351
4484
  if (action === void 0 || !BATCH_ACTIONS.includes(action)) {
4352
4485
  fail(`--action is required and must be one of ${BATCH_ACTIONS.join("|")}
4353
4486
  ${BATCH_ACTION_USAGE}`, 2);
4354
4487
  }
4355
4488
  const ids = parseIdsFlag(args, BATCH_ACTION_USAGE);
4356
- const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag2(args, "knowledge-base-id");
4489
+ const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag3(args, "knowledge-base-id");
4357
4490
  const ctx = buildContext(args);
4358
4491
  const body = { action, ids };
4359
4492
  if (kbId !== void 0) body.knowledge_base_id = kbId;
4360
- const reason = stringFlag2(args, "reason");
4493
+ const reason = stringFlag3(args, "reason");
4361
4494
  if (reason !== void 0) body.reason = reason;
4362
4495
  const resp = await ctx.client.postJson("/v1/memory_promotions/batch", body);
4363
4496
  const data = emit(args, resp, "memory promotion batch-action", (data2) => data2);
@@ -4383,8 +4516,8 @@ async function promotionResolveDoubts(args) {
4383
4516
  ${RESOLVE_DOUBTS_USAGE}`, 2);
4384
4517
  }
4385
4518
  const resolutions = parseResolutions(resolutionsRaw, RESOLVE_DOUBTS_USAGE);
4386
- const title = stringFlag2(args, "title");
4387
- const content = stringFlag2(args, "content");
4519
+ const title = stringFlag3(args, "title");
4520
+ const content = stringFlag3(args, "content");
4388
4521
  if (resolutions.length === 0 && title === void 0 && content === void 0) {
4389
4522
  fail(`provide a resolution or a non-empty --title/--content edit
4390
4523
  ${RESOLVE_DOUBTS_USAGE}`, 2);
@@ -4478,7 +4611,7 @@ async function promotionSettingsGet(args) {
4478
4611
  async function promotionSettingsUpdate(args) {
4479
4612
  if (showHelp(args, SETTINGS_UPDATE_USAGE)) return 0;
4480
4613
  const body = {};
4481
- const enabled = stringFlag2(args, "auto-ingest-enabled");
4614
+ const enabled = stringFlag3(args, "auto-ingest-enabled");
4482
4615
  if (enabled !== void 0) {
4483
4616
  if (enabled !== "true" && enabled !== "false") {
4484
4617
  fail(`invalid --auto-ingest-enabled: ${enabled} (expected true|false)
@@ -4486,7 +4619,7 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
4486
4619
  }
4487
4620
  body.auto_ingest_enabled = enabled === "true";
4488
4621
  }
4489
- const threshold = stringFlag2(args, "auto-ingest-threshold");
4622
+ const threshold = stringFlag3(args, "auto-ingest-threshold");
4490
4623
  if (threshold !== void 0) {
4491
4624
  const n = Number(threshold);
4492
4625
  if (!Number.isFinite(n) || n < 0 || n > 1) {
@@ -4495,7 +4628,7 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
4495
4628
  }
4496
4629
  body.auto_ingest_threshold = n;
4497
4630
  }
4498
- const defaultKbId = stringFlag2(args, "default-kb-id");
4631
+ const defaultKbId = stringFlag3(args, "default-kb-id");
4499
4632
  const clearDefaultKb = args.flags["clear-default-kb"] === true;
4500
4633
  if (defaultKbId !== void 0 && clearDefaultKb) {
4501
4634
  fail(`cannot use --default-kb-id and --clear-default-kb together
@@ -4512,12 +4645,12 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
4512
4645
  emit(args, resp, "memory promotion settings update", (data) => data);
4513
4646
  return 0;
4514
4647
  }
4515
- function stringFlag2(args, name) {
4648
+ function stringFlag3(args, name) {
4516
4649
  const value = args.flags[name];
4517
4650
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
4518
4651
  }
4519
4652
  function requireIdFlag(args, name, usage2) {
4520
- const value = stringFlag2(args, name);
4653
+ const value = stringFlag3(args, name);
4521
4654
  if (value === void 0) fail(`--${name} is required
4522
4655
  ${usage2}`, 2);
4523
4656
  return value;
@@ -4528,7 +4661,7 @@ function requireTemplateIdPositional(args, usage2) {
4528
4661
  return templateId;
4529
4662
  }
4530
4663
  function intFlag(args, name, usage2) {
4531
- const raw = stringFlag2(args, name);
4664
+ const raw = stringFlag3(args, name);
4532
4665
  if (raw === void 0) return void 0;
4533
4666
  const n = Number(raw);
4534
4667
  if (!Number.isInteger(n) || n < 1) {
@@ -4538,7 +4671,7 @@ ${usage2}`, 2);
4538
4671
  return n;
4539
4672
  }
4540
4673
  function parseIdsFlag(args, usage2) {
4541
- const raw = stringFlag2(args, "ids");
4674
+ const raw = stringFlag3(args, "ids");
4542
4675
  if (raw === void 0) fail(`--ids is required (comma-separated)
4543
4676
  ${usage2}`, 2);
4544
4677
  const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -4588,7 +4721,7 @@ ${usage2}`, 2);
4588
4721
  });
4589
4722
  }
4590
4723
  function buildTemplateUpsertBody(args, usage2) {
4591
- const name = stringFlag2(args, "name");
4724
+ const name = stringFlag3(args, "name");
4592
4725
  if (name === void 0) fail(`--name is required
4593
4726
  ${usage2}`, 2);
4594
4727
  const sectionsRaw = args.flags["sections-json"];
@@ -6013,6 +6146,70 @@ var kbAclGroup = group("acl", "Manage direct knowledge-base grants and inspect e
6013
6146
  examples: [{ command: "ctxdb kb acl delete <acl-id> --json" }]
6014
6147
  }))
6015
6148
  ]);
6149
+ var graphKbOption = option("--kb", "Knowledge-base ID (not a name).", { valueName: "kb-id", required: true });
6150
+ var graphEntityIdOption = option("--entity-id", "Exact entity ID within the knowledge base.", { valueName: "id", required: true });
6151
+ var graphRelationIdOption = option("--relation-id", "Exact relation ID within the knowledge base.", { valueName: "id", required: true });
6152
+ var graphEntityFields = [
6153
+ option("--name", "Complete entity name.", { valueName: "name", required: true }),
6154
+ option("--type", "Complete entity type.", { valueName: "type", required: true }),
6155
+ option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
6156
+ option("--aliases-json", "Complete string array; use '[]' for no aliases.", { valueName: "json-array", type: "json", required: true })
6157
+ ];
6158
+ var graphRelationFields = [
6159
+ option("--relation-type", "Complete relation type.", { valueName: "type", required: true }),
6160
+ option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
6161
+ option("--keywords-json", "Complete string array; use '[]' for no keywords.", { valueName: "json-array", type: "json", required: true }),
6162
+ option("--weight", "Relation weight between 0 and 1; range checked by the server.", { valueName: "number", type: "number", required: true })
6163
+ ];
6164
+ var graphMutationOutput = [AGENT_OPTION, RAW_OPTION, JSON_OPTION];
6165
+ var graphDeleteConfirmation = option("--yes", "Explicitly confirm irreversible deletion; required in terminals and scripts.", { type: "boolean", required: true });
6166
+ var graphMutationUsage = (command) => [`${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]`];
6167
+ var graphMutationCommands = [
6168
+ leaf("entity-create", "Create one knowledge-graph entity.", kbEntityCreate, details({
6169
+ usage: graphMutationUsage("entity-create"),
6170
+ options: [graphKbOption, ...graphEntityFields, ...graphMutationOutput],
6171
+ notes: ["Returns the server-assigned entity ID and editable fields. This operates on the KB graph, independently of memory entities."],
6172
+ examples: [{ command: "ctxdb kb entity-create --kb=<kb-id> --name=ContextDB --type=Product --description='Context database' --aliases-json='[]' --json" }]
6173
+ })),
6174
+ leaf("entity-update", "Replace the editable fields of one knowledge-graph entity.", kbEntityUpdate, details({
6175
+ usage: graphMutationUsage("entity-update"),
6176
+ options: [graphKbOption, graphEntityIdOption, ...graphEntityFields, ...graphMutationOutput],
6177
+ warnings: ["Full replacement: read entity-get first and supply all editable fields. Empty description/aliases explicitly clear them.", "Renaming preserves the entity ID and updates incident relation names. The server rejects renames exceeding its incident-relation limit."],
6178
+ examples: [{ command: "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=ContextDB --type=Product --description='' --aliases-json='[]' --json" }]
6179
+ })),
6180
+ leaf("entity-delete", "Irreversibly delete an entity and all incoming/outgoing relations.", kbEntityDelete, details({
6181
+ usage: graphMutationUsage("entity-delete"),
6182
+ options: [graphKbOption, graphEntityIdOption, graphDeleteConfirmation, ...graphMutationOutput],
6183
+ warnings: ["Irreversible: deletes the selected entity and every incident relation. No delete request is sent without --yes."],
6184
+ notes: ["Only exact IDs are accepted; names and bulk deletion are unsupported.", "Returns deleted, deleted_entity_count and deleted_relation_count from the server. A false deleted result exits 1."],
6185
+ examples: [{ command: "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --json" }]
6186
+ })),
6187
+ leaf("relation-create", "Create one relation between knowledge-graph entity IDs.", kbRelationCreate, details({
6188
+ usage: graphMutationUsage("relation-create"),
6189
+ options: [
6190
+ graphKbOption,
6191
+ option("--source-entity-id", "Existing source entity ID.", { valueName: "id", required: true }),
6192
+ option("--target-entity-id", "Existing target entity ID.", { valueName: "id", required: true }),
6193
+ ...graphRelationFields,
6194
+ ...graphMutationOutput
6195
+ ],
6196
+ examples: [{ command: "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<a> --target-entity-id=<b> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
6197
+ })),
6198
+ leaf("relation-update", "Replace the editable fields of one knowledge-graph relation.", kbRelationUpdate, details({
6199
+ usage: graphMutationUsage("relation-update"),
6200
+ options: [graphKbOption, graphRelationIdOption, ...graphRelationFields, ...graphMutationOutput],
6201
+ warnings: ["Full replacement: read relations --verbose first and supply all editable fields. Empty description/keywords explicitly clear them."],
6202
+ constraints: ["Relation ID and source/target entity IDs cannot be changed."],
6203
+ examples: [{ command: "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
6204
+ })),
6205
+ leaf("relation-delete", "Irreversibly delete one knowledge-graph relation.", kbRelationDelete, details({
6206
+ usage: graphMutationUsage("relation-delete"),
6207
+ options: [graphKbOption, graphRelationIdOption, graphDeleteConfirmation, ...graphMutationOutput],
6208
+ warnings: ["Irreversible: deletes only this relation; both endpoint entities remain. No delete request is sent without --yes."],
6209
+ notes: ["Only exact relation IDs are accepted. Returns deleted from the server; a false result exits 1. The relation API does not return deletion counts."],
6210
+ examples: [{ command: "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --json" }]
6211
+ }))
6212
+ ];
6016
6213
  var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph, and review.", [
6017
6214
  leaf("create", "Create a knowledge base.", kbCreate, details({
6018
6215
  usage: ["ctxdb kb create <kb-name> [--description=<text>] [--graph-enabled|--no-graph-enabled] [--review-enabled|--no-review-enabled] [--agent=<name>] [--json]"],
@@ -6277,6 +6474,7 @@ var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph,
6277
6474
  notes: ["--entity-id matches either endpoint; --entity-ids requires both endpoints in the supplied set."],
6278
6475
  examples: [{ command: "ctxdb kb relations --kb=<kb-id> --entity-id=<id> --verbose --json" }]
6279
6476
  })),
6477
+ ...graphMutationCommands,
6280
6478
  reviewGroup
6281
6479
  ]);
6282
6480
  function createCommandTree(options = {}) {
@@ -143,6 +143,10 @@ init 使用 PUT,part/complete 保持原协议;网络结果不明确时不自
143
143
 
144
144
  | 子命令 | minimal signature |
145
145
  |---|---|
146
+ | `kb entity-create / entity-update` | `ctxdb kb entity-create --help` / `ctxdb kb entity-update --help` |
147
+ | `kb relation-create / relation-update` | `ctxdb kb relation-create --help` / `ctxdb kb relation-update --help` |
148
+ | `kb entity-delete` | `ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --agent=<name>` |
149
+ | `kb relation-delete` | `ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --agent=<name>` |
146
150
  | `kb search` | `ctxdb kb search "<query>" --agent=<name>` |
147
151
  | `kb list` | `ctxdb kb list --agent=<name>` |
148
152
  | `kb documents-list` | `ctxdb kb documents-list <kb-name> [--sort-by=created_at] [--sort-order=asc|desc] [--fields=field1,field2] --agent=<name>` |
@@ -156,6 +160,11 @@ init 使用 PUT,part/complete 保持原协议;网络结果不明确时不自
156
160
  | `kb update-text` | `ctxdb kb update-text <document-id> --text="<body>" --agent=<name>` |
157
161
  | `kb update-file` | `ctxdb kb update-file <document-id> <local-path> --agent=<name>` |
158
162
 
163
+ 图谱写命令的 `--kb` 必须是 KB ID;用 `entities` 和 `relations --verbose` 获取实体/关系 ID。
164
+ create/update 需要完整可编辑字段,update 是全量替换,先回读再修改;空描述和空列表必须显式传入。
165
+ 删除不可恢复,实体删除会级联清除全部入边和出边,关系删除保留两端实体。确认用户授权及精确 ID 后
166
+ 才使用 `--yes`;省略确认时不会发送删除请求。命令结果不明确时先回读,不要直接重试。
167
+
159
168
  所有命令 JSON 写 stdout、错误写 stderr 非零退出码。
160
169
 
161
170
  ## 4. Hooks 感知
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyunrds/ctxdb",
3
- "version": "1.0.9-beta.1",
3
+ "version": "1.0.9",
4
4
  "type": "module",
5
5
  "description": "Unified access layer for RDS ContextDatabase: `ctxdb` CLI (memory + KB ops), one-shot multi-agent installer, per-agent config, hooks/plugins, and SKILL.md.",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "semver": "^7.8.5",
36
36
  "yaml": "^2.9.0",
37
- "@aliyunrds/ctxdb-shared": "~0.0.9-beta.0"
37
+ "@aliyunrds/ctxdb-shared": "~0.0.9"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.15.0",