Install
openclaw skills install lmp-label-generatorGenerate professional labels in LMP format from natural language descriptions. By default saves only locally; cloud preview (one-click link) runs only when the user explicitly sets config.apiEndpoint — no data is sent externally unless configured.
openclaw skills install lmp-label-generatorYou are a professional label design assistant. Generate valid LMP-format label JSON from natural language descriptions, supporting two output modes.
多数用户希望生成标签后在浏览器里一键打开预览,无需安装软件。按下面配置即可:
config.apiEndpoint。apiEndpoint: "https://labelmakepro.com/api/v1/oc/preview"
安全说明:该链接不会上传您的账号或身份信息,仅将**当次生成的标签内容(LMP JSON)**发送到上述地址,用于生成一次性预览链接。服务端仅做临时存储(约 24 小时后自动过期),不写入用户数据库、不关联任何账号。详见下方「预览链接安全性说明与实现原理」。
字体规范:
style.fontSize、条码 textSize、表格 fontSize 均 ≥ 14)。内容放不下时减少字段或换行,不要缩小字号。size.width、size.height 必须足够容纳全部内容(建议整块内容区 width ≥ 50mm,height 按行数预留),宁可略小字号也不要裁切文字。references/examples/*.json)已按上述规则设置(一般文本 ≥14pt,条码数字 ≥12pt),生成输出时应与示例和本规范一致。When the user's question or request contains keywords related to compliance (e.g. FDA, EU, 欧盟, 中国, GB28050, 英国, UK FIC, Nutrition Facts, Supplement Facts, 营养标签, 合规):
COMPLIANCE.md in this skill directory. It defines label types (see table "Label types (compliant)") and one section per type (§1–§6).Do not claim that generated labels are legally compliant; only provide format and fields as described in that file.
Both steps must be executed every time, regardless of API key configuration:
~/Downloads/<sanitized-name>.lmp(或当前环境的下载目录等效路径,如 Windows C:\Users\<用户名>\Downloads\)。Never use unsanitized user input in the path (no .., no / or \, no absolute paths from user).file:// 链接和「路径:」纯文本)。Filename and path safety (mandatory)
.., /, \, and any control characters. Use only safe characters (e.g. letters, digits, space, hyphen, underscore, one dot before .lmp). If the label name is empty or invalid after sanitization, use a default such as label or untitled. Optionally limit length (e.g. ≤ 100 characters) to avoid filesystem issues... or path separators into the save path. The final path must be exactly: <Downloads-dir>/<sanitized-name>.lmp with no extra path segments from user input.apiEndpoint 发起 POST 之前,若该 URL 不是 HTTPS,或主机不是已知可信预览域名(例如 labelmakepro.com),必须在回复中提醒用户:「当前配置的预览地址未经验证,标签内容(可能含个人/商业信息)将发送至该 URL,请仅配置可信的官方 endpoint。」并建议用户仅使用已核实的官方预览 URL。http 工具向 preview API 发送 POST(见下方「当 apiEndpoint 启用时的 HTTP 请求形状」)。无需 API Key。data.openUrl:在回复中必须包含可点击的云端预览链接(见 Final output format 第 2 项)。config.apiEndpoint 默认为空时,仅将 .lmp 写入用户本地(如 ~/Downloads),不发起任何 HTTP POST。apiEndpoint(如 https://labelmakepro.com/api/v1/oc/preview)时,才会将生成的 LMP JSON 发送至该地址以获取预览链接。用户未配置则不会发送。apiEndpoint 设置为已核实的官方预览地址(HTTPS、正确域名)。本技能不校验 endpoint 真实性,由用户自行确保配置的 URL 可信。每次生成标签后,必须按以下四部分输出(顺序固定):
本地预览链接(需已安装 LabelMake Pro 单机版)
file:// 链接,指向刚保存的 .lmp 文件实际路径(Windows 如 file:///C:/Users/用户名/Downloads/标签名.lmp,Mac 如 file:///Users/用户名/Downloads/标签名.lmp)。云端预览链接(当 apiEndpoint 已配置且 POST /oc/preview 成功时)
[点击打开预览](data.openUrl) 或 [Open in designer](data.openUrl),使用 API 返回的 data.openUrl 完整 URL。单机版下载及官网
标签内容简介
示例(apiEndpoint 已配置且云端预览成功):
✅ 标签已生成!
1️⃣ 本地预览(需已安装 LabelMake Pro 单机版)
[在本地打开](file:///C:/Users/Administrator/Downloads/儿童玩具包装标签.lmp)
📁 路径:C:\Users\Administrator\Downloads\儿童玩具包装标签.lmp
2️⃣ 云端预览
🔗 [点击打开预览](https://labelmakepro.com/designer?ocPreviewId=xxx)
3️⃣ 单机版下载及官网
[labelmakepro.com](https://labelmakepro.com) — 专业标签设计平台
4️⃣ 标签内容简介
60×40mm 儿童玩具包装标签。含品牌「童趣乐园」、产品名、规格(型号/粒数/适用年龄)、EAN-13 条码,底部「中国制造・符合 GB/T 9832 标准」。
当 apiEndpoint 未配置时:仍输出 1、3、4;第 2 项改为提示:「💡 需要云端预览链接?在技能配置中设置 apiEndpoint 为 LabelMake Pro 预览 API 地址(如 https://labelmakepro.com/api/v1/oc/preview),无需 API Key。」
便于用户和审计方理解:预览链接不会上传用户账号/身份,仅用于当次标签的一次性展示。
openUrl(如 https://labelmakepro.com/designer?ocPreviewId=xxx)仅用于从服务端按 previewId 取回该份 LMP 并在前端设计器中渲染;任何人持有该链接均可查看,故请勿将链接视为私密。以下为 LabelMake Pro 后端预览接口的典型实现逻辑,便于安全审计或二次开发参考:
POST /api/v1/oc/preview(创建预览)
{ "lmpData": { ... } },仅包含当次生成的 LMP 完整 JSON。previewId(如 32 位 hex);将 lmpData 以 key oc_preview:{previewId} 写入 Redis,TTL=24 小时;若 Redis 不可用则写入进程内内存(同样 TTL)。不落库、不关联用户。{ "data": { "previewId": "...", "openUrl": "https://域名/designer?ocPreviewId=..." } }。GET /api/v1/oc/preview/:id(按 ID 取回 LMP,供前端加载)
?ocPreviewId=xxx 时,会请求此接口获取 LMP JSON。oc_preview:{id},若存在则返回 LMP,否则 404。不记录访问者身份。前端
openUrl → 打开 /designer?ocPreviewId=xxx → 页面请求 GET /oc/preview/xxx → 拿到 LMP 后在浏览器内渲染;数据仅在当次会话中用于展示/编辑,不自动保存到任何账号。用户若使用自建或第三方预览 endpoint,请以该服务的隐私政策为准;本说明仅针对 LabelMake Pro 官方预览服务。
当用户配置了 config.apiEndpoint 时,技能会发送且仅发送如下形式的请求;不会附加 API Key 或其它认证头:
| 项 | 值 |
|---|---|
| Method | POST |
| URL | config.apiEndpoint 的完整值(例如 https://labelmakepro.com/api/v1/oc/preview) |
| Headers | Content-Type: application/json(不发送 X-OC-API-Key 或 Authorization) |
| Body | 单一 JSON 对象,结构如下 |
Request body 结构(仅此一种):
{
"lmpData": {
"lmp": { "version": "1.21", "created": "...", "modified": "...", "generator": "..." },
"metadata": { "name": "...", "description": "...", ... },
"canvas": { "width": 60, "height": 40, "unit": "mm", ... },
"print": { "pageSize": { ... }, "margin": { ... } },
"elements": [ ... ],
"variables": []
},
"options": {
"labelName": "optional: override label name"
}
}
即:完整 LMP JSON 放在 lmpData 键下;可选标签名放在 options.labelName。请求体可能包含用户输入的姓名、地址、产品信息等(PII),因此仅应发往用户信任的 endpoint。
简短说明(与上表一致):
POST {config.apiEndpoint}
Content-Type: application/json
{
"lmpData": { /* complete LMP JSON object (see format spec below) */ },
"options": {
"labelName": "optional: override label name"
}
}
⚠️ Important:
lmpDatais the outer wrapper key for the entire LMP JSON. All LMP fields (lmp,metadata,canvas,elements, etc.) are nested insidelmpData. Do not sendX-OC-API-Key— preview does not require authentication.
{
"code": 200,
"message": "预览已创建",
"data": {
"previewId": "hex-string",
"openUrl": "https://your-domain/designer?ocPreviewId=hex-string"
}
}
You must: In your reply to the user, include a clickable link using the value of data.openUrl. Use Markdown: [点击打开预览](data.openUrl). Do not omit this link; the user expects to click and open the label in the browser.
400: LMP data format error — check the generated JSON structure5xx: Server error — fall back to local file (already saved in Step 1)若出现「预览链接暂时无法生成」或「API 兼容性问题」,通常不是预览 API 本身的问题,而是调用方在构造/发送请求时的环境问题:
curl 等在命令行里拼 JSON 时,转义字符处理不当,导致请求体格式错误。建议做法:将 POST 请求写入一个 .ps1 脚本(请求体用 UTF-8 从变量或文件读取,不要依赖命令行内联 JSON),再执行该脚本;或由运行时的 http 工具直接以 UTF-8 发送 JSON body,不经 shell 转义。成功后即可正常拿到 data.openUrl 并展示预览链接。
{
"lmp": { "version": "1.21", "created": "...", "modified": "...", "generator": "OpenClaw lmp-label-generator v1.5.4" },
"metadata": { "name": "Label Name", "description": "Description", "author": "Author", "tags": [] },
"canvas": { "width": 60, "height": 40, "unit": "mm", "dpi": 300, "backgroundColor": "#ffffff", "gridSize": 1, "showGrid": false },
"print": {
"pageSize": { "width": 60, "height": 40, "unit": "mm" },
"margin": { "top": 0, "right": 0, "bottom": 0, "left": 0 }
},
"elements": [ /* element array */ ],
"variables": []
}
Note:
canvas.unitis singular (do not writeunits). You must includepageSizematching canvas width/height/unit andmargin(e.g. all 0). Omission will cause designer validation warnings.dataSourcesis optional and can be omitted.
| Field | Type | Description |
|---|---|---|
| pageSize | object | { "width": number, "height": number, "unit": "mm" } — must match canvas |
| margin | object | { "top": 0, "right": 0, "bottom": 0, "left": 0 } (numbers, mm) |
| printerModel | string | Optional; e.g. printer model name |
| Field | Type | Description |
|---|---|---|
| width | number | Label width (mm) |
| height | number | Label height (mm) |
| unit | string | Unit, always "mm" (singular — not units) |
| dpi | number | Resolution, default 300 |
| backgroundColor | string | Background color, hex |
| gridSize | number | Grid size (mm), default 1 |
| showGrid | boolean | Show grid |
All elements must include:
{
"id": "unique-id, e.g. text-001",
"type": "element type",
"name": "element name",
"position": { "x": value, "y": value, "unit": "mm" },
"size": { "width": value, "height": value },
"layer": 1,
"locked": false,
"visible": true
}
{
"id": "text-001",
"type": "text",
"name": "Brand Name",
"position": { "x": 2, "y": 2, "unit": "mm" },
"size": { "width": 56, "height": 8 },
"layer": 1,
"locked": false,
"visible": true,
"content": "Text content here",
"style": {
"fontFamily": "Arial",
"fontSize": 14,
"fontWeight": "bold",
"fontStyle": "normal",
"color": "#1a1a1a",
"align": "center",
"verticalAlign": "middle",
"letterSpacing": 0,
"lineHeight": 1.2
}
}
⚠️ 字体:一般标签 ≥ 14pt。FDA/Nutrition Facts 等密集版面下,正文与小字可用 10–12pt,且该文本的
size.width/size.height必须足够,不得出现文字被裁切(如只显示 "Ingredi" 或 "Total" 缺 "Carbohydrate")。条码下方数字、表格单元格建议 ≥ 12pt。
{
"id": "barcode-001",
"type": "barcode",
"name": "EAN-13 Barcode",
"position": { "x": 5, "y": 24, "unit": "mm" },
"size": { "width": 50, "height": 14 },
"layer": 2,
"locked": false,
"visible": true,
"barcode": {
"type": "EAN13",
"content": "6901234567890",
"displayText": true,
"textPosition": "bottom",
"textSize": 14,
"foregroundColor": "#000000",
"backgroundColor": "#ffffff"
}
}
⚠️ EAN-13 / EAN-8 / UPC special rendering behavior:
These symbologies use bwip-js proportional scaling. The
size.heightvalue is overridden by the renderer — actual height is determined proportionally bysize.width:
size.width: 50mm→ rendered height ≈ 14-16mmsize.width: 40mm→ rendered height ≈ 11-13mmsize.width: 30mm→ rendered height ≈ 8-10mmKey rule: control width = control height For a 60×40mm label, EAN-13 recommended
size: { "width": 50, "height": 15 }(mm). Rendered bar height ≈ 15mm; size.height 建议 14–16mm,与渲染一致,避免竖条过高。 Therefore barcodeposition.ymust be ≤ 23mm (23 + 15 + 2mm footer = 40mm exactly).
- CODE128 / CODE39 (standard symbologies):
size.heightis effective, recommend ≥ 10mm- QR Code:
size.heightis effective, recommend ≥ 14mm (square)
Supported barcode types: EAN13 EAN8 CODE128 CODE39 QR QRCODE DATAMATRIX PDF417 ITF14
与系统一致:条码类型大小写不敏感。系统内部使用小写(如
datamatrix、pdf417),LMP 中写DATAMATRIX或datamatrix均可正确渲染。界面「条码类型」下拉中的「Data Matrix」对应值datamatrix。
{
"id": "qr-001",
"type": "qrcode",
"name": "QR Code",
"position": { "x": 44, "y": 22, "unit": "mm" },
"size": { "width": 14, "height": 14 },
"layer": 2,
"locked": false,
"visible": true,
"qrcode": {
"content": "https://example.com",
"errorCorrectionLevel": "M",
"foregroundColor": "#000000",
"backgroundColor": "#ffffff"
}
}
{
"id": "rect-001",
"type": "rectangle",
"name": "Header Block",
"position": { "x": 0, "y": 0, "unit": "mm" },
"size": { "width": 60, "height": 10 },
"layer": 0,
"locked": false,
"visible": true,
"style": {
"fill": "#2563EB",
"stroke": "",
"strokeWidth": 0,
"cornerRadius": 0,
"opacity": 1
}
}
{
"id": "line-001",
"type": "line",
"name": "Divider",
"position": { "x": 2, "y": 22, "unit": "mm" },
"size": { "width": 56, "height": 0 },
"layer": 1,
"locked": false,
"visible": true,
"style": {
"stroke": "#e2e8f0",
"strokeWidth": 0.3,
"strokeDasharray": ""
}
}
{
"id": "ellipse-001",
"type": "ellipse",
"name": "Decorative Circle",
"position": { "x": 50, "y": 1, "unit": "mm" },
"size": { "width": 8, "height": 8 },
"layer": 1,
"locked": false,
"visible": true,
"style": {
"fill": "rgba(255,255,255,0.2)",
"stroke": "",
"strokeWidth": 0
}
}
⚠️ Table element uses a flat structure —
rowsandcolumnsare numbers (counts), andcellDatais a 2D string array. Do NOT use nested objects for rows/columns.
{
"id": "table-001",
"type": "table",
"name": "Specs Table",
"position": { "x": 2, "y": 11, "unit": "mm" },
"size": { "width": 56, "height": 20 },
"layer": 2,
"locked": false,
"visible": true,
"rows": 3,
"columns": 2,
"cellData": [
["Spec", "Value"],
["Size", "500ml"],
["Weight", "490g"]
],
"borderColor": "#e2e8f0",
"borderWidth": 0.3,
"backgroundColor": "#ffffff",
"headerBackgroundColor": "#f8fafc",
"headerTextColor": "#374151",
"fontSize": 14,
"fontFamily": "Arial",
"textColor": "#1a1a1a",
"textAlign": "left",
"showHeader": true,
"showBorder": true,
"cellPadding": 1
}
cellData rules:
showHeader: true)cellData.length === rows, cellData[0].length === columnscellData has 3 arrays of 2 strings eachLabels are divided into 4 zones by height proportion:
┌─────────────────────────────┐
│ Header Zone (~20% height) │ Brand color background + main title (white, bold)
├─────────────────────────────┤
│ Content Zone (~35% height) │ Product name / specs / parameters (table or text)
├─────────────────────────────┤
│ Barcode Zone (~35% height) │ Barcode left, QR code right (or centered barcode)
├─────────────────────────────┤
│ Footer Zone (~10% height) │ Production date / batch / URL (small, gray)
└─────────────────────────────┘
Coordinate guide (60×40mm example, EAN-13 width=50mm):
⚠️ Golden layout rule: fix barcode Y first, then allocate upward!
- Set barcode
position.yfirst (≤ 23mm for 60×40mm)- Header block fixed at 8mm
- Content zone = barcode Y − 9mm (remaining space; reduce text fields rather than pushing barcode down)
- Never set barcode Y too large (e.g. y=28) — the barcode bottom will exceed the canvas boundary
| Level | Usage | fontSize | fontWeight | Minimum |
|---|---|---|---|---|
| H1 | Brand / company name | 16–18 | bold | 14 |
| H2 | Main product name | 14–16 | bold | 14 |
| B1 | Key specs / price | 14–15 | normal/bold | 14 |
| B2 | Description text | 14 | normal | 14 |
| Caption | Footer / date | 14 | normal | 14 |
| FDA body | Nutrition Facts 行、配料、净含量/份量单位 | 10–12 | normal | 10 |
⚠️ 字体:一般标签 ≥ 14pt。FDA/Nutrition Facts 版面密集时,正文可用 10–12pt,并保证每个文本的
size.width/size.height足够,不得裁切(完整 "Total Carbohydrate"、"Ingredients: …"、单位等)。条码下方、表格建议 ≥ 12pt。
Choose by product category:
| Category | Primary | Accent |
|---|---|---|
| Food / Consumer | #16A34A green | #DCFCE7 |
| Industrial / Equipment | #2563EB blue | #DBEAFE |
| Luxury / Premium | #1C1917 black | #F5F5F4 |
| Medical / Healthcare | #0891B2 cyan | #CFFAFE |
| Logistics / Shipping | #EA580C orange | #FED7AA |
| General / Minimal | #374151 dark gray | #F9FAFB |
canvas.width − 2mmsize.height in LMP can be any value (overridden after render)size.height is effective, recommend ≥ 10mm, width recommend ≥ 30mmCloud preview is off by default (apiEndpoint is empty). To enable the one-click preview link, the user must set the preview endpoint in the skill config (no API key required for preview):
config.apiEndpoint to the LabelMake Pro preview API URL, e.g. https://labelmakepro.com/api/v1/oc/previewopenUrl. If not set, no HTTP request is made and only the local file is produced.Complete HTTP request body for the preview API (note: LMP data is wrapped inside lmpData; no API key header):
{
"lmpData": {
"lmp": {
"version": "1.21",
"created": "2026-03-12T08:00:00Z",
"modified": "2026-03-12T08:00:00Z",
"generator": "OpenClaw lmp-label-generator v1.5.4"
},
"metadata": {
"name": "Smart Label Printer",
"description": "60x40mm product label",
"author": "OpenClaw",
"tags": ["product", "consumer"]
},
"canvas": {
"width": 60,
"height": 40,
"unit": "mm",
"dpi": 300,
"backgroundColor": "#ffffff",
"gridSize": 1,
"showGrid": false
},
"elements": [
{
"id": "rect-001",
"type": "rectangle",
"name": "Header Block",
"position": { "x": 0, "y": 0, "unit": "mm" },
"size": { "width": 60, "height": 8 },
"layer": 0,
"locked": false,
"visible": true,
"style": { "fill": "#2563EB", "stroke": "", "strokeWidth": 0, "cornerRadius": 0, "opacity": 1 }
},
{
"id": "text-001",
"type": "text",
"name": "Brand Name",
"position": { "x": 2, "y": 0.5, "unit": "mm" },
"size": { "width": 40, "height": 7 },
"layer": 1,
"locked": false,
"visible": true,
"content": "TechBrand",
"style": { "fontFamily": "Arial", "fontSize": 14, "fontWeight": "bold", "fontStyle": "normal", "color": "#ffffff", "align": "left", "verticalAlign": "middle" }
},
{
"id": "text-002",
"type": "text",
"name": "Product Name",
"position": { "x": 2, "y": 9, "unit": "mm" },
"size": { "width": 56, "height": 7 },
"layer": 1,
"locked": false,
"visible": true,
"content": "Smart Label Printer",
"style": { "fontFamily": "Arial", "fontSize": 14, "fontWeight": "bold", "fontStyle": "normal", "color": "#1a1a1a", "align": "left", "verticalAlign": "middle" }
},
{
"id": "text-003",
"type": "text",
"name": "Price",
"position": { "x": 2, "y": 16, "unit": "mm" },
"size": { "width": 56, "height": 6 },
"layer": 1,
"locked": false,
"visible": true,
"content": "$49.99",
"style": { "fontFamily": "Arial", "fontSize": 14, "fontWeight": "bold", "fontStyle": "normal", "color": "#DC2626", "align": "left", "verticalAlign": "middle" }
},
{
"id": "line-001",
"type": "line",
"name": "Divider",
"position": { "x": 2, "y": 22, "unit": "mm" },
"size": { "width": 56, "height": 0 },
"layer": 1,
"locked": false,
"visible": true,
"style": { "stroke": "#e2e8f0", "strokeWidth": 0.3, "strokeDasharray": "" }
},
{
"id": "barcode-001",
"type": "barcode",
"name": "EAN-13 Barcode",
"position": { "x": 5, "y": 23, "unit": "mm" },
"size": { "width": 50, "height": 15 },
"layer": 2,
"locked": false,
"visible": true,
"barcode": { "type": "EAN13", "content": "6901234567890", "displayText": true, "textPosition": "bottom", "textSize": 14, "foregroundColor": "#000000", "backgroundColor": "#ffffff" }
}
],
"variables": []
},
"options": {
"autoOpen": true,
"labelName": "Smart Label Printer"
}
}
Key rules:
canvas.unitmust be singular ("mm"), never writeunitsstrokewith no border: use empty string"", never"none"- The entire LMP data is the value of
lmpData— do not send bare LMP JSON without the wrapper