@analyticscli/growth-engineer 0.1.1-preview.13 → 0.1.1-preview.14

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.
@@ -140,12 +140,20 @@ function printMessages(messages) {
140
140
  }
141
141
  }
142
142
 
143
- function chunkMessage(content) {
143
+ function truncateDiscordText(value, maxLength) {
144
+ const text = String(value || "").trim();
145
+ if (text.length <= maxLength) {
146
+ return text;
147
+ }
148
+ return `${text.slice(0, Math.max(0, maxLength - 3)).trim()}...`;
149
+ }
150
+
151
+ function chunkEmbedDescription(content) {
144
152
  const chunks = [];
145
- const maxLength = 1900;
146
- let remaining = content.trim();
153
+ const maxLength = 4000;
154
+ let remaining = String(content || "").trim();
147
155
 
148
- while (remaining.length > maxLength) {
156
+ while (remaining.length > maxLength && chunks.length < 9) {
149
157
  let splitAt = remaining.lastIndexOf("\n", maxLength);
150
158
  if (splitAt < maxLength * 0.5) {
151
159
  splitAt = remaining.lastIndexOf(" ", maxLength);
@@ -158,27 +166,69 @@ function chunkMessage(content) {
158
166
  }
159
167
 
160
168
  if (remaining) {
161
- chunks.push(remaining);
169
+ chunks.push(truncateDiscordText(remaining, maxLength));
162
170
  }
163
- return chunks;
171
+ return chunks.slice(0, 10);
164
172
  }
165
173
 
166
- function normalizeEmbedPayload(input) {
167
- if (process.env.OPENCLAW_DISCORD_DELIVERY_FORMAT !== "embed" && !process.argv.includes("--json")) {
174
+ function plainTextToEmbedPayload(input) {
175
+ const text = String(input || "").trim();
176
+ if (!text) {
168
177
  return null;
169
178
  }
170
- try {
171
- const payload = JSON.parse(String(input || ""));
172
- const embeds = Array.isArray(payload.embeds) ? payload.embeds : [];
173
- if (embeds.length === 0) return null;
174
- return {
175
- content: String(payload.content || "").slice(0, 2000),
176
- embeds: embeds.slice(0, 10),
177
- fallbackText: String(payload.fallbackText || payload.fallback_text || "").trim(),
178
- };
179
- } catch {
180
- return null;
179
+
180
+ const lines = text.split(/\r?\n/);
181
+ const firstLineIndex = Math.max(0, lines.findIndex((line) => line.trim()));
182
+ const firstLine = lines[firstLineIndex]?.trim() || "OpenClaw update";
183
+ const title = truncateDiscordText(firstLine, 256);
184
+ const body = lines.slice(firstLineIndex + 1).join("\n").trim();
185
+ const descriptionChunks = chunkEmbedDescription(body || (firstLine.length > 256 ? firstLine : ""));
186
+ const embeds = [];
187
+
188
+ if (descriptionChunks.length === 0) {
189
+ embeds.push({
190
+ title,
191
+ color: 0x2f81f7,
192
+ timestamp: new Date().toISOString(),
193
+ });
194
+ } else {
195
+ for (const [index, description] of descriptionChunks.entries()) {
196
+ embeds.push({
197
+ ...(index === 0 ? { title } : {}),
198
+ description,
199
+ color: 0x2f81f7,
200
+ timestamp: new Date().toISOString(),
201
+ });
202
+ }
203
+ }
204
+
205
+ return {
206
+ content: "",
207
+ embeds,
208
+ fallbackText: text,
209
+ };
210
+ }
211
+
212
+ function normalizeEmbedPayload(input) {
213
+ const raw = String(input || "");
214
+ if (process.env.OPENCLAW_DISCORD_DELIVERY_FORMAT === "embed" || process.argv.includes("--json") || raw.trim().startsWith("{")) {
215
+ try {
216
+ const payload = JSON.parse(raw);
217
+ const embeds = Array.isArray(payload.embeds) ? payload.embeds : [];
218
+ if (embeds.length > 0) {
219
+ return {
220
+ content: String(payload.content || "").slice(0, 2000),
221
+ embeds: embeds.slice(0, 10),
222
+ fallbackText: String(payload.fallbackText || payload.fallback_text || "").trim(),
223
+ };
224
+ }
225
+ } catch {
226
+ if (process.argv.includes("--json")) {
227
+ return null;
228
+ }
229
+ }
181
230
  }
231
+ return plainTextToEmbedPayload(raw);
182
232
  }
183
233
 
184
234
  async function sendDiscordPayload(payload) {
@@ -204,26 +254,7 @@ async function sendMessage(content) {
204
254
  return await sendDiscordPayload(embedPayload);
205
255
  }
206
256
 
207
- const chunks = chunkMessage(content);
208
- if (chunks.length === 0) {
209
- throw new Error("Refusing to send an empty message.");
210
- }
211
-
212
- const sent = [];
213
- for (const target of DISCORD_TARGETS) {
214
- await validateTargetChannel(target);
215
- for (const chunk of chunks) {
216
- const message = await discordFetch(`/channels/${target.channelId}/messages`, {
217
- method: "POST",
218
- body: JSON.stringify({
219
- allowed_mentions: { parse: [], users: target.allowedMentionUsers },
220
- content: chunk,
221
- }),
222
- });
223
- sent.push({ target, message });
224
- }
225
- }
226
- return sent;
257
+ throw new Error("Refusing to send an empty message.");
227
258
  }
228
259
 
229
260
  async function readStdin() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analyticscli/growth-engineer",
3
- "version": "0.1.1-preview.13",
3
+ "version": "0.1.1-preview.14",
4
4
  "description": "Growth Engineer CLI for connector setup, scheduling, health checks, and OpenClaw-compatible growth runs.",
5
5
  "license": "MIT",
6
6
  "type": "module",