@robbeverhelst/do 1.351.0 → 1.352.0

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.
Files changed (2) hide show
  1. package/dist/do.js +192 -24
  2. package/package.json +8 -2
package/dist/do.js CHANGED
@@ -14420,7 +14420,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
14420
14420
 
14421
14421
  // src/meta.ts
14422
14422
  var CLI_NAME = "do";
14423
- var VERSION = "1.351.0";
14423
+ var VERSION = "1.352.0";
14424
14424
  var API_PREFIX = "/api/v1";
14425
14425
  var DEFAULT_API_URL = "http://localhost:3001";
14426
14426
 
@@ -14722,8 +14722,8 @@ class DoClient {
14722
14722
  put(path, body) {
14723
14723
  return this.request("PUT", path, { body });
14724
14724
  }
14725
- delete(path) {
14726
- return this.request("DELETE", path, {});
14725
+ delete(path, opts = {}) {
14726
+ return this.request("DELETE", path, opts);
14727
14727
  }
14728
14728
  upload(path, form) {
14729
14729
  return this.request("POST", path, { form });
@@ -17650,6 +17650,7 @@ var tenants = pgTable("tenants", {
17650
17650
  name: text("name").notNull(),
17651
17651
  backdrop: text("backdrop"),
17652
17652
  gaardHouseUrl: text("gaard_house_url"),
17653
+ address: text("address"),
17653
17654
  awayFrom: date5("away_from", { mode: "string" }),
17654
17655
  awayUntil: date5("away_until", { mode: "string" }),
17655
17656
  lastTaskNumber: integer2("last_task_number").notNull().default(0),
@@ -18455,6 +18456,7 @@ var contacts = pgTable("contacts", {
18455
18456
  website: text("website"),
18456
18457
  notes: text("notes"),
18457
18458
  tags: text("tags").array().notNull().default([]),
18459
+ knowsAbout: text("knows_about").array().notNull().default([]),
18458
18460
  lastContactedAt: timestamp("last_contacted_at", { withTimezone: true }),
18459
18461
  keepInTouchRuleId: text("keep_in_touch_rule_id").references(() => recurrenceRules.id),
18460
18462
  rhythmDays: integer2("rhythm_days"),
@@ -19475,7 +19477,8 @@ var oauthIdentitySchema = createSelectSchema(oauthIdentities, {
19475
19477
  var spaceSchema = createSelectSchema(tenants, {
19476
19478
  createdAt: exports_coerce.date(),
19477
19479
  updatedAt: exports_coerce.date(),
19478
- deletedAt: exports_coerce.date().nullable()
19480
+ deletedAt: exports_coerce.date().nullable(),
19481
+ address: string2().nullable().default(null)
19479
19482
  }).omit({
19480
19483
  gaardHouseUrl: true,
19481
19484
  awayFrom: true,
@@ -19484,12 +19487,14 @@ var spaceSchema = createSelectSchema(tenants, {
19484
19487
  lastProjectNumber: true
19485
19488
  });
19486
19489
  var backdropIdSchema = string2().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "a backdrop id is lowercase words joined by dashes").max(40);
19490
+ var spaceAddressSchema = string2().trim().max(200).transform((line) => line || null).nullable();
19487
19491
  var updateSpaceInputSchema = object({
19488
19492
  name: string2().trim().min(1).max(120).optional(),
19489
- backdrop: backdropIdSchema.nullable().optional()
19490
- }).strict().refine((input) => input.name !== undefined || input.backdrop !== undefined, {
19491
- message: "nothing to update"
19492
- });
19493
+ backdrop: backdropIdSchema.nullable().optional(),
19494
+ address: spaceAddressSchema.optional()
19495
+ }).strict().refine((input) => input.name !== undefined || input.backdrop !== undefined || input.address !== undefined, { message: "nothing to update" });
19496
+ var setSpaceAddressInputSchema = object({ address: spaceAddressSchema }).strict();
19497
+ var spaceAddressViewSchema = object({ address: string2().nullable() });
19493
19498
  var onboardingResultSchema = object({ onboardingCompleted: boolean2() });
19494
19499
  var authMethodsSchema = object({
19495
19500
  hasPassword: boolean2(),
@@ -21010,6 +21015,12 @@ var animalArrivedInputSchema = object({
21010
21015
  }).strict();
21011
21016
 
21012
21017
  // ../../packages/schema/src/operations/crud.ts
21018
+ var DELETE_REASON_QUERY = [
21019
+ {
21020
+ name: "reason",
21021
+ description: `An Agent's one line on why (first line, up to 280 characters); the Trash shows it as "Deleted by <agent>: <reason>". Ignored for a member's key.`
21022
+ }
21023
+ ];
21013
21024
  function crudOperations(r) {
21014
21025
  const base = `/${r.segment}`;
21015
21026
  const one = `/${r.segment}/{id}`;
@@ -21072,6 +21083,7 @@ function crudOperations(r) {
21072
21083
  tag: r.tag,
21073
21084
  summary: `Delete a ${r.noun}`,
21074
21085
  description: `Delete a ${r.noun}; fires the \`${event}.deleted\` webhook.`,
21086
+ query: DELETE_REASON_QUERY,
21075
21087
  status: 204,
21076
21088
  response: { description: "Deleted." },
21077
21089
  errors: { 404: true },
@@ -25644,7 +25656,8 @@ var contactSchema = createSelectSchema(contacts, {
25644
25656
  userId: string2().nullable().default(null),
25645
25657
  rhythmDays: number2().int().nullable().default(null),
25646
25658
  rhythmSnoozedUntil: string2().nullable().default(null),
25647
- emailInvalidAt: exports_coerce.date().nullable().default(null)
25659
+ emailInvalidAt: exports_coerce.date().nullable().default(null),
25660
+ knowsAbout: array(string2()).default([])
25648
25661
  });
25649
25662
  var contactImportantDateSchema = object({
25650
25663
  id: ulidSchema,
@@ -25677,6 +25690,18 @@ var contactImportantDateInputSchema = preprocess((v) => {
25677
25690
  eventDate: new Date(Date.UTC(YEARLESS_YEAR, d.eventDate.getUTCMonth(), d.eventDate.getUTCDate()))
25678
25691
  } : d));
25679
25692
  var websiteSchema = string2().trim().url().max(500).refine((v) => /^https?:\/\//i.test(v), "must start with http:// or https://");
25693
+ var KNOWS_ABOUT_PHRASE_MAX = 80;
25694
+ var KNOWS_ABOUT_MAX = 30;
25695
+ var knowsAboutSchema = array(string2().trim().max(KNOWS_ABOUT_PHRASE_MAX)).max(KNOWS_ABOUT_MAX).transform((phrases) => {
25696
+ const seen = new Set;
25697
+ return phrases.filter((phrase) => {
25698
+ const key = phrase.toLowerCase();
25699
+ if (!phrase || seen.has(key))
25700
+ return false;
25701
+ seen.add(key);
25702
+ return true;
25703
+ });
25704
+ });
25680
25705
  var createContactInputSchema = createInsertSchema(contacts, {
25681
25706
  id: ulidSchema,
25682
25707
  email: string2().email().nullish(),
@@ -25685,6 +25710,7 @@ var createContactInputSchema = createInsertSchema(contacts, {
25685
25710
  website: websiteSchema.nullish(),
25686
25711
  notes: string2().nullish(),
25687
25712
  tags: array(string2()).optional(),
25713
+ knowsAbout: knowsAboutSchema.optional(),
25688
25714
  lastContactedAt: exports_coerce.date().nullish(),
25689
25715
  rhythmDays: contactPeopleInputShape.rhythmDays,
25690
25716
  rhythmSnoozedUntil: contactPeopleInputShape.rhythmSnoozedUntil
@@ -25702,6 +25728,7 @@ var updateContactInputSchema = createUpdateSchema(contacts, {
25702
25728
  website: websiteSchema.nullish(),
25703
25729
  notes: string2().nullish(),
25704
25730
  tags: array(string2()).optional(),
25731
+ knowsAbout: knowsAboutSchema.optional(),
25705
25732
  lastContactedAt: exports_coerce.date().nullish(),
25706
25733
  rhythmDays: contactPeopleInputShape.rhythmDays,
25707
25734
  rhythmSnoozedUntil: contactPeopleInputShape.rhythmSnoozedUntil
@@ -37004,7 +37031,13 @@ var automationRunSummarySchema = object({
37004
37031
  });
37005
37032
  var automationListItemSchema = automationViewSchema.extend({
37006
37033
  madeBy: object({ userId: string2(), name: string2(), you: boolean2() }).nullable(),
37007
- lastRun: automationRunSummarySchema.nullable()
37034
+ lastRun: automationRunSummarySchema.nullable(),
37035
+ needs: object({
37036
+ connectionId: string2(),
37037
+ provider: string2(),
37038
+ title: string2(),
37039
+ reconnect: boolean2()
37040
+ }).nullable().default(null)
37008
37041
  });
37009
37042
  var automationRunNowSchema = object({ queued: literal(true) });
37010
37043
 
@@ -39840,6 +39873,7 @@ var shareLinkSchema = createSelectSchema(shareLinks, {
39840
39873
  updatedAt: exports_coerce.date()
39841
39874
  });
39842
39875
  var shareLinkPublicSchema = shareLinkSchema.omit({ tokenHash: true });
39876
+ var shareLinkListedSchema = shareLinkPublicSchema.extend({ token: string2().nullable() });
39843
39877
  var createShareLinkInputSchema = createInsertSchema(shareLinks, {
39844
39878
  id: ulidSchema,
39845
39879
  nodeId: ulidSchema,
@@ -40087,6 +40121,32 @@ var shoppingListOperations = defineOperations({
40087
40121
  })
40088
40122
  });
40089
40123
 
40124
+ // ../../packages/schema/src/operations/space.ts
40125
+ var spaceOperations = defineOperations({
40126
+ address: {
40127
+ method: "get",
40128
+ path: "/space/address",
40129
+ scopes: ["settings:read"],
40130
+ tag: "Space",
40131
+ summary: "Get the Space's address",
40132
+ description: "The household's postal address as `{ address }`: one line, or null when none is set.",
40133
+ status: 200,
40134
+ response: { description: "The Space's address." }
40135
+ },
40136
+ setAddress: {
40137
+ method: "put",
40138
+ path: "/space/address",
40139
+ scopes: ["settings:write"],
40140
+ tag: "Space",
40141
+ summary: "Set the Space's address",
40142
+ description: "Set the household's postal address to one line (trimmed, at most 200 characters); `null` or a blank line clears it. Returns `{ address }`.",
40143
+ body: setSpaceAddressInputSchema,
40144
+ status: 200,
40145
+ response: { description: "The Space's address now." },
40146
+ errors: { 400: true }
40147
+ }
40148
+ });
40149
+
40090
40150
  // ../../packages/schema/src/db/tap-tags.ts
40091
40151
  var tapTagKind = pgEnum("tap_tag_kind", ["habit", "routine"]);
40092
40152
  var tapTags = pgTable("tap_tags", {
@@ -40503,6 +40563,7 @@ var taskOperations = defineOperations({
40503
40563
  tag: "Tasks",
40504
40564
  summary: "Delete a task",
40505
40565
  description: "Delete a task; fires the `task.deleted` webhook.",
40566
+ query: DELETE_REASON_QUERY,
40506
40567
  status: 204,
40507
40568
  response: { description: "Deleted." },
40508
40569
  errors: { 404: true },
@@ -46808,6 +46869,22 @@ function renderKraken(data, mode) {
46808
46869
  }
46809
46870
  var commands11 = pairCommands(CONNECTION_COMMAND_SPECS, CONNECTION_HANDLERS);
46810
46871
 
46872
+ // ../../packages/domain/src/knows-about.ts
46873
+ var same = (a, b) => a.trim().toLowerCase() === b.trim().toLowerCase();
46874
+ function knowsAboutHas(list, phrase) {
46875
+ return list.some((p) => same(p, phrase));
46876
+ }
46877
+ function canAddKnowsAbout(list, draft) {
46878
+ const phrase = draft.trim();
46879
+ return phrase.length > 0 && phrase.length <= KNOWS_ABOUT_PHRASE_MAX && list.length < KNOWS_ABOUT_MAX && !knowsAboutHas(list, phrase);
46880
+ }
46881
+ function addKnowsAbout(list, draft) {
46882
+ return canAddKnowsAbout(list, draft) ? [...list, draft.trim()] : [...list];
46883
+ }
46884
+ function removeKnowsAbout(list, phrase) {
46885
+ return list.filter((p) => !same(p, phrase));
46886
+ }
46887
+
46811
46888
  // src/commands/contacts.ts
46812
46889
  var FIELD_FLAGS = [
46813
46890
  { name: "name", type: "string", description: "The display name (or pass it as the argument)." },
@@ -46837,6 +46914,21 @@ var FIELD_FLAGS = [
46837
46914
  description: "The circle ids they are in, comma-separated (replaces the membership)."
46838
46915
  },
46839
46916
  { name: "tags", type: "string", description: "Tags, comma-separated (replaces the tags)." },
46917
+ {
46918
+ name: "knows",
46919
+ type: "string",
46920
+ description: "What they know about, the go-to for, comma-separated (replaces the list; none clears it)."
46921
+ },
46922
+ {
46923
+ name: "knows-add",
46924
+ type: "string",
46925
+ description: `One more thing they know about ("the boat's engine"); a repeat is skipped.`
46926
+ },
46927
+ {
46928
+ name: "knows-remove",
46929
+ type: "string",
46930
+ description: "Take one thing off what they know about (ignoring case)."
46931
+ },
46840
46932
  { name: "notes", type: "string", description: "Long-form notes." },
46841
46933
  {
46842
46934
  name: "rhythm",
@@ -46862,10 +46954,10 @@ var CONTACT_COMMAND_SPECS = [
46862
46954
  flags: FIELD_FLAGS,
46863
46955
  scopes: [...contactOperations.update.scopes],
46864
46956
  returns: "ContactView",
46865
- notes: "Only the flags you pass change. `none` clears a field (--email none, --birthday none). --birthday keeps the contact's other dates; --roles, --circles and --tags replace the list (a role it already has keeps its details)."
46957
+ notes: "Only the flags you pass change. `none` clears a field (--email none, --birthday none). --birthday keeps the contact's other dates; --roles, --circles, --tags and --knows replace the list (a role it already has keeps its details); --knows-add and --knows-remove change one phrase of what they know about."
46866
46958
  }
46867
46959
  ];
46868
- var MERGING_FLAGS = ["birthday", "no-year", "roles"];
46960
+ var MERGING_FLAGS = ["birthday", "no-year", "roles", "knows-add", "knows-remove"];
46869
46961
  var NULLABLE = ["email", "phone", "company", "website", "notes"];
46870
46962
  function list2(value) {
46871
46963
  return value.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
@@ -46900,6 +46992,19 @@ function contactBodyFromFlags(flags, base, op, current) {
46900
46992
  const tags = flagString(flags, "tags");
46901
46993
  if (tags !== undefined)
46902
46994
  body.tags = isNone(tags) ? [] : list2(tags);
46995
+ const knows = flagString(flags, "knows");
46996
+ if (knows !== undefined)
46997
+ body.knowsAbout = isNone(knows) ? [] : list2(knows);
46998
+ const knowsAdd = flagString(flags, "knows-add");
46999
+ const knowsRemove = flagString(flags, "knows-remove");
47000
+ if (knowsAdd !== undefined || knowsRemove !== undefined) {
47001
+ let phrases = body.knowsAbout ?? current?.knowsAbout ?? [];
47002
+ if (knowsRemove !== undefined)
47003
+ phrases = removeKnowsAbout(phrases, knowsRemove);
47004
+ if (knowsAdd !== undefined)
47005
+ phrases = addKnowsAbout(phrases, knowsAdd);
47006
+ body.knowsAbout = phrases;
47007
+ }
46903
47008
  const circles = flagString(flags, "circles");
46904
47009
  if (circles !== undefined)
46905
47010
  body.circleIds = isNone(circles) ? [] : list2(circles);
@@ -52347,6 +52452,11 @@ var GLOBAL_FLAGS = [
52347
52452
  type: "boolean",
52348
52453
  description: "Permit a destructive verb (delete/revoke/remove-member). Off by default \u2014 a guardrail for autonomous agents."
52349
52454
  },
52455
+ {
52456
+ name: "reason",
52457
+ type: "string",
52458
+ description: `One line on why, for delete/archive (sent as ?reason=). An agent's shows in the Trash: "Deleted by <agent>: <reason>".`
52459
+ },
52350
52460
  {
52351
52461
  name: "dry-run",
52352
52462
  type: "boolean",
@@ -57504,7 +57614,7 @@ var commands52 = [
57504
57614
  ],
57505
57615
  scopes: [...shareLinkOperations.create.scopes],
57506
57616
  returns: "CreateShareLinkResult",
57507
- notes: "The signed token is returned once, in the create response."
57617
+ notes: "The signed token isn't shown once: `do link list <node>` gives it again until the link expires or is revoked."
57508
57618
  },
57509
57619
  run: async ({ args, flags, requireClient }) => {
57510
57620
  const node = requireArg(args, 0, "node id");
@@ -57527,7 +57637,7 @@ var commands52 = [
57527
57637
  args: [{ name: "node", required: true, description: "Node ULID." }],
57528
57638
  flags: [],
57529
57639
  scopes: [...shareLinkOperations.listForNode.scopes],
57530
- returns: "ShareLinkPublic"
57640
+ returns: "ShareLinkListed"
57531
57641
  },
57532
57642
  run: readOperation(shareLinkOperations.listForNode, renderList, { nodeId: "node id" })
57533
57643
  },
@@ -57552,8 +57662,64 @@ var commands52 = [
57552
57662
  }
57553
57663
  ];
57554
57664
 
57555
- // src/commands/tap-tags.ts
57665
+ // src/commands/space.ts
57556
57666
  var commands53 = [
57667
+ {
57668
+ spec: {
57669
+ path: ["space", "address"],
57670
+ summary: "Show the Space's postal address (null when none is set).",
57671
+ args: [],
57672
+ flags: [],
57673
+ scopes: [...spaceOperations.address.scopes],
57674
+ returns: "SpaceAddress",
57675
+ notes: `${routeOf(spaceOperations.address)}.`
57676
+ },
57677
+ run: readOperation(spaceOperations.address, renderRecord)
57678
+ },
57679
+ {
57680
+ spec: {
57681
+ path: ["space", "address", "set"],
57682
+ summary: "Set the Space's postal address to one line.",
57683
+ args: [
57684
+ {
57685
+ name: "address",
57686
+ required: true,
57687
+ description: 'One line, e.g. "Main Street 12, Springfield".'
57688
+ }
57689
+ ],
57690
+ flags: [],
57691
+ scopes: [...spaceOperations.setAddress.scopes],
57692
+ returns: "SpaceAddress",
57693
+ notes: `${routeOf(spaceOperations.setAddress)}. Trimmed, at most 200 characters.`
57694
+ },
57695
+ run: async ({ args, requireClient }) => {
57696
+ requireArg(args, 0, "address");
57697
+ const body = parseInput(setSpaceAddressInputSchema, { address: args.join(" ") });
57698
+ return {
57699
+ data: await requireClient().put(pathOf(spaceOperations.setAddress), body),
57700
+ render: renderRecord
57701
+ };
57702
+ }
57703
+ },
57704
+ {
57705
+ spec: {
57706
+ path: ["space", "address", "clear"],
57707
+ summary: "Clear the Space's postal address.",
57708
+ args: [],
57709
+ flags: [],
57710
+ scopes: [...spaceOperations.setAddress.scopes],
57711
+ returns: "SpaceAddress",
57712
+ notes: `${routeOf(spaceOperations.setAddress)} with \`{ "address": null }\`.`
57713
+ },
57714
+ run: async ({ requireClient }) => ({
57715
+ data: await requireClient().put(pathOf(spaceOperations.setAddress), { address: null }),
57716
+ render: renderRecord
57717
+ })
57718
+ }
57719
+ ];
57720
+
57721
+ // src/commands/tap-tags.ts
57722
+ var commands54 = [
57557
57723
  {
57558
57724
  spec: {
57559
57725
  path: ["tap-tags", "tap"],
@@ -57581,7 +57747,7 @@ var BY = {
57581
57747
  type: "string",
57582
57748
  description: "The blocking task's ref (DO-147) or ULID."
57583
57749
  };
57584
- var commands54 = [
57750
+ var commands55 = [
57585
57751
  defineCommand({
57586
57752
  operation: taskBlockOperations.addBlocker,
57587
57753
  path: ["tasks", "block"],
@@ -57647,7 +57813,7 @@ function renderSeries(data, mode) {
57647
57813
  }).join(`
57648
57814
  `);
57649
57815
  }
57650
- var commands55 = [
57816
+ var commands56 = [
57651
57817
  defineCommand({
57652
57818
  operation: taskSeriesOperations.make,
57653
57819
  path: ["tasks", "make-series"],
@@ -57802,7 +57968,7 @@ function repeatFromFlags(flags) {
57802
57968
  out.anchor = "from_completion";
57803
57969
  return out;
57804
57970
  }
57805
- var commands56 = [
57971
+ var commands57 = [
57806
57972
  {
57807
57973
  spec: {
57808
57974
  path: ["capture"],
@@ -58260,7 +58426,7 @@ var commands56 = [
58260
58426
  ];
58261
58427
 
58262
58428
  // src/commands/time-blocks.ts
58263
- var commands57 = [
58429
+ var commands58 = [
58264
58430
  {
58265
58431
  spec: {
58266
58432
  path: ["time-blocks", "record"],
@@ -58441,7 +58607,7 @@ function renderLandedCounts(data, mode) {
58441
58607
  return out.join(`
58442
58608
  `);
58443
58609
  }
58444
- var commands58 = [
58610
+ var commands59 = [
58445
58611
  {
58446
58612
  spec: {
58447
58613
  path: ["today"],
@@ -58813,7 +58979,7 @@ function renderViews(data, mode) {
58813
58979
  function renderView(data, mode) {
58814
58980
  return renderViews([data], mode);
58815
58981
  }
58816
- var commands59 = [
58982
+ var commands60 = [
58817
58983
  defineCommand({
58818
58984
  operation: workViewOperations.list,
58819
58985
  path: ["views", "list"],
@@ -58932,7 +59098,7 @@ var commands59 = [
58932
59098
  ];
58933
59099
 
58934
59100
  // src/commands/worktree-links.ts
58935
- var commands60 = [
59101
+ var commands61 = [
58936
59102
  {
58937
59103
  spec: {
58938
59104
  path: ["worktree-links", "list"],
@@ -59098,7 +59264,8 @@ var FEATURE_COMMANDS = [
59098
59264
  ...commands57,
59099
59265
  ...commands58,
59100
59266
  ...commands59,
59101
- ...commands60
59267
+ ...commands60,
59268
+ ...commands61
59102
59269
  ];
59103
59270
 
59104
59271
  // src/registry.ts
@@ -59291,7 +59458,8 @@ async function dispatchEntity(entity, rest, flags, requireClient) {
59291
59458
  }
59292
59459
  if (op === "delete")
59293
59460
  assertDestructiveAllowed(flags, `delete ${entity.name} ${id}`);
59294
- await client.delete(`${entity.path}/${id}`);
59461
+ const reason = flagString(flags, "reason");
59462
+ await client.delete(`${entity.path}/${id}`, reason ? { query: { reason } } : {});
59295
59463
  return {
59296
59464
  data: op === "archive" ? { ok: true, archived: id, softDelete: true } : { ok: true, deleted: id, softDelete: true },
59297
59465
  render: renderRecord
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.351.0",
3
+ "version": "1.352.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "homepage": "https://github.com/robbeverhelst/do/tree/main/apps/cli",
@@ -27,7 +27,7 @@
27
27
  "compile": "bun build --compile src/index.ts --outfile do",
28
28
  "prepublishOnly": "bun run bundle",
29
29
  "typecheck": "tsc --noEmit",
30
- "build": "tsc --noEmit",
30
+ "build": "bun run bundle",
31
31
  "test": "bun test",
32
32
  "test:coverage": "bun test --coverage"
33
33
  },
@@ -39,5 +39,11 @@
39
39
  "@types/node": "^25.9.3",
40
40
  "ulidx": "^2.4.1",
41
41
  "zod": "^4.4.3"
42
+ },
43
+ "do": {
44
+ "tier": "backend",
45
+ "coverage": {
46
+ "floor": 90
47
+ }
42
48
  }
43
49
  }