@odla-ai/chapter 0.51.0 → 0.52.1

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}",
@@ -1529,7 +1560,9 @@ var DEFAULT_JOIN_COPY = {
1529
1560
  incomplete: "The payment did not complete. Please try again.",
1530
1561
  consent: "I have read and agree to the pricing and refund policy above.",
1531
1562
  foundingMember: "Founding member rate",
1532
- foundingDiscount: "Founding discount"
1563
+ foundingDiscount: "Founding discount",
1564
+ standardRate: "Standard rate",
1565
+ yourRate: "Your rate"
1533
1566
  },
1534
1567
  done: {
1535
1568
  label: "You're booked",
@@ -2158,6 +2191,10 @@ function defineChapter(config) {
2158
2191
  if (!config.emails || typeof config.emails.notificationEmail !== "string" || config.emails.notificationEmail === "") {
2159
2192
  throw new Error("defineChapter.emails.notificationEmail: required in chapter mode");
2160
2193
  }
2194
+ const fromName = config.emails.fromName;
2195
+ if (fromName !== void 0 && (typeof fromName !== "string" || /[\r\n<>]/.test(fromName) || fromName.trim().length > 100)) {
2196
+ throw new Error("defineChapter.emails.fromName: must be one line of at most 100 characters with no angle brackets");
2197
+ }
2161
2198
  if (!config.prices || typeof config.prices.standardCents !== "number") {
2162
2199
  throw new Error("defineChapter.prices.standardCents: required in chapter mode");
2163
2200
  }
@@ -3153,6 +3190,7 @@ function createChapterIntegration(chapter, options = {}) {
3153
3190
  description: "Signing secret for the read-only network commercial Stripe webhook endpoint.",
3154
3191
  required: true
3155
3192
  });
3193
+ secrets.push(...federationEdgeSecrets(chapter));
3156
3194
  return {
3157
3195
  id: "chapter",
3158
3196
  title: `Chapter: ${chapter.name}`,
@@ -3196,6 +3234,41 @@ function createChapterIntegration(chapter, options = {}) {
3196
3234
  runbooks
3197
3235
  };
3198
3236
  }
3237
+ function federationEdgeSecrets(chapter) {
3238
+ const secrets = chapter.network.targets.map((target) => ({
3239
+ name: target.secretName,
3240
+ 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}".`,
3241
+ required: true,
3242
+ edge: { role: "leader", peers: [target.id] }
3243
+ }));
3244
+ const peers = [.../* @__PURE__ */ new Set([
3245
+ ...chapter.network.readers.map((reader) => reader.id),
3246
+ ...(chapter.builderLibrary?.sources ?? []).map((source) => source.sourceId)
3247
+ ])];
3248
+ if (peers.length > 0) secrets.push({
3249
+ name: "network_share_secret",
3250
+ 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}".`,
3251
+ required: true,
3252
+ edge: { role: "follower", peers }
3253
+ });
3254
+ return secrets;
3255
+ }
3256
+
3257
+ // src/network-edge.ts
3258
+ var EDGE_UNPAIRED = "edge_unpaired";
3259
+ function pairingCommand(leaderId, targetId, env) {
3260
+ const scope2 = env ? ` --env ${env}` : "";
3261
+ return `odla-ai chapter edge pair --leader ${leaderId} --follower ${targetId}${scope2}`;
3262
+ }
3263
+ function edgeUnpaired(leaderId, target, env) {
3264
+ return {
3265
+ code: EDGE_UNPAIRED,
3266
+ target: target.id,
3267
+ secretName: target.secretName,
3268
+ message: `edge to "${target.id}" is unpaired: leader vault slot "${target.secretName}" is empty`,
3269
+ hint: pairingCommand(leaderId, target.id, env)
3270
+ };
3271
+ }
3199
3272
 
3200
3273
  // src/membership-policy.ts
3201
3274
  function seatPurchaseDeadline(offering, member) {
@@ -6565,6 +6638,12 @@ function planDelivery(input) {
6565
6638
  }
6566
6639
 
6567
6640
  // src/notify.ts
6641
+ function fromAddress(configured, name) {
6642
+ const display = name?.trim();
6643
+ if (!display) return configured;
6644
+ const angled = configured.match(/<([^>]+)>/);
6645
+ return { name: display, email: (angled ? angled[1] : configured).trim() };
6646
+ }
6568
6647
  async function sendTemplated(deps, input) {
6569
6648
  const { emailLog: emailLog2 } = await deps.db.query({ emailLog: { $: { where: { dedupeKey: input.dedupeKey } } } });
6570
6649
  const prior = Array.isArray(emailLog2) ? emailLog2 : [];
@@ -6627,7 +6706,7 @@ async function sendTemplated(deps, input) {
6627
6706
  let messageId;
6628
6707
  try {
6629
6708
  const response = await deps.sender.send({
6630
- from: deps.from,
6709
+ from: fromAddress(deps.from, input.group.fromName),
6631
6710
  to: [decision.to],
6632
6711
  subject: String(row2.subject),
6633
6712
  text: String(row2.body),
@@ -6660,6 +6739,7 @@ function emailGroupFrom(row2) {
6660
6739
  name: String(row2.name ?? ""),
6661
6740
  replyTo: str2(row2.replyTo) ?? "",
6662
6741
  debugEmail: str2(row2.debugEmail),
6742
+ fromName: str2(row2.fromName),
6663
6743
  refundPolicyText: str2(row2.refundPolicyText),
6664
6744
  commitmentText: str2(row2.commitmentText),
6665
6745
  normsText: str2(row2.normsText),
@@ -9426,6 +9506,7 @@ export {
9426
9506
  CHAPTER_PROMOTION_ADAPTER,
9427
9507
  DEFAULT_CHAPTER_COPY,
9428
9508
  DEFAULT_SHARE_FIELDS,
9509
+ EDGE_UNPAIRED,
9429
9510
  GIFT_OFFER_CATALOG_VERSION,
9430
9511
  JOURNEY_ENDINGS,
9431
9512
  JOURNEY_STAGES,
@@ -9506,6 +9587,7 @@ export {
9506
9587
  defaultCrm,
9507
9588
  defineChapter,
9508
9589
  drainMembershipEffects,
9590
+ edgeUnpaired,
9509
9591
  emailGroupFrom,
9510
9592
  endForSlot,
9511
9593
  exportChapterPromotion,
@@ -9565,6 +9647,7 @@ export {
9565
9647
  normalizeSharedRecord,
9566
9648
  normalizeWebhookEvent,
9567
9649
  offerableTiers,
9650
+ pairingCommand,
9568
9651
  paymentsReady,
9569
9652
  personInputFromApp,
9570
9653
  planDelivery,