From 45a2ce0f40f57215bfe72045bdf34605c28251e3 Mon Sep 17 00:00:00 2001 From: bardonadam Date: Sun, 20 Sep 2026 12:28:58 +0700 Subject: [PATCH] feat: add Value Live Activities in v1.12.0 --- CHANGELOG.md | 4 +++ README.md | 86 +++++++++++++++++++++++++++--------------------- package.json | 4 +-- src/cli.js | 28 +++++++++++----- test/cli.test.js | 22 +++++++++++++ 5 files changed, 97 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6bf11e..8e9a429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.12.0 + +Adds the new `value` Live Activity type. + ## 1.11.0 This version adds support for Metadata and iOS deep links, and expands Tags support when updating or ending Live Activities. diff --git a/README.md b/README.md index e7add21..faf2e45 100644 --- a/README.md +++ b/README.md @@ -158,33 +158,41 @@ activitysmith push \ Choose the Live Activity type that matches what you want to show: -![Stats Live Activity with six labeled sales metrics](https://cdn.activitysmith.com/features/stats-live-activity.png) +- ![Value Live Activity showing revenue with a growth badge](https://cdn.activitysmith.com/features/value-live-activity.png) **Value**: Show a single value on your Lock Screen, such as revenue, a queue count, or a temperature. -**Stats**: Show up to 8 labeled values on your Lock Screen, from revenue and orders to uptime and conversion. +- ![Stats Live Activity with six labeled sales metrics](https://cdn.activitysmith.com/features/stats-live-activity.png) **Stats**: Show up to 8 labeled values on your Lock Screen, from revenue and orders to uptime and conversion. -![Metrics Live Activity with CPU and memory values](https://cdn.activitysmith.com/features/metrics-live-activity-start.png) +- ![Alert Live Activity showing a customer reactivation update](https://cdn.activitysmith.com/features/alert-live-activity.png) **Alert**: Show status updates with a clear message, badge, and icon. When you add an action button, `color` controls the button tint. -**Metrics**: Track two related values with segmented bars, such as CPU and memory. +- ![Metrics Live Activity with CPU and memory values](https://cdn.activitysmith.com/features/metrics-live-activity-start.png) **Metrics**: Track two related values with segmented bars, such as CPU and memory. -![Segmented Progress Live Activity showing a workflow step](https://cdn.activitysmith.com/features/update-live-activity.png) +- ![Segmented Progress Live Activity showing a workflow step](https://cdn.activitysmith.com/features/update-live-activity.png) **Segmented Progress**: Show progress through a known set of steps, like build, test, deploy, and verify. -**Segmented Progress**: Show progress through a known set of steps, like build, test, deploy, and verify. +- ![Progress Live Activity showing percentage completion](https://cdn.activitysmith.com/features/progress-live-activity.png) **Progress**: Show percentage progress for jobs that move continuously toward completion. -![Progress Live Activity showing percentage completion](https://cdn.activitysmith.com/features/progress-live-activity.png) +- ![Timer Live Activity showing a benchmark run countdown](https://cdn.activitysmith.com/features/timer-live-activity.png) **Timer**: Count down from a duration, or count up from 00:00 while a job runs. -**Progress**: Show percentage progress for jobs that move continuously toward completion. - -![Alert Live Activity showing a customer reactivation update](https://cdn.activitysmith.com/features/alert-live-activity.png) +### Start & Update Live Activity -**Alert**: Show status updates with a clear message, badge, and icon. When you add an action button, `color` controls the button tint. +Use a stable `stream_key` to identify the metric, job, deployment, or system you want to keep visible. The first `activity stream` command starts the Live Activity. Later commands with the same `stream_key` update it. -![Timer Live Activity showing a benchmark run countdown](https://cdn.activitysmith.com/features/timer-live-activity.png) +#### Value -**Timer**: Count down from a duration, or count up from 00:00 while a job runs. +![Value Live Activity stream example](https://cdn.activitysmith.com/features/value-live-activity.png) -### Start & Update Live Activity +```bash +activitysmith activity stream revenue-today \ + --content-state '{ + "title": "Revenue", + "type": "value", + "value": "$1,240", + "color": "green", + "icon": { "symbol": "dollarsign.circle", "color": "green" }, + "badge": { "title": "↑ 18%", "color": "purple" } + }' +``` -Use a stable `stream_key` to identify the metric, job, deployment, or system you want to keep visible. The first `activity stream` command starts the Live Activity. Later commands with the same `stream_key` update it. +Send a string or number in `value`. Strings keep their formatting, including currency symbols, units, and decimal places. #### Stats @@ -207,6 +215,27 @@ activitysmith activity stream sales-hourly \ }' ``` +#### Alert + +![Alert Live Activity stream example](https://cdn.activitysmith.com/features/alert-live-activity.png) + +```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" + } + }' +``` + #### Metrics ![Metrics Live Activity stream example](https://cdn.activitysmith.com/features/metrics-live-activity-start.png) @@ -253,27 +282,6 @@ activitysmith activity stream search-reindex \ }' ``` -#### Alert - -![Alert Live Activity stream example](https://cdn.activitysmith.com/features/alert-live-activity.png) - -```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" - } - }' -``` - #### Timer ![Timer Live Activity stream example](https://cdn.activitysmith.com/features/timer-live-activity.png) @@ -434,7 +442,7 @@ activitysmith activity stream search-reindex \ Use `--secondary-action` when you want a second button beside the primary `--action`. -The secondary action button is supported for `alert`, `progress`, and `segmented_progress` Live Activities. Both buttons use the same `open_url`, `webhook`, and Apple Shortcut payload shapes. +The secondary action button is supported for `value`, `alert`, `segmented_progress`, and `progress` Live Activities. Both buttons use the same `open_url`, `webhook`, and Apple Shortcut payload shapes. ```bash activitysmith activity stream agent-approval \ @@ -601,3 +609,7 @@ The CLI exits non-zero on non-2xx responses and prints the API error body. That ### [NPM Package](https://www.npmjs.com/package/activitysmith-cli) Install the ActivitySmith CLI from npm + +### [Source Code](https://github.com/ActivitySmithHQ/activitysmith-cli) + +View the CLI source on GitHub diff --git a/package.json b/package.json index 12c216a..c57ed55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "activitysmith-cli", - "version": "1.11.0", + "version": "1.12.0", "description": "Command-line interface for ActivitySmith. Send Push Notifications, start, update, and end Live Activities, and set App Icon Badge Counts from your terminal.", "keywords": [ "activitysmith", @@ -42,7 +42,7 @@ "test": "node --test" }, "dependencies": { - "activitysmith": "^1.11.0", + "activitysmith": "^1.12.0", "commander": "^12.1.0" } } diff --git a/src/cli.js b/src/cli.js index b8e54f0..2ce1731 100755 --- a/src/cli.js +++ b/src/cli.js @@ -214,9 +214,8 @@ const addContentStateOptions = (command, { includeAutoDismiss, includeAutoDismis parseNumberOption("percentage") ) .option( - "--value ", - "Content state value", - parseNumberOption("value") + "--value ", + "Formatted Value readout, or numeric progress value" ) .option( "--upper-limit ", @@ -388,10 +387,11 @@ const validateContentState = (contentState, mode) => { normalizedType !== "metrics" && normalizedType !== "stats" && normalizedType !== "alert" && - normalizedType !== "timer" + normalizedType !== "timer" && + normalizedType !== "value" ) { throw new Error( - "contentState.type must be one of: segmented_progress, progress, metrics, stats, alert, timer" + "contentState.type must be one of: segmented_progress, progress, metrics, stats, alert, timer, value" ); } @@ -416,7 +416,7 @@ const validateContentState = (contentState, mode) => { } } - if (hasValue !== hasUpperLimit) { + if (normalizedType !== "value" && hasValue !== hasUpperLimit) { throw new Error( "contentState.value and contentState.upperLimit must be provided together" ); @@ -450,7 +450,7 @@ const validateContentState = (contentState, mode) => { } const hasSegmentedFields = hasNumberOfSteps || hasCurrentStep || hasStepColor; - const hasProgressFields = hasPercentage || hasValue || hasUpperLimit; + const hasProgressFields = hasPercentage || (hasValue && normalizedType !== "value") || hasUpperLimit; const hasAlertFields = hasMessage || hasIcon || hasBadge; if (hasIcon) { @@ -549,6 +549,16 @@ const validateContentState = (contentState, mode) => { }); } + if (normalizedType === "value") { + if (!hasValue || !(typeof contentState.value === "string" || Number.isFinite(contentState.value))) { + throw new Error("Value requires contentState.value as a string or finite number"); + } + if (hasProgressFields || hasSegmentedFields || hasMetrics || hasTimerFields || hasMessage) { + throw new Error("Do not mix Value with other Live Activity type fields"); + } + return; + } + const effectiveType = normalizedType; if (mode === "start" || mode === "stream") { @@ -850,7 +860,9 @@ const buildContentStateFromOptions = (options) => { } if (options.value !== undefined) { - contentState.value = options.value; + contentState.value = options.type === "value" + ? options.value + : parseNumberOption("value")(options.value); } if (options.upperLimit !== undefined) { diff --git a/test/cli.test.js b/test/cli.test.js index 90465bc..1f01ced 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -308,3 +308,25 @@ test("end-stream rejects conflicting Tags flags", async () => { const result = await runCli(["activity","end-stream","job","--tags","finished","--clear-tags"]); assert.notEqual(result.code,0); assert.equal(result.request,null); }); + +for (const value of ['$1,240', '0007', '', 0, -12.75]) { + test(`Value preserves ${JSON.stringify(value)} through JSON`, async () => { + const result = await runCli(['activity', 'stream', 'revenue', '--content-state', JSON.stringify({title:'Revenue', type:'value', value})]); + assert.equal(result.code, 0, result.stderr); + assert.equal(result.request.body.content_state.value, value); + }); +} +for (const value of ['$1,240', '0007', '0']) { + test(`Value flag preserves ${value}`, async () => { + const result = await runCli(['activity', 'stream', 'revenue', '--title', 'Revenue', '--type', 'value', '--value', value]); + assert.equal(result.code, 0, result.stderr); + assert.equal(result.request.body.content_state.value, value); + }); +} +for (const value of [undefined, null, true, {}, []]) { + test(`Value rejects ${JSON.stringify(value)}`, async () => { + const result = await runCli(['activity', 'stream', 'revenue', '--content-state', JSON.stringify({title:'Revenue', type:'value', value})]); + assert.notEqual(result.code, 0); + assert.equal(result.request, null); + }); +}