lumia-plugin 0.2.3 → 0.2.5

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/README.md CHANGED
@@ -14,7 +14,7 @@ Run any command with `--help` to see detailed options.
14
14
 
15
15
  ## Template
16
16
 
17
- The CLI ships with a showcase template that demonstrates logging, settings, variables, and alert handling. It mirrors the starter plugin available in this repository so the CLI can scaffold it anywhere.
17
+ The CLI ships with a showcase template that demonstrates settings, actions, variables, and alert handling. It mirrors the starter plugin available in this repository so the CLI can scaffold it anywhere.
18
18
 
19
19
  ## License
20
20
 
@@ -0,0 +1,16 @@
1
+ # Showcase Plugin Template
2
+
3
+ This template demonstrates a minimal, production-friendly Lumia Stream plugin workflow:
4
+
5
+ - Defines a small set of settings with a short setup tutorial
6
+ - Exposes a single action that triggers an alert
7
+ - Updates a few variables that alerts and other Lumia features can use
8
+ - Keeps logging to errors only
9
+
10
+ Use the CLI to copy and customize the template:
11
+
12
+ ```
13
+ npx lumia-plugin create my_plugin
14
+ ```
15
+
16
+ After scaffolding you can tailor the manifest, code, and README to match your idea.
@@ -0,0 +1,90 @@
1
+ const { Plugin } = require("@lumiastream/plugin");
2
+
3
+ const DEFAULTS = {
4
+ message: "Hello from Showcase Plugin!",
5
+ username: "Viewer",
6
+ color: "#00c2ff",
7
+ duration: 5,
8
+ };
9
+
10
+ const VARIABLE_NAMES = {
11
+ message: "message",
12
+ username: "username",
13
+ color: "color",
14
+ duration: "duration",
15
+ };
16
+
17
+ class ShowcasePluginTemplate extends Plugin {
18
+ async onload() {
19
+ await this._syncDefaults();
20
+ }
21
+
22
+ async onsettingsupdate(settings, previous = {}) {
23
+ if (
24
+ settings?.defaultMessage !== previous?.defaultMessage ||
25
+ settings?.defaultColor !== previous?.defaultColor ||
26
+ settings?.defaultDuration !== previous?.defaultDuration
27
+ ) {
28
+ await this._syncDefaults(settings);
29
+ }
30
+ }
31
+
32
+ async actions(config = {}) {
33
+ const actions = Array.isArray(config.actions) ? config.actions : [];
34
+ for (const action of actions) {
35
+ if (action?.type === "trigger_alert") {
36
+ await this._triggerSampleAlert(action.data);
37
+ }
38
+ }
39
+ }
40
+
41
+ async _syncDefaults(settings = this.settings) {
42
+ const message = settings?.defaultMessage ?? DEFAULTS.message;
43
+ const color = settings?.defaultColor ?? DEFAULTS.color;
44
+ const duration = Number(settings?.defaultDuration ?? DEFAULTS.duration);
45
+
46
+ await this.lumia.setVariable(VARIABLE_NAMES.message, message);
47
+ await this.lumia.setVariable(VARIABLE_NAMES.color, color);
48
+ await this.lumia.setVariable(VARIABLE_NAMES.duration, duration);
49
+ }
50
+
51
+ async _triggerSampleAlert(data = {}) {
52
+ const username = data?.username ?? DEFAULTS.username;
53
+ const message =
54
+ data?.message ?? this.settings?.defaultMessage ?? DEFAULTS.message;
55
+ const color = data?.color ?? this.settings?.defaultColor ?? DEFAULTS.color;
56
+ const duration = Number(
57
+ data?.duration ?? this.settings?.defaultDuration ?? DEFAULTS.duration
58
+ );
59
+
60
+ await this.lumia.setVariable(VARIABLE_NAMES.username, username);
61
+ await this.lumia.setVariable(VARIABLE_NAMES.message, message);
62
+ await this.lumia.setVariable(VARIABLE_NAMES.color, color);
63
+ await this.lumia.setVariable(VARIABLE_NAMES.duration, duration);
64
+
65
+ try {
66
+ await this.lumia.triggerAlert({
67
+ alert: "sample_alert",
68
+ dynamic: {
69
+ value: color,
70
+ username,
71
+ message,
72
+ color,
73
+ duration,
74
+ },
75
+ extraSettings: {
76
+ username,
77
+ message,
78
+ color,
79
+ duration,
80
+ },
81
+ });
82
+ } catch (error) {
83
+ await this.lumia.addLog(
84
+ `Sample alert failed: ${error?.message ?? String(error)}`
85
+ );
86
+ }
87
+ }
88
+ }
89
+
90
+ module.exports = ShowcasePluginTemplate;
@@ -0,0 +1,125 @@
1
+ {
2
+ "id": "showcase_plugin",
3
+ "name": "Showcase Plugin",
4
+ "version": "1.0.0",
5
+ "author": "Lumia Stream",
6
+ "email": "",
7
+ "website": "",
8
+ "repository": "",
9
+ "description": "Starter template that demonstrates settings, actions, variables, and alerts with a minimal code path.",
10
+ "license": "MIT",
11
+ "lumiaVersion": "^9.0.0",
12
+ "category": "apps",
13
+ "keywords": "sample, demo, lumia, showcase, template",
14
+ "icon": "",
15
+ "changelog": "",
16
+ "config": {
17
+ "settings": [
18
+ {
19
+ "key": "defaultMessage",
20
+ "label": "Default Message",
21
+ "type": "text",
22
+ "defaultValue": "Hello from Showcase Plugin!",
23
+ "helperText": "Used when the action does not supply a message."
24
+ },
25
+ {
26
+ "key": "defaultColor",
27
+ "label": "Default Color",
28
+ "type": "color",
29
+ "defaultValue": "#00c2ff",
30
+ "helperText": "Used when the action does not supply a color."
31
+ },
32
+ {
33
+ "key": "defaultDuration",
34
+ "label": "Default Duration (seconds)",
35
+ "type": "number",
36
+ "defaultValue": 5,
37
+ "min": 1,
38
+ "max": 60,
39
+ "helperText": "Used when the action does not supply a duration."
40
+ }
41
+ ],
42
+ "settings_tutorial": "---\n### Setup\n1) Enter a default message and color.\n2) Adjust the default duration if you want a longer or shorter alert.\n3) Click Save to store the defaults.\n---\n### What this plugin does\n- Stores the message, username, color, and duration in variables.\n- Uses those values when triggering the sample alert.\n---",
43
+ "actions_tutorial": "---\n### Trigger Sample Alert\nUse this action to fire the sample alert. You can override the message, username, color, and duration per action. The alert uses both dynamic and extraSettings so variations and templates have the same data.\n---",
44
+ "actions": [
45
+ {
46
+ "type": "trigger_alert",
47
+ "label": "Trigger Sample Alert",
48
+ "description": "Trigger the sample alert with optional overrides.",
49
+ "fields": [
50
+ {
51
+ "key": "username",
52
+ "label": "Username",
53
+ "type": "text",
54
+ "defaultValue": "Viewer"
55
+ },
56
+ {
57
+ "key": "message",
58
+ "label": "Message",
59
+ "type": "text",
60
+ "defaultValue": "Hello from Showcase Plugin!"
61
+ },
62
+ {
63
+ "key": "color",
64
+ "label": "Color",
65
+ "type": "color",
66
+ "defaultValue": "#00c2ff"
67
+ },
68
+ {
69
+ "key": "duration",
70
+ "label": "Duration (seconds)",
71
+ "type": "number",
72
+ "defaultValue": 5,
73
+ "min": 1,
74
+ "max": 60
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "variables": [
80
+ {
81
+ "name": "message",
82
+ "description": "Stores the most recent message handled by the plugin.",
83
+ "value": ""
84
+ },
85
+ {
86
+ "name": "username",
87
+ "description": "Stores the most recent username handled by the plugin.",
88
+ "value": ""
89
+ },
90
+ {
91
+ "name": "color",
92
+ "description": "Tracks the color used by the latest sample alert.",
93
+ "value": ""
94
+ },
95
+ {
96
+ "name": "duration",
97
+ "description": "Tracks the duration used by the latest sample alert.",
98
+ "value": 0
99
+ }
100
+ ],
101
+ "alerts": [
102
+ {
103
+ "title": "Sample Alert",
104
+ "key": "sample_alert",
105
+ "acceptedVariables": [
106
+ "message",
107
+ "username",
108
+ "color",
109
+ "duration"
110
+ ],
111
+ "defaultMessage": "{{username}}: {{message}}",
112
+ "variationConditions": [
113
+ {
114
+ "type": "EQUAL_SELECTION",
115
+ "description": "Matches dynamic.value against the selected color.",
116
+ "selections": [
117
+ { "label": "Blue", "value": "#00c2ff" },
118
+ { "label": "Red", "value": "#ff5f5f" }
119
+ ]
120
+ }
121
+ ]
122
+ }
123
+ ]
124
+ }
125
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "lumia-showcase-plugin-template",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "Internal template illustrating settings, actions, variables, and alerts for Lumia Stream plugins.",
6
+ "main": "main.js",
7
+ "dependencies": {
8
+ "@lumiastream/plugin": "^0.2.5"
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "lumia-plugin",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Command-line tools for creating, building, and validating Lumia Stream plugins.",
5
5
  "bin": {
6
6
  "lumia-plugin": "scripts/cli.js"
7
7
  },
8
+ "scripts": {
9
+ "prepack": "node scripts/sync-template.js",
10
+ "postpack": "node scripts/clean-template.js"
11
+ },
8
12
  "files": [
9
13
  "scripts",
10
14
  "examples/base_plugin",
@@ -20,7 +24,7 @@
20
24
  "author": "Lumia Stream",
21
25
  "license": "MIT",
22
26
  "dependencies": {
23
- "@lumiastream/plugin": "^0.2.3",
27
+ "@lumiastream/plugin": "^0.2.5",
24
28
  "jszip": "3.10.1"
25
29
  }
26
30
  }
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ const DEST_DIR = path.resolve(__dirname, "..", "examples", "base_plugin");
6
+
7
+ async function main() {
8
+ if (!fs.existsSync(DEST_DIR)) return;
9
+ await fs.promises.rm(DEST_DIR, { recursive: true, force: true });
10
+ }
11
+
12
+ main().catch((error) => {
13
+ console.error("✖ Failed to clean template:", error?.message ?? error);
14
+ process.exit(1);
15
+ });
@@ -2,7 +2,19 @@
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
4
 
5
- const TEMPLATE_DIR = path.resolve(__dirname, "..", "examples", "base_plugin");
5
+ const TEMPLATE_DIRS = [
6
+ path.resolve(__dirname, "..", "..", "examples", "base_plugin"),
7
+ path.resolve(__dirname, "..", "examples", "base_plugin"),
8
+ ];
9
+
10
+ function resolveTemplateDir() {
11
+ for (const candidate of TEMPLATE_DIRS) {
12
+ if (fs.existsSync(candidate)) {
13
+ return candidate;
14
+ }
15
+ }
16
+ return null;
17
+ }
6
18
 
7
19
  function toSafeId(value) {
8
20
  const cleaned = value
@@ -95,18 +107,18 @@ async function updateManifest(manifestPath, pluginId, displayName) {
95
107
  if (manifest.config && typeof manifest.config === "object") {
96
108
  const { settings, actions, variables } = manifest.config;
97
109
 
98
- const welcomeSetting = ensureArray(settings).find(
99
- (setting) => setting?.key === "welcomeMessage"
110
+ const defaultMessageSetting = ensureArray(settings).find(
111
+ (setting) => setting?.key === "defaultMessage"
100
112
  );
101
- if (welcomeSetting) {
102
- welcomeSetting.defaultValue = `Hello from ${displayName}!`;
113
+ if (defaultMessageSetting) {
114
+ defaultMessageSetting.defaultValue = `Hello from ${displayName}!`;
103
115
  }
104
116
 
105
- const logAction = ensureArray(actions).find(
106
- (action) => action?.type === "log_message"
117
+ const triggerAlertAction = ensureArray(actions).find(
118
+ (action) => action?.type === "trigger_alert"
107
119
  );
108
- if (logAction && Array.isArray(logAction.fields)) {
109
- const messageField = logAction.fields.find(
120
+ if (triggerAlertAction && Array.isArray(triggerAlertAction.fields)) {
121
+ const messageField = triggerAlertAction.fields.find(
110
122
  (field) => field?.key === "message"
111
123
  );
112
124
  if (messageField) {
@@ -119,7 +131,7 @@ async function updateManifest(manifestPath, pluginId, displayName) {
119
131
  if (variable.origin) {
120
132
  variable.origin = pluginId;
121
133
  }
122
- if (variable.name === "last_message" && "example" in variable) {
134
+ if (variable.name === "message" && "example" in variable) {
123
135
  variable.example = `Hello from ${displayName}!`;
124
136
  }
125
137
  }
@@ -180,8 +192,13 @@ async function main() {
180
192
  return;
181
193
  }
182
194
 
183
- if (!fs.existsSync(TEMPLATE_DIR)) {
184
- console.error("✖ Template directory not found:", TEMPLATE_DIR);
195
+ const templateDir = resolveTemplateDir();
196
+ if (!templateDir) {
197
+ console.error("✖ Template directory not found.");
198
+ console.error(" Looked in:");
199
+ for (const candidate of TEMPLATE_DIRS) {
200
+ console.error(` - ${candidate}`);
201
+ }
185
202
  console.error(" Tip: Make sure lumia-plugin is properly installed.");
186
203
  process.exit(1);
187
204
  }
@@ -198,7 +215,7 @@ async function main() {
198
215
  await ensureEmptyDir(targetDir);
199
216
 
200
217
  console.log(" 📋 Copying template files...");
201
- await copyTemplate(TEMPLATE_DIR, targetDir);
218
+ await copyTemplate(templateDir, targetDir);
202
219
 
203
220
  console.log(" ✏️ Updating manifest.json...");
204
221
  await updateManifest(
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ const SOURCE_DIR = path.resolve(__dirname, "..", "..", "examples", "base_plugin");
6
+ const DEST_DIR = path.resolve(__dirname, "..", "examples", "base_plugin");
7
+
8
+ async function copyDir(src, dest) {
9
+ const stats = await fs.promises.stat(src);
10
+ if (stats.isDirectory()) {
11
+ await fs.promises.mkdir(dest, { recursive: true });
12
+ const entries = await fs.promises.readdir(src);
13
+ for (const entry of entries) {
14
+ await copyDir(path.join(src, entry), path.join(dest, entry));
15
+ }
16
+ return;
17
+ }
18
+ if (stats.isFile()) {
19
+ await fs.promises.copyFile(src, dest);
20
+ }
21
+ }
22
+
23
+ async function main() {
24
+ const sourceExists = fs.existsSync(SOURCE_DIR);
25
+ const destExists = fs.existsSync(DEST_DIR);
26
+
27
+ if (!sourceExists && !destExists) {
28
+ console.error("✖ Template source not found:", SOURCE_DIR);
29
+ process.exit(1);
30
+ }
31
+
32
+ if (!sourceExists) {
33
+ console.warn("⚠ Template source missing, leaving existing copy in place.");
34
+ return;
35
+ }
36
+
37
+ if (destExists) {
38
+ await fs.promises.rm(DEST_DIR, { recursive: true, force: true });
39
+ }
40
+
41
+ await copyDir(SOURCE_DIR, DEST_DIR);
42
+ }
43
+
44
+ main().catch((error) => {
45
+ console.error("✖ Failed to sync template:", error?.message ?? error);
46
+ process.exit(1);
47
+ });