@odla-ai/chapter 0.51.0 → 0.52.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.
package/dist/index.js CHANGED
@@ -502,7 +502,7 @@ var giftRedemptionAttemptsEntity = { attrs: {
502
502
  } };
503
503
 
504
504
  // src/payment-recovery-schema.ts
505
- var field = (type, unique = false) => ({ type, unique, indexed: type !== "json", optional: false });
505
+ var field = (type, unique = false, optional = false) => ({ type, unique, indexed: type !== "json", optional });
506
506
  var paymentRecoveryReviewsEntity = { attrs: {
507
507
  id: field("string", true),
508
508
  eventId: field("string"),
@@ -511,7 +511,9 @@ var paymentRecoveryReviewsEntity = { attrs: {
511
511
  previousDigest: field("string"),
512
512
  providerDigest: field("string"),
513
513
  scope: field("string"),
514
- previousState: field("json")
514
+ previousState: field("json"),
515
+ action: field("string", false, true),
516
+ reason: field("string", false, true)
515
517
  } };
516
518
 
517
519
  // src/schema-stripe.ts
@@ -707,7 +709,7 @@ var boolean = (flags = {}) => ({
707
709
  indexed: flags.indexed ?? false,
708
710
  optional: false
709
711
  });
710
- var json2 = () => ({ type: "json", unique: false, indexed: false, optional: false });
712
+ var json2 = (flags = {}) => ({ type: "json", unique: false, indexed: false, optional: flags.optional ?? false });
711
713
  var id4 = () => string2({ unique: true, indexed: true });
712
714
  var events = { attrs: {
713
715
  id: id4(),
@@ -736,6 +738,10 @@ var exceptions = { attrs: {
736
738
  status: string2({ indexed: true }),
737
739
  resolvedDigest: string2({ optional: true }),
738
740
  incomingDigest: string2({ optional: true }),
741
+ incomingFact: json2({ optional: true }),
742
+ reviewedDigest: string2({ optional: true }),
743
+ resolvedBy: string2({ optional: true }),
744
+ reviewId: string2({ optional: true }),
739
745
  detail: json2(),
740
746
  updatedAt: number2({ indexed: true })
741
747
  } };
@@ -874,6 +880,7 @@ var groups = {
874
880
  stripePublishableKey: attr7("string", { optional: true }),
875
881
  notificationEmail: attr7("string"),
876
882
  replyTo: attr7("string"),
883
+ fromName: attr7("string", { optional: true }),
877
884
  debugEmail: attr7("string", { optional: true }),
878
885
  calendarLink: attr7("string", { optional: true }),
879
886
  disclaimerText: attr7("string"),
@@ -1070,6 +1077,13 @@ function defaultCrm(mode) {
1070
1077
  };
1071
1078
  }
1072
1079
 
1080
+ // src/named-seat-accepted-template.ts
1081
+ var SEAT_ACCEPTED_TEMPLATE = {
1082
+ enabled: true,
1083
+ subject: "{{recipientName}} accepted your {{chapterName}} seat",
1084
+ text: "Hi {{firstName}},\n\n{{recipientName}} has accepted the {{tierName}} seat you purchased at {{chapterName}}. They will complete their own application and any membership requirements from here; nothing more is needed from you.\n\nWarmly,\n{{chapterName}}"
1085
+ };
1086
+
1073
1087
  // src/group.ts
1074
1088
  var DEFAULT_SCHEDULING = {
1075
1089
  slotMinutes: 45,
@@ -1091,7 +1105,16 @@ ${name}`;
1091
1105
  text: `A new application came in for ${name}.
1092
1106
 
1093
1107
  Name: {{firstName}} {{lastName}}
1094
- Email: {{email}}`
1108
+ Email: {{email}}
1109
+ Tier: {{tier}}
1110
+
1111
+ Review it: {{adminUrl}}`
1112
+ },
1113
+ submitConfirmation: {
1114
+ subject: `We received your application to ${name}`,
1115
+ text: `Hi {{firstName}},
1116
+
1117
+ Thanks for applying to ${name}. {{submitNextStep}}${sign}`
1095
1118
  },
1096
1119
  paymentConfirmation: {
1097
1120
  subject: `Welcome to ${name}`,
@@ -1103,14 +1126,17 @@ Your membership payment{{paymentAmountSummary}} is confirmed. {{paymentNextStep}
1103
1126
  subject: `Your ${name} intro call`,
1104
1127
  text: `Hi {{firstName}},
1105
1128
 
1106
- Looking forward to our call at {{meetingTime}}. {{meetingLink}}${sign}`
1129
+ You are all set{{paymentAmountSummary}}. Looking forward to our call on {{startAt}}.
1130
+
1131
+ Join here: {{meetingLink}}${sign}`
1107
1132
  },
1108
1133
  onboardingInvite: {
1109
1134
  subject: `You're in, ${name}`,
1110
1135
  text: `Hi {{firstName}},
1111
1136
 
1112
1137
  Welcome to ${name}. Your member area is here: {{membersUrl}}${sign}`
1113
- }
1138
+ },
1139
+ seatAccepted: SEAT_ACCEPTED_TEMPLATE
1114
1140
  };
1115
1141
  }
1116
1142
  function buildGroupSeed(config) {
@@ -1139,6 +1165,7 @@ function buildGroupSeed(config) {
1139
1165
  schedulingJson: scheduling
1140
1166
  };
1141
1167
  if (emails?.debugEmail) row2.debugEmail = emails.debugEmail;
1168
+ if (emails?.fromName?.trim()) row2.fromName = emails.fromName.trim();
1142
1169
  if (policy.commitmentText) row2.commitmentText = policy.commitmentText;
1143
1170
  if (policy.normsText) row2.normsText = policy.normsText;
1144
1171
  return row2;
@@ -1359,6 +1386,8 @@ var DEFAULT_ADMIN_COPY = {
1359
1386
  delivery: "Delivery",
1360
1387
  notificationAddress: "Notification address",
1361
1388
  replyTo: "Reply-to",
1389
+ fromName: "From name",
1390
+ fromNameHint: "Shown as the sender; the address stays the Worker's configured one.",
1362
1391
  debugInbox: "Debug inbox",
1363
1392
  debugInboxHint: "Non-production sends redirect here.",
1364
1393
  sendTest: "Send test",
@@ -1430,6 +1459,8 @@ var DEFAULT_ADMIN_COPY = {
1430
1459
  transportReachable: "transport reachable",
1431
1460
  transportUnavailable: "transport unavailable",
1432
1461
  transportNotChecked: "transport not checked",
1462
+ edgeUnpaired: "edge unpaired",
1463
+ edgeUnpairedHint: "This follower's shared secret is not in the leader vault, so nothing can be signed. Pair both vaults from an enrolled machine:",
1433
1464
  bindingTransport: "service binding {binding}",
1434
1465
  urlTransport: "public URL transport",
1435
1466
  activeConfiguration: "active configuration r{revision}",
@@ -2158,6 +2189,10 @@ function defineChapter(config) {
2158
2189
  if (!config.emails || typeof config.emails.notificationEmail !== "string" || config.emails.notificationEmail === "") {
2159
2190
  throw new Error("defineChapter.emails.notificationEmail: required in chapter mode");
2160
2191
  }
2192
+ const fromName = config.emails.fromName;
2193
+ if (fromName !== void 0 && (typeof fromName !== "string" || /[\r\n<>]/.test(fromName) || fromName.trim().length > 100)) {
2194
+ throw new Error("defineChapter.emails.fromName: must be one line of at most 100 characters with no angle brackets");
2195
+ }
2161
2196
  if (!config.prices || typeof config.prices.standardCents !== "number") {
2162
2197
  throw new Error("defineChapter.prices.standardCents: required in chapter mode");
2163
2198
  }
@@ -3153,6 +3188,7 @@ function createChapterIntegration(chapter, options = {}) {
3153
3188
  description: "Signing secret for the read-only network commercial Stripe webhook endpoint.",
3154
3189
  required: true
3155
3190
  });
3191
+ secrets.push(...federationEdgeSecrets(chapter));
3156
3192
  return {
3157
3193
  id: "chapter",
3158
3194
  title: `Chapter: ${chapter.name}`,
@@ -3196,6 +3232,41 @@ function createChapterIntegration(chapter, options = {}) {
3196
3232
  runbooks
3197
3233
  };
3198
3234
  }
3235
+ function federationEdgeSecrets(chapter) {
3236
+ const secrets = chapter.network.targets.map((target) => ({
3237
+ name: target.secretName,
3238
+ description: `Shared secret for the signed federation edge to follower "${target.id}"; pair both vaults with "odla-ai chapter edge pair --leader ${chapter.id} --follower ${target.id}".`,
3239
+ required: true,
3240
+ edge: { role: "leader", peers: [target.id] }
3241
+ }));
3242
+ const peers = [.../* @__PURE__ */ new Set([
3243
+ ...chapter.network.readers.map((reader) => reader.id),
3244
+ ...(chapter.builderLibrary?.sources ?? []).map((source) => source.sourceId)
3245
+ ])];
3246
+ if (peers.length > 0) secrets.push({
3247
+ name: "network_share_secret",
3248
+ description: `Shared secret every declared leader (${peers.join(", ")}) signs federation requests with; pair it with "odla-ai chapter edge pair --leader <leader> --follower ${chapter.id}".`,
3249
+ required: true,
3250
+ edge: { role: "follower", peers }
3251
+ });
3252
+ return secrets;
3253
+ }
3254
+
3255
+ // src/network-edge.ts
3256
+ var EDGE_UNPAIRED = "edge_unpaired";
3257
+ function pairingCommand(leaderId, targetId, env) {
3258
+ const scope2 = env ? ` --env ${env}` : "";
3259
+ return `odla-ai chapter edge pair --leader ${leaderId} --follower ${targetId}${scope2}`;
3260
+ }
3261
+ function edgeUnpaired(leaderId, target, env) {
3262
+ return {
3263
+ code: EDGE_UNPAIRED,
3264
+ target: target.id,
3265
+ secretName: target.secretName,
3266
+ message: `edge to "${target.id}" is unpaired: leader vault slot "${target.secretName}" is empty`,
3267
+ hint: pairingCommand(leaderId, target.id, env)
3268
+ };
3269
+ }
3199
3270
 
3200
3271
  // src/membership-policy.ts
3201
3272
  function seatPurchaseDeadline(offering, member) {
@@ -6565,6 +6636,12 @@ function planDelivery(input) {
6565
6636
  }
6566
6637
 
6567
6638
  // src/notify.ts
6639
+ function fromAddress(configured, name) {
6640
+ const display = name?.trim();
6641
+ if (!display) return configured;
6642
+ const angled = configured.match(/<([^>]+)>/);
6643
+ return { name: display, email: (angled ? angled[1] : configured).trim() };
6644
+ }
6568
6645
  async function sendTemplated(deps, input) {
6569
6646
  const { emailLog: emailLog2 } = await deps.db.query({ emailLog: { $: { where: { dedupeKey: input.dedupeKey } } } });
6570
6647
  const prior = Array.isArray(emailLog2) ? emailLog2 : [];
@@ -6627,7 +6704,7 @@ async function sendTemplated(deps, input) {
6627
6704
  let messageId;
6628
6705
  try {
6629
6706
  const response = await deps.sender.send({
6630
- from: deps.from,
6707
+ from: fromAddress(deps.from, input.group.fromName),
6631
6708
  to: [decision.to],
6632
6709
  subject: String(row2.subject),
6633
6710
  text: String(row2.body),
@@ -6660,6 +6737,7 @@ function emailGroupFrom(row2) {
6660
6737
  name: String(row2.name ?? ""),
6661
6738
  replyTo: str2(row2.replyTo) ?? "",
6662
6739
  debugEmail: str2(row2.debugEmail),
6740
+ fromName: str2(row2.fromName),
6663
6741
  refundPolicyText: str2(row2.refundPolicyText),
6664
6742
  commitmentText: str2(row2.commitmentText),
6665
6743
  normsText: str2(row2.normsText),
@@ -9426,6 +9504,7 @@ export {
9426
9504
  CHAPTER_PROMOTION_ADAPTER,
9427
9505
  DEFAULT_CHAPTER_COPY,
9428
9506
  DEFAULT_SHARE_FIELDS,
9507
+ EDGE_UNPAIRED,
9429
9508
  GIFT_OFFER_CATALOG_VERSION,
9430
9509
  JOURNEY_ENDINGS,
9431
9510
  JOURNEY_STAGES,
@@ -9506,6 +9585,7 @@ export {
9506
9585
  defaultCrm,
9507
9586
  defineChapter,
9508
9587
  drainMembershipEffects,
9588
+ edgeUnpaired,
9509
9589
  emailGroupFrom,
9510
9590
  endForSlot,
9511
9591
  exportChapterPromotion,
@@ -9565,6 +9645,7 @@ export {
9565
9645
  normalizeSharedRecord,
9566
9646
  normalizeWebhookEvent,
9567
9647
  offerableTiers,
9648
+ pairingCommand,
9568
9649
  paymentsReady,
9569
9650
  personInputFromApp,
9570
9651
  planDelivery,