From 6a7a4a4b414ec096625b82deb1d80255410b9b8e Mon Sep 17 00:00:00 2001 From: activitysmith-bot Date: Sun, 22 Mar 2026 12:56:52 +0000 Subject: [PATCH 1/2] chore: regenerate SDK --- generated/apis/LiveActivitiesApi.ts | 127 ++++++- generated/models/index.ts | 515 +++++++++++++++++++++++++++- 2 files changed, 630 insertions(+), 12 deletions(-) diff --git a/generated/apis/LiveActivitiesApi.ts b/generated/apis/LiveActivitiesApi.ts index 9faeebc..dc71bad 100644 --- a/generated/apis/LiveActivitiesApi.ts +++ b/generated/apis/LiveActivitiesApi.ts @@ -21,16 +21,32 @@ import type { LiveActivityEndResponse, LiveActivityStartRequest, LiveActivityStartResponse, + LiveActivityStreamDeleteRequest, + LiveActivityStreamDeleteResponse, + LiveActivityStreamPutResponse, + LiveActivityStreamRequest, LiveActivityUpdateRequest, LiveActivityUpdateResponse, NoRecipientsError, + NotFoundError, RateLimitError, + SendPushNotification429Response, } from '../models/index'; export interface EndLiveActivityRequest { liveActivityEndRequest: LiveActivityEndRequest; } +export interface EndLiveActivityStreamRequest { + streamKey: string; + liveActivityStreamDeleteRequest?: LiveActivityStreamDeleteRequest; +} + +export interface ReconcileLiveActivityStreamRequest { + streamKey: string; + liveActivityStreamRequest: LiveActivityStreamRequest; +} + export interface StartLiveActivityRequest { liveActivityStartRequest: LiveActivityStartRequest; } @@ -45,7 +61,7 @@ export interface UpdateLiveActivityRequest { export class LiveActivitiesApi extends runtime.BaseAPI { /** - * Ends a Live Activity and archives its lifecycle. For segmented_progress activities, you can send the latest number_of_steps here if the workflow changed after start. + * Ends a Live Activity and archives its lifecycle. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can send the latest number_of_steps here if the workflow changed after start. * End a Live Activity */ async endLiveActivityRaw(requestParameters: EndLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -82,7 +98,7 @@ export class LiveActivitiesApi extends runtime.BaseAPI { } /** - * Ends a Live Activity and archives its lifecycle. For segmented_progress activities, you can send the latest number_of_steps here if the workflow changed after start. + * Ends a Live Activity and archives its lifecycle. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can send the latest number_of_steps here if the workflow changed after start. * End a Live Activity */ async endLiveActivity(requestParameters: EndLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { @@ -91,7 +107,106 @@ export class LiveActivitiesApi extends runtime.BaseAPI { } /** - * Starts a Live Activity on devices matched by API key scope and optional target channels. For segmented_progress activities, number_of_steps can be changed later during update or end calls if the workflow changes. + * Use this endpoint when the process you are tracking is finished and you no longer want the Live Activity on your devices. ActivitySmith ends the current Live Activity for this stream and dismisses it from devices. If you need direct lifecycle control, use /live-activity/start, /live-activity/update, and /live-activity/end instead. + * End a stream + */ + async endLiveActivityStreamRaw(requestParameters: EndLiveActivityStreamRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['streamKey'] == null) { + throw new runtime.RequiredError( + 'streamKey', + 'Required parameter "streamKey" was null or undefined when calling endLiveActivityStream().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("apiKeyAuth", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/live-activity/stream/{stream_key}`.replace(`{${"stream_key"}}`, encodeURIComponent(String(requestParameters['streamKey']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + body: requestParameters['liveActivityStreamDeleteRequest'], + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * Use this endpoint when the process you are tracking is finished and you no longer want the Live Activity on your devices. ActivitySmith ends the current Live Activity for this stream and dismisses it from devices. If you need direct lifecycle control, use /live-activity/start, /live-activity/update, and /live-activity/end instead. + * End a stream + */ + async endLiveActivityStream(requestParameters: EndLiveActivityStreamRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.endLiveActivityStreamRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Use this endpoint when you want the easiest, stateless way to trigger Live Activities. You do not need to store activity_id or manage the Live Activity lifecycle yourself. Send the latest state for a stable stream_key and ActivitySmith will handle the rest for you: if there is no Live Activity yet, it starts one; if there is already one for this stream, it updates it. If you need direct lifecycle control, use /live-activity/start, /live-activity/update, and /live-activity/end instead. + * Send a stream update + */ + async reconcileLiveActivityStreamRaw(requestParameters: ReconcileLiveActivityStreamRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['streamKey'] == null) { + throw new runtime.RequiredError( + 'streamKey', + 'Required parameter "streamKey" was null or undefined when calling reconcileLiveActivityStream().' + ); + } + + if (requestParameters['liveActivityStreamRequest'] == null) { + throw new runtime.RequiredError( + 'liveActivityStreamRequest', + 'Required parameter "liveActivityStreamRequest" was null or undefined when calling reconcileLiveActivityStream().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("apiKeyAuth", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/live-activity/stream/{stream_key}`.replace(`{${"stream_key"}}`, encodeURIComponent(String(requestParameters['streamKey']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: requestParameters['liveActivityStreamRequest'], + }, initOverrides); + + return new runtime.JSONApiResponse(response); + } + + /** + * Use this endpoint when you want the easiest, stateless way to trigger Live Activities. You do not need to store activity_id or manage the Live Activity lifecycle yourself. Send the latest state for a stable stream_key and ActivitySmith will handle the rest for you: if there is no Live Activity yet, it starts one; if there is already one for this stream, it updates it. If you need direct lifecycle control, use /live-activity/start, /live-activity/update, and /live-activity/end instead. + * Send a stream update + */ + async reconcileLiveActivityStream(requestParameters: ReconcileLiveActivityStreamRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.reconcileLiveActivityStreamRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Starts a Live Activity on devices matched by API key scope and optional target channels. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, number_of_steps can be changed later during update or end calls if the workflow changes. * Start a Live Activity */ async startLiveActivityRaw(requestParameters: StartLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -128,7 +243,7 @@ export class LiveActivitiesApi extends runtime.BaseAPI { } /** - * Starts a Live Activity on devices matched by API key scope and optional target channels. For segmented_progress activities, number_of_steps can be changed later during update or end calls if the workflow changes. + * Starts a Live Activity on devices matched by API key scope and optional target channels. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, number_of_steps can be changed later during update or end calls if the workflow changes. * Start a Live Activity */ async startLiveActivity(requestParameters: StartLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { @@ -137,7 +252,7 @@ export class LiveActivitiesApi extends runtime.BaseAPI { } /** - * Updates an existing Live Activity. If the per-activity token is not registered yet, the update is queued. For segmented_progress activities, you can increase or decrease number_of_steps here as the workflow changes. + * Updates an existing Live Activity. If the per-activity token is not registered yet, the update is queued. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can increase or decrease number_of_steps here as the workflow changes. * Update a Live Activity */ async updateLiveActivityRaw(requestParameters: UpdateLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -174,7 +289,7 @@ export class LiveActivitiesApi extends runtime.BaseAPI { } /** - * Updates an existing Live Activity. If the per-activity token is not registered yet, the update is queued. For segmented_progress activities, you can increase or decrease number_of_steps here as the workflow changes. + * Updates an existing Live Activity. If the per-activity token is not registered yet, the update is queued. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can increase or decrease number_of_steps here as the workflow changes. * Update a Live Activity */ async updateLiveActivity(requestParameters: UpdateLiveActivityRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { diff --git a/generated/models/index.ts b/generated/models/index.ts index 0c2dc07..5413ad9 100644 --- a/generated/models/index.ts +++ b/generated/models/index.ts @@ -1,5 +1,31 @@ /* tslint:disable */ /* eslint-disable */ +/** + * + * @export + * @interface ActivityMetric + */ +export interface ActivityMetric { + [key: string]: any | any; + /** + * + * @type {string} + * @memberof ActivityMetric + */ + label: string; + /** + * + * @type {number} + * @memberof ActivityMetric + */ + value: number; + /** + * + * @type {string} + * @memberof ActivityMetric + */ + unit?: string; +} /** * * @export @@ -55,7 +81,7 @@ export interface ChannelTarget { channels: Array; } /** - * End payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. Type is optional when ending an existing activity. You can send an updated number_of_steps here if the workflow changed after start. + * End payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics include a non-empty metrics array. Legacy counter/timer/countdown types also use current_step and number_of_steps. Type is optional when ending an existing activity. You can send an updated number_of_steps here if the workflow changed after start. * @export * @interface ContentStateEnd */ @@ -103,6 +129,12 @@ export interface ContentStateEnd { * @memberof ContentStateEnd */ upper_limit?: number; + /** + * Use for type=metrics. + * @type {Array} + * @memberof ContentStateEnd + */ + metrics?: Array; /** * Optional. When omitted, the API uses the existing Live Activity type. * @type {string} @@ -121,6 +153,12 @@ export interface ContentStateEnd { * @memberof ContentStateEnd */ step_color?: ContentStateEndStepColorEnum; + /** + * Optional. Colors for completed steps. When used with segmented_progress, the array length should match current_step. + * @type {Array} + * @memberof ContentStateEnd + */ + step_colors?: Array; /** * Optional. Minutes before the ended Live Activity is dismissed. Default 3. Set 0 for immediate dismissal. iOS will dismiss ended Live Activities after ~4 hours max. * @type {number} @@ -135,7 +173,11 @@ export interface ContentStateEnd { */ export const ContentStateEndTypeEnum = { SegmentedProgress: 'segmented_progress', - Progress: 'progress' + Progress: 'progress', + Metrics: 'metrics', + Counter: 'counter', + Timer: 'timer', + Countdown: 'countdown' } as const; export type ContentStateEndTypeEnum = typeof ContentStateEndTypeEnum[keyof typeof ContentStateEndTypeEnum]; @@ -172,7 +214,23 @@ export const ContentStateEndStepColorEnum = { export type ContentStateEndStepColorEnum = typeof ContentStateEndStepColorEnum[keyof typeof ContentStateEndStepColorEnum]; /** - * Start payload requires title and type. For segmented_progress include number_of_steps and current_step. For progress include percentage or value with upper_limit. For segmented_progress, number_of_steps is not locked and can be changed in later update or end calls. + * @export + */ +export const ContentStateEndStepColorsEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type ContentStateEndStepColorsEnum = typeof ContentStateEndStepColorsEnum[keyof typeof ContentStateEndStepColorsEnum]; + +/** + * Start payload requires title and type. For segmented_progress include number_of_steps and current_step. For progress include percentage or value with upper_limit. For metrics include a non-empty metrics array. Legacy counter/timer/countdown types also use current_step and number_of_steps. For segmented_progress, number_of_steps is not locked and can be changed in later update or end calls. * @export * @interface ContentStateStart */ @@ -220,6 +278,12 @@ export interface ContentStateStart { * @memberof ContentStateStart */ upper_limit?: number; + /** + * Use for type=metrics. + * @type {Array} + * @memberof ContentStateStart + */ + metrics?: Array; /** * * @type {string} @@ -238,6 +302,12 @@ export interface ContentStateStart { * @memberof ContentStateStart */ step_color?: ContentStateStartStepColorEnum; + /** + * Optional. Colors for completed steps. When used with segmented_progress, the array length should match current_step. + * @type {Array} + * @memberof ContentStateStart + */ + step_colors?: Array; } @@ -246,7 +316,11 @@ export interface ContentStateStart { */ export const ContentStateStartTypeEnum = { SegmentedProgress: 'segmented_progress', - Progress: 'progress' + Progress: 'progress', + Metrics: 'metrics', + Counter: 'counter', + Timer: 'timer', + Countdown: 'countdown' } as const; export type ContentStateStartTypeEnum = typeof ContentStateStartTypeEnum[keyof typeof ContentStateStartTypeEnum]; @@ -283,7 +357,23 @@ export const ContentStateStartStepColorEnum = { export type ContentStateStartStepColorEnum = typeof ContentStateStartStepColorEnum[keyof typeof ContentStateStartStepColorEnum]; /** - * Update payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. Type is optional when updating an existing activity. You can increase or decrease number_of_steps during updates. + * @export + */ +export const ContentStateStartStepColorsEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type ContentStateStartStepColorsEnum = typeof ContentStateStartStepColorsEnum[keyof typeof ContentStateStartStepColorsEnum]; + +/** + * Update payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics include a non-empty metrics array. Legacy counter/timer/countdown types also use current_step and number_of_steps. Type is optional when updating an existing activity. You can increase or decrease number_of_steps during updates. * @export * @interface ContentStateUpdate */ @@ -331,6 +421,12 @@ export interface ContentStateUpdate { * @memberof ContentStateUpdate */ upper_limit?: number; + /** + * Use for type=metrics. + * @type {Array} + * @memberof ContentStateUpdate + */ + metrics?: Array; /** * Optional. When omitted, the API uses the existing Live Activity type. * @type {string} @@ -349,6 +445,12 @@ export interface ContentStateUpdate { * @memberof ContentStateUpdate */ step_color?: ContentStateUpdateStepColorEnum; + /** + * Optional. Colors for completed steps. When used with segmented_progress, the array length should match current_step. + * @type {Array} + * @memberof ContentStateUpdate + */ + step_colors?: Array; } @@ -357,7 +459,11 @@ export interface ContentStateUpdate { */ export const ContentStateUpdateTypeEnum = { SegmentedProgress: 'segmented_progress', - Progress: 'progress' + Progress: 'progress', + Metrics: 'metrics', + Counter: 'counter', + Timer: 'timer', + Countdown: 'countdown' } as const; export type ContentStateUpdateTypeEnum = typeof ContentStateUpdateTypeEnum[keyof typeof ContentStateUpdateTypeEnum]; @@ -393,6 +499,22 @@ export const ContentStateUpdateStepColorEnum = { } as const; export type ContentStateUpdateStepColorEnum = typeof ContentStateUpdateStepColorEnum[keyof typeof ContentStateUpdateStepColorEnum]; +/** + * @export + */ +export const ContentStateUpdateStepColorsEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type ContentStateUpdateStepColorsEnum = typeof ContentStateUpdateStepColorsEnum[keyof typeof ContentStateUpdateStepColorsEnum]; + /** * * @export @@ -634,6 +756,212 @@ export interface LiveActivityStartResponse { */ timestamp: string; } +/** + * Optional payload for ending a managed stream. When omitted, ActivitySmith ends the stream using the latest known state when possible. + * @export + * @interface LiveActivityStreamDeleteRequest + */ +export interface LiveActivityStreamDeleteRequest { + [key: string]: any | any; + /** + * + * @type {StreamContentState} + * @memberof LiveActivityStreamDeleteRequest + */ + content_state?: StreamContentState; + /** + * + * @type {LiveActivityAction} + * @memberof LiveActivityStreamDeleteRequest + */ + action?: LiveActivityAction; + /** + * + * @type {AlertPayload} + * @memberof LiveActivityStreamDeleteRequest + */ + alert?: AlertPayload; +} +/** + * Returned after a managed stream is ended and removed. + * @export + * @interface LiveActivityStreamDeleteResponse + */ +export interface LiveActivityStreamDeleteResponse { + [key: string]: any | any; + /** + * + * @type {boolean} + * @memberof LiveActivityStreamDeleteResponse + */ + success: boolean; + /** + * + * @type {string} + * @memberof LiveActivityStreamDeleteResponse + */ + operation: LiveActivityStreamDeleteResponseOperationEnum; + /** + * + * @type {string} + * @memberof LiveActivityStreamDeleteResponse + */ + stream_key: string; + /** + * + * @type {string} + * @memberof LiveActivityStreamDeleteResponse + */ + activity_id?: string | null; + /** + * + * @type {number} + * @memberof LiveActivityStreamDeleteResponse + */ + devices_queued?: number; + /** + * + * @type {number} + * @memberof LiveActivityStreamDeleteResponse + */ + devices_notified?: number; + /** + * + * @type {string} + * @memberof LiveActivityStreamDeleteResponse + */ + timestamp: string; +} + + +/** + * @export + */ +export const LiveActivityStreamDeleteResponseOperationEnum = { + Ended: 'ended' +} as const; +export type LiveActivityStreamDeleteResponseOperationEnum = typeof LiveActivityStreamDeleteResponseOperationEnum[keyof typeof LiveActivityStreamDeleteResponseOperationEnum]; + +/** + * Returned after a managed stream request is reconciled. + * @export + * @interface LiveActivityStreamPutResponse + */ +export interface LiveActivityStreamPutResponse { + [key: string]: any | any; + /** + * + * @type {boolean} + * @memberof LiveActivityStreamPutResponse + */ + success: boolean; + /** + * + * @type {string} + * @memberof LiveActivityStreamPutResponse + */ + operation: LiveActivityStreamPutResponseOperationEnum; + /** + * + * @type {string} + * @memberof LiveActivityStreamPutResponse + */ + stream_key: string; + /** + * + * @type {string} + * @memberof LiveActivityStreamPutResponse + */ + activity_id?: string | null; + /** + * + * @type {string} + * @memberof LiveActivityStreamPutResponse + */ + previous_activity_id?: string; + /** + * + * @type {number} + * @memberof LiveActivityStreamPutResponse + */ + devices_notified?: number; + /** + * + * @type {number} + * @memberof LiveActivityStreamPutResponse + */ + devices_queued?: number; + /** + * + * @type {number} + * @memberof LiveActivityStreamPutResponse + */ + users_notified?: number; + /** + * + * @type {Array} + * @memberof LiveActivityStreamPutResponse + */ + effective_channel_slugs?: Array; + /** + * + * @type {string} + * @memberof LiveActivityStreamPutResponse + */ + timestamp: string; +} + + +/** + * @export + */ +export const LiveActivityStreamPutResponseOperationEnum = { + Started: 'started', + Updated: 'updated', + Rotated: 'rotated', + Noop: 'noop', + Paused: 'paused' +} as const; +export type LiveActivityStreamPutResponseOperationEnum = typeof LiveActivityStreamPutResponseOperationEnum[keyof typeof LiveActivityStreamPutResponseOperationEnum]; + +/** + * Send the latest state for a managed Live Activity stream. channels is the streamlined form for stream targeting. target.channels is also accepted for compatibility. If both are provided, they must match. + * @export + * @interface LiveActivityStreamRequest + */ +export interface LiveActivityStreamRequest { + [key: string]: any | any; + /** + * + * @type {StreamContentState} + * @memberof LiveActivityStreamRequest + */ + content_state: StreamContentState; + /** + * + * @type {LiveActivityAction} + * @memberof LiveActivityStreamRequest + */ + action?: LiveActivityAction; + /** + * + * @type {AlertPayload} + * @memberof LiveActivityStreamRequest + */ + alert?: AlertPayload; + /** + * Channel slugs. When omitted, API key scope determines recipients. + * @type {Array} + * @memberof LiveActivityStreamRequest + */ + channels?: Array; + /** + * + * @type {ChannelTarget} + * @memberof LiveActivityStreamRequest + */ + target?: ChannelTarget; +} /** * Update an existing Live Activity by activity_id. * @export @@ -735,6 +1063,26 @@ export interface NoRecipientsError { */ effective_channel_slugs?: Array; } +/** + * + * @export + * @interface NotFoundError + */ +export interface NotFoundError { + [key: string]: any | any; + /** + * + * @type {string} + * @memberof NotFoundError + */ + error: string; + /** + * + * @type {string} + * @memberof NotFoundError + */ + message: string; +} /** * * @export @@ -927,3 +1275,158 @@ export interface RateLimitError { * @export */ export type SendPushNotification429Response = LiveActivityLimitError | RateLimitError; +/** + * Current state for a managed Live Activity stream. Include type on the first PUT, and whenever the stream may need to start a fresh activity. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based types. + * @export + * @interface StreamContentState + */ +export interface StreamContentState { + [key: string]: any | any; + /** + * + * @type {string} + * @memberof StreamContentState + */ + title: string; + /** + * + * @type {string} + * @memberof StreamContentState + */ + subtitle?: string; + /** + * Use for segmented_progress, counter, timer, and countdown. + * @type {number} + * @memberof StreamContentState + */ + number_of_steps?: number; + /** + * Use for segmented_progress, counter, timer, and countdown. + * @type {number} + * @memberof StreamContentState + */ + current_step?: number; + /** + * Use for progress. Takes precedence over value/upper_limit if both are provided. + * @type {number} + * @memberof StreamContentState + */ + percentage?: number; + /** + * Current progress value. Use with upper_limit for progress. + * @type {number} + * @memberof StreamContentState + */ + value?: number; + /** + * Maximum progress value. Use with value for progress. + * @type {number} + * @memberof StreamContentState + */ + upper_limit?: number; + /** + * Required on the first PUT or whenever the stream cannot infer the current activity type. + * @type {string} + * @memberof StreamContentState + */ + type?: StreamContentStateTypeEnum; + /** + * Optional. Accent color for the Live Activity. Defaults to blue. + * @type {string} + * @memberof StreamContentState + */ + color?: StreamContentStateColorEnum; + /** + * Optional. Overrides color for the current step. Only applies to segmented_progress. + * @type {string} + * @memberof StreamContentState + */ + step_color?: StreamContentStateStepColorEnum; + /** + * Optional. Colors for completed steps. When used with segmented_progress, the array length should match current_step. + * @type {Array} + * @memberof StreamContentState + */ + step_colors?: Array; + /** + * Use for metrics activities. + * @type {Array} + * @memberof StreamContentState + */ + metrics?: Array; + /** + * Optional. Seconds before the ended Live Activity is dismissed. + * @type {number} + * @memberof StreamContentState + */ + auto_dismiss_seconds?: number; + /** + * Optional. Minutes before the ended Live Activity is dismissed. + * @type {number} + * @memberof StreamContentState + */ + auto_dismiss_minutes?: number; +} + + +/** + * @export + */ +export const StreamContentStateTypeEnum = { + SegmentedProgress: 'segmented_progress', + Progress: 'progress', + Metrics: 'metrics', + Counter: 'counter', + Timer: 'timer', + Countdown: 'countdown' +} as const; +export type StreamContentStateTypeEnum = typeof StreamContentStateTypeEnum[keyof typeof StreamContentStateTypeEnum]; + +/** + * @export + */ +export const StreamContentStateColorEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type StreamContentStateColorEnum = typeof StreamContentStateColorEnum[keyof typeof StreamContentStateColorEnum]; + +/** + * @export + */ +export const StreamContentStateStepColorEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type StreamContentStateStepColorEnum = typeof StreamContentStateStepColorEnum[keyof typeof StreamContentStateStepColorEnum]; + +/** + * @export + */ +export const StreamContentStateStepColorsEnum = { + Lime: 'lime', + Green: 'green', + Cyan: 'cyan', + Blue: 'blue', + Purple: 'purple', + Magenta: 'magenta', + Red: 'red', + Orange: 'orange', + Yellow: 'yellow' +} as const; +export type StreamContentStateStepColorsEnum = typeof StreamContentStateStepColorsEnum[keyof typeof StreamContentStateStepColorsEnum]; + From 379945e49a8e591b12a83fa53b2112b87f78e050 Mon Sep 17 00:00:00 2001 From: bardonadam Date: Sun, 22 Mar 2026 15:40:48 +0100 Subject: [PATCH 2/2] feat: add Node live activity stream wrapper and docs Add the handwritten live activity stream wrapper methods, Node README stream guidance, metrics examples, screenshots, and the 1.1.0 version bump. --- README.md | 236 ++++++++++++++++++++++++++++++++++++---- package-lock.json | 4 +- package.json | 2 +- src/ActivitySmith.ts | 48 ++++++++ tests/resources.test.js | 72 ++++++++++++ tests/smoke.test.js | 11 +- 6 files changed, 346 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 94326e6..e70805f 100644 --- a/README.md +++ b/README.md @@ -111,27 +111,212 @@ await activitysmith.notifications.send({ ## Live Activities -Live Activities come in two UI types, but the lifecycle stays the same: -start the activity, keep the returned `activity_id`, update it as state -changes, then end it when the work is done. +

+ Live Activities example +

+ +ActivitySmith supports two ways to drive Live Activities: + +- Recommended: stream updates with `activitysmith.liveActivities.stream(...)` +- Advanced: manual lifecycle control with `start`, `update`, and `end` + +Use stream updates when you want the easiest, stateless flow. You don't need to +store `activity_id` or manage lifecycle state yourself. Send the latest state +for a stable `streamKey` and ActivitySmith will start or update the Live +Activity for you. When the tracked process is over, call `endStream(...)`. + +Use the manual lifecycle methods when you need direct control over a specific +Live Activity instance. + +Live Activity UI types: + +- `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag +- `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines +- `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage + +### Recommended: Stream updates + +Use a stable `streamKey` to identify the system or workflow you are tracking, +such as a server, deployment, build pipeline, cron job, or charging session. +This is especially useful for cron jobs and other scheduled tasks where you do +not want to store `activity_id` between runs. + +#### Metrics + +

+ Metrics stream example +

+ +```ts +const status = await activitysmith.liveActivities.stream("prod-web-1", { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 9, unit: "%" }, + { label: "MEM", value: 45, unit: "%" }, + ], + }, +}); +``` + +#### Segmented progress + +

+ Segmented progress stream example +

+ +```ts +await activitysmith.liveActivities.stream("nightly-backup", { + content_state: { + title: "Nightly Backup", + subtitle: "upload archive", + type: "segmented_progress", + number_of_steps: 3, + current_step: 2, + }, +}); +``` + +#### Progress + +

+ Progress stream example +

+ +```ts +await activitysmith.liveActivities.stream("search-reindex", { + content_state: { + title: "Search Reindex", + subtitle: "catalog-v2", + type: "progress", + percentage: 42, + }, +}); +``` + +Call `stream(...)` again with the same `streamKey` whenever the state changes. + +#### End a stream + +Use this when the tracked process is finished and you no longer want the Live +Activity on devices. `content_state` is optional here; include it if you want +to end the stream with a final state. + +```ts +await activitysmith.liveActivities.endStream("prod-web-1", { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 7, unit: "%" }, + { label: "MEM", value: 38, unit: "%" }, + ], + }, +}); +``` + +If you later send another `stream(...)` request with the same `streamKey`, +ActivitySmith starts a new Live Activity for that stream again. + +Stream responses include an `operation` field: + +- `started`: ActivitySmith started a new Live Activity for this `streamKey` +- `updated`: ActivitySmith updated the current Live Activity +- `rotated`: ActivitySmith ended the previous Live Activity and started a new one +- `noop`: the incoming state matched the current state, so no update was sent +- `paused`: the stream is paused, so no Live Activity was started or updated +- `ended`: returned by `endStream(...)` after the stream is ended + +### Advanced: Manual lifecycle control -- `segmented_progress`: best for jobs tracked in steps -- `progress`: best for jobs tracked as a percentage or numeric range +Use these methods when you want to manage the Live Activity lifecycle yourself. -### Shared flow +#### Shared flow 1. Call `activitysmith.liveActivities.start(...)`. 2. Save the returned `activity_id`. 3. Call `activitysmith.liveActivities.update(...)` as progress changes. 4. Call `activitysmith.liveActivities.end(...)` when the work is finished. +### Metrics Type + +Use `metrics` when you want to keep a small set of live stats visible, such as +server health, queue pressure, or database load. + +#### Start + +

+ Metrics start example +

+ +```ts +const start = await activitysmith.liveActivities.start({ + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 9, unit: "%" }, + { label: "MEM", value: 45, unit: "%" }, + ], + }, +}); + +const activityId = start.activity_id; +``` + +#### Update + +

+ Metrics update example +

+ +```ts +await activitysmith.liveActivities.update({ + activity_id: activityId, + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 76, unit: "%" }, + { label: "MEM", value: 52, unit: "%" }, + ], + }, +}); +``` + +#### End + +

+ Metrics end example +

+ +```ts +await activitysmith.liveActivities.end({ + activity_id: activityId, + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 7, unit: "%" }, + { label: "MEM", value: 38, unit: "%" }, + ], + auto_dismiss_minutes: 2, + }, +}); +``` + ### Segmented Progress Type Use `segmented_progress` when progress is easier to follow as steps instead of a raw percentage. It fits jobs like backups, deployments, ETL pipelines, and -checklists where "step 2 of 3" is more useful than "67%". -`number_of_steps` is dynamic, so you can increase or decrease it later if the -workflow changes. +checklists where "step 2 of 3" is more useful than "67%". `number_of_steps` is +dynamic, so you can increase or decrease it later if the workflow changes. #### Start @@ -149,7 +334,6 @@ const start = await activitysmith.liveActivities.start({ type: "segmented_progress", color: "yellow", }, - channels: ["devs", "ops"], // Optional }); const activityId = start.activity_id; @@ -167,7 +351,7 @@ await activitysmith.liveActivities.update({ content_state: { title: "Nightly database backup", subtitle: "upload archive", - number_of_steps: 4, + number_of_steps: 3, current_step: 2, }, }); @@ -185,8 +369,8 @@ await activitysmith.liveActivities.end({ content_state: { title: "Nightly database backup", subtitle: "verify restore", - number_of_steps: 4, - current_step: 4, + number_of_steps: 3, + current_step: 3, auto_dismiss_minutes: 2, }, }); @@ -257,25 +441,27 @@ await activitysmith.liveActivities.end({ Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend. +#### Open URL action +

- Live Activity with action + Metrics Live Activity with action

-#### Open URL action - ```ts const start = await activitysmith.liveActivities.start({ content_state: { - title: "Deploying payments-api", - subtitle: "Running database migrations", - number_of_steps: 5, - current_step: 3, - type: "segmented_progress", + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 76, unit: "%" }, + { label: "MEM", value: 52, unit: "%" }, + ], }, action: { - title: "Open Workflow", + title: "Open Dashboard", type: "open_url", - url: "https://github.com/acme/payments-api/actions/runs/1234567890", + url: "https://ops.example.com/servers/prod-web-1", }, }); @@ -284,6 +470,10 @@ const activityId = start.activity_id; #### Webhook action +

+ Live Activity with action +

+ ```ts await activitysmith.liveActivities.update({ activity_id: activityId, diff --git a/package-lock.json b/package-lock.json index 1f95263..e1e7b50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "activitysmith", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "activitysmith", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "devDependencies": { "typescript": "^5.3.3", diff --git a/package.json b/package.json index 24fee17..1e38ac6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "activitysmith", - "version": "1.0.0", + "version": "1.1.0", "description": "Official ActivitySmith Node.js SDK", "keywords": [ "activitysmith", diff --git a/src/ActivitySmith.ts b/src/ActivitySmith.ts index 0c431b0..d3bb5d9 100644 --- a/src/ActivitySmith.ts +++ b/src/ActivitySmith.ts @@ -11,10 +11,15 @@ type SendInitOverrides = Parameters[0]["liveActivityStartRequest"]; type UpdateRequestBody = Parameters[0]["liveActivityUpdateRequest"]; type EndRequestBody = Parameters[0]["liveActivityEndRequest"]; +type StreamRequestBody = + Parameters[0]["liveActivityStreamRequest"]; +type StreamDeleteRequestBody = + Parameters[0]["liveActivityStreamDeleteRequest"]; type LiveInitOverrides = Parameters[1]; type ChannelTargetInput = { channels?: string[] }; type PushSendRequest = PushRequestBody & { channels?: string[] }; type LiveStartSendRequest = StartRequestBody & { channels?: string[] }; +type LiveStreamSendRequest = StreamRequestBody & { channels?: string[] }; function withTargetChannels( request: T & { channels?: string[] }, @@ -108,6 +113,31 @@ export class LiveActivitiesResource { return this.api.endLiveActivity({ liveActivityEndRequest: request }, initOverrides); } + stream(streamKey: string, request: LiveStreamSendRequest, initOverrides?: LiveInitOverrides) { + return this.api.reconcileLiveActivityStream( + { + streamKey, + liveActivityStreamRequest: withTargetChannels(request), + }, + initOverrides, + ); + } + + endStream( + streamKey: string, + request?: StreamDeleteRequestBody, + initOverrides?: LiveInitOverrides, + ) { + if (request) { + return this.api.endLiveActivityStream( + { streamKey, liveActivityStreamDeleteRequest: request }, + initOverrides, + ); + } + + return this.api.endLiveActivityStream({ streamKey }, initOverrides); + } + // Backward-compatible aliases. startLiveActivity(...args: Parameters) { return this.api.startLiveActivity(...args); @@ -121,6 +151,14 @@ export class LiveActivitiesResource { return this.api.endLiveActivity(...args); } + reconcileLiveActivityStream(...args: Parameters) { + return this.api.reconcileLiveActivityStream(...args); + } + + endLiveActivityStream(...args: Parameters) { + return this.api.endLiveActivityStream(...args); + } + startLiveActivityRaw(...args: Parameters) { return this.api.startLiveActivityRaw(...args); } @@ -132,6 +170,16 @@ export class LiveActivitiesResource { endLiveActivityRaw(...args: Parameters) { return this.api.endLiveActivityRaw(...args); } + + reconcileLiveActivityStreamRaw( + ...args: Parameters + ) { + return this.api.reconcileLiveActivityStreamRaw(...args); + } + + endLiveActivityStreamRaw(...args: Parameters) { + return this.api.endLiveActivityStreamRaw(...args); + } } export class ActivitySmith { diff --git a/tests/resources.test.js b/tests/resources.test.js index 7b1a7e1..d6fa38d 100644 --- a/tests/resources.test.js +++ b/tests/resources.test.js @@ -228,6 +228,78 @@ describe("resource wrappers", () => { expect(startSpy).toHaveBeenCalledWith({ liveActivityStartRequest: payload }, undefined); }); + it("wraps live activity stream payloads for short methods", async () => { + const ActivitySmith = require("../dist/src/index.js"); + const generated = require("../dist/generated/index.js"); + + const streamSpy = vi + .spyOn(generated.LiveActivitiesApi.prototype, "reconcileLiveActivityStream") + .mockResolvedValue({ operation: "started", stream_key: "prod-web-1" }); + const endStreamSpy = vi + .spyOn(generated.LiveActivitiesApi.prototype, "endLiveActivityStream") + .mockResolvedValue({ operation: "ended", stream_key: "prod-web-1" }); + + const client = new ActivitySmith({ apiKey: "test" }); + await client.liveActivities.stream("prod-web-1", { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 9, unit: "%" }, + { label: "MEM", value: 45, unit: "%" }, + ], + }, + channels: ["ops"], + }); + await client.liveActivities.endStream("prod-web-1"); + + expect(streamSpy).toHaveBeenCalledWith( + { + streamKey: "prod-web-1", + liveActivityStreamRequest: { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [ + { label: "CPU", value: 9, unit: "%" }, + { label: "MEM", value: 45, unit: "%" }, + ], + }, + target: { channels: ["ops"] }, + }, + }, + undefined, + ); + expect(endStreamSpy).toHaveBeenCalledWith({ streamKey: "prod-web-1" }, undefined); + }); + + it("keeps long stream aliases working", async () => { + const ActivitySmith = require("../dist/src/index.js"); + const generated = require("../dist/generated/index.js"); + + const streamSpy = vi + .spyOn(generated.LiveActivitiesApi.prototype, "reconcileLiveActivityStream") + .mockResolvedValue({ operation: "started", stream_key: "prod-web-1" }); + + const client = new ActivitySmith({ apiKey: "test" }); + const request = { + streamKey: "prod-web-1", + liveActivityStreamRequest: { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [{ label: "CPU", value: 9, unit: "%" }], + }, + }, + }; + + await client.liveActivities.reconcileLiveActivityStream(request); + expect(streamSpy).toHaveBeenCalledWith(request); + }); + it("passes through live activity actions for short methods", async () => { const ActivitySmith = require("../dist/src/index.js"); const generated = require("../dist/generated/index.js"); diff --git a/tests/smoke.test.js b/tests/smoke.test.js index e515448..3975e8e 100644 --- a/tests/smoke.test.js +++ b/tests/smoke.test.js @@ -49,9 +49,18 @@ describe("smoke", () => { type: "segmented_progress", }, }); + await client.liveActivities.stream("prod-web-1", { + content_state: { + title: "Server Health", + subtitle: "prod-web-1", + type: "metrics", + metrics: [{ label: "CPU", value: 9, unit: "%" }], + }, + }); - expect(fetchSpy).toHaveBeenCalledTimes(2); + expect(fetchSpy).toHaveBeenCalledTimes(3); expect(String(fetchSpy.mock.calls[0][0])).toContain("/push-notification"); expect(String(fetchSpy.mock.calls[1][0])).toContain("/live-activity/start"); + expect(String(fetchSpy.mock.calls[2][0])).toContain("/live-activity/stream/prod-web-1"); }); });