activitysmith-cli
Advanced tools
+2
-2
| { | ||
| "name": "activitysmith-cli", | ||
| "version": "1.3.1", | ||
| "version": "1.4.0", | ||
| "description": "Command-line interface for ActivitySmith. Send push notifications and manage Live Activities from your terminal.", | ||
@@ -43,5 +43,5 @@ "keywords": [ | ||
| "dependencies": { | ||
| "activitysmith": "^1.3.1", | ||
| "activitysmith": "^1.4.0", | ||
| "commander": "^12.1.0" | ||
| } | ||
| } |
+38
-1
@@ -138,3 +138,3 @@ # ActivitySmith CLI | ||
| There are four types of Live Activities: | ||
| There are five types of Live Activities: | ||
@@ -145,2 +145,3 @@ - `stats`: best for showing business numbers side by side, such as revenue, sales, new users, conversion, refunds, or any other value you want visible at a glance | ||
| - `progress`: best for tracking real-time progress with percentage, like tasks, backups, migrations, syncs, or uploads | ||
| - `alert`: best for status updates, such as feature adoption, reactivation, onboarding blockers, incidents, escalations, and other operational states | ||
@@ -242,2 +243,37 @@ ### Start & Update Live Activity | ||
| #### Alert | ||
| <p align="center"> | ||
| <img | ||
| src="https://cdn.activitysmith.com/features/alert-live-activity.png" | ||
| alt="Alert Live Activity stream example" | ||
| width="680" | ||
| /> | ||
| </p> | ||
| ```bash | ||
| activitysmith activity stream customer-ops \ | ||
| --content-state '{ | ||
| "title": "Reactivation", | ||
| "message": "Lumen came back after 2 weeks", | ||
| "type": "alert", | ||
| "icon": { | ||
| "symbol": "cloud.sun", | ||
| "color": "yellow" | ||
| }, | ||
| "badge": { | ||
| "title": "Customer", | ||
| "color": "magenta" | ||
| } | ||
| }' | ||
| ``` | ||
| The `icon.symbol` value is an Apple SF Symbol name. Browse the catalog with one of these tools: | ||
| - [ActivitySmith app](https://apps.apple.com/us/app/activitysmith/id6752254835) - Open Settings -> SF Symbols to browse 45 hand-picked icons ready to use | ||
| - [SF Symbols](https://developer.apple.com/sf-symbols/) - Apple's official macOS app | ||
| - [Interactful](https://apps.apple.com/app/interactful/id1528095640) - free third-party iOS app listing all SF Symbols under Foundations -> Iconography | ||
| `icon` and `badge` are optional. If you omit either one, that element is not shown in the Live Activity. | ||
| ### End Live Activity | ||
@@ -264,2 +300,3 @@ | ||
| Live Activities can include one optional action button. Use it to open a URL from the Live Activity or trigger a backend webhook. | ||
| For Alert Live Activities, set `color` in `--content-state` to tint the action button. `icon.color` and `badge.color` only affect the icon and badge. | ||
@@ -266,0 +303,0 @@ <p align="center"> |
+105
-10
@@ -109,2 +109,7 @@ #!/usr/bin/env node | ||
| .option("--type <type>", "Content state type") | ||
| .option("--message <message>", "Alert message") | ||
| .option("--icon-symbol <symbol>", "Alert SF Symbol name") | ||
| .option("--icon-color <color>", "Alert icon color") | ||
| .option("--badge-title <title>", "Alert badge title") | ||
| .option("--badge-color <color>", "Alert badge color") | ||
| .option( | ||
@@ -240,5 +245,6 @@ "--number-of-steps <number>", | ||
| const liveActivityMetricColors = new Set([ | ||
| const liveActivityColors = new Set([ | ||
| "blue", | ||
| "cyan", | ||
| "gray", | ||
| "green", | ||
@@ -253,2 +259,4 @@ "lime", | ||
| const liveActivityColorList = [...liveActivityColors].join(", "); | ||
| const validateContentState = (contentState, mode) => { | ||
@@ -267,9 +275,13 @@ const normalizedType = | ||
| normalizedType !== "metrics" && | ||
| normalizedType !== "stats" | ||
| normalizedType !== "stats" && | ||
| normalizedType !== "alert" | ||
| ) { | ||
| throw new Error( | ||
| "contentState.type must be one of: segmented_progress, progress, metrics, stats" | ||
| "contentState.type must be one of: segmented_progress, progress, metrics, stats, alert" | ||
| ); | ||
| } | ||
| const hasMessage = hasOwn(contentState, "message"); | ||
| const hasIcon = hasOwn(contentState, "icon"); | ||
| const hasBadge = hasOwn(contentState, "badge"); | ||
| const hasNumberOfSteps = hasOwn(contentState, "numberOfSteps"); | ||
@@ -310,3 +322,38 @@ const hasCurrentStep = hasOwn(contentState, "currentStep"); | ||
| const hasProgressFields = hasPercentage || hasValue || hasUpperLimit; | ||
| const hasAlertFields = hasMessage || hasIcon || hasBadge; | ||
| if (hasIcon) { | ||
| assertPlainObject(contentState.icon, "contentState.icon"); | ||
| if (!isNonEmptyString(contentState.icon.symbol)) { | ||
| throw new Error("contentState.icon.symbol is required"); | ||
| } | ||
| if (contentState.icon.color !== undefined) { | ||
| if (!isNonEmptyString(contentState.icon.color)) { | ||
| throw new Error("contentState.icon.color must be a string"); | ||
| } | ||
| if (!liveActivityColors.has(contentState.icon.color.trim())) { | ||
| throw new Error( | ||
| `contentState.icon.color must be one of: ${liveActivityColorList}` | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| if (hasBadge) { | ||
| assertPlainObject(contentState.badge, "contentState.badge"); | ||
| if (!isNonEmptyString(contentState.badge.title)) { | ||
| throw new Error("contentState.badge.title is required"); | ||
| } | ||
| if (contentState.badge.color !== undefined) { | ||
| if (!isNonEmptyString(contentState.badge.color)) { | ||
| throw new Error("contentState.badge.color must be a string"); | ||
| } | ||
| if (!liveActivityColors.has(contentState.badge.color.trim())) { | ||
| throw new Error( | ||
| `contentState.badge.color must be one of: ${liveActivityColorList}` | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| if (hasSegmentedFields && hasProgressFields) { | ||
@@ -324,2 +371,11 @@ throw new Error( | ||
| if ( | ||
| hasAlertFields && | ||
| (hasMetrics || hasSegmentedFields || hasProgressFields || hasStepColor) | ||
| ) { | ||
| throw new Error( | ||
| "Do not mix alert fields with metrics, segmented_progress, or progress fields in the same contentState" | ||
| ); | ||
| } | ||
| if (hasMetrics) { | ||
@@ -357,7 +413,5 @@ if (!Array.isArray(contentState.metrics) || contentState.metrics.length === 0) { | ||
| } | ||
| if (!liveActivityMetricColors.has(metric.color.trim())) { | ||
| if (!liveActivityColors.has(metric.color.trim())) { | ||
| throw new Error( | ||
| `contentState.metrics[${index}].color must be one of: ${[ | ||
| ...liveActivityMetricColors, | ||
| ].join(", ")}` | ||
| `contentState.metrics[${index}].color must be one of: ${liveActivityColorList}` | ||
| ); | ||
@@ -392,2 +446,9 @@ } | ||
| if (effectiveType === "alert") { | ||
| if (!isNonEmptyString(contentState.message)) { | ||
| throw new Error(`alert ${mode} requires contentState.message`); | ||
| } | ||
| return; | ||
| } | ||
| if (!hasPercentage && !hasValue) { | ||
@@ -426,5 +487,12 @@ throw new Error( | ||
| if (!hasSegmentedFields && !hasProgressFields && !hasMetrics) { | ||
| if (effectiveType === "alert") { | ||
| if (!isNonEmptyString(contentState.message)) { | ||
| throw new Error(`alert ${mode} requires contentState.message`); | ||
| } | ||
| return; | ||
| } | ||
| if (!hasSegmentedFields && !hasProgressFields && !hasMetrics && !hasAlertFields) { | ||
| throw new Error( | ||
| `contentState for activity ${mode} must include metrics, segmented_progress fields, or progress fields` | ||
| `contentState for activity ${mode} must include metrics, segmented_progress fields, progress fields, or alert fields` | ||
| ); | ||
@@ -448,2 +516,6 @@ } | ||
| } | ||
| if (hasAlertFields && !isNonEmptyString(contentState.message)) { | ||
| throw new Error(`alert ${mode} requires contentState.message`); | ||
| } | ||
| }; | ||
@@ -573,2 +645,26 @@ | ||
| if (options.message !== undefined) { | ||
| contentState.message = options.message; | ||
| } | ||
| if (options.iconSymbol !== undefined || options.iconColor !== undefined) { | ||
| contentState.icon = {}; | ||
| if (options.iconSymbol !== undefined) { | ||
| contentState.icon.symbol = options.iconSymbol; | ||
| } | ||
| if (options.iconColor !== undefined) { | ||
| contentState.icon.color = options.iconColor; | ||
| } | ||
| } | ||
| if (options.badgeTitle !== undefined || options.badgeColor !== undefined) { | ||
| contentState.badge = {}; | ||
| if (options.badgeTitle !== undefined) { | ||
| contentState.badge.title = options.badgeTitle; | ||
| } | ||
| if (options.badgeColor !== undefined) { | ||
| contentState.badge.color = options.badgeColor; | ||
| } | ||
| } | ||
| if (options.numberOfSteps !== undefined) { | ||
@@ -971,3 +1067,2 @@ contentState.numberOfSteps = options.numberOfSteps; | ||
| : null, | ||
| response?.timestamp ? `Timestamp: ${response.timestamp}` : null, | ||
| ]); | ||
@@ -974,0 +1069,0 @@ } catch (error) { |
49765
10.16%1069
8.64%447
9.02%Updated