@synopackageland/cli 0.7.1 → 0.7.3
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/CHANGELOG.md +102 -0
- package/assets/default-icon/README.md +19 -0
- package/assets/default-icon/default-icon-small.svg +9 -0
- package/assets/default-icon/default-icon.svg +10 -0
- package/assets/default-icon/icon_16.png +0 -0
- package/assets/default-icon/icon_24.png +0 -0
- package/assets/default-icon/icon_256.png +0 -0
- package/assets/default-icon/icon_32.png +0 -0
- package/assets/default-icon/icon_48.png +0 -0
- package/assets/default-icon/icon_64.png +0 -0
- package/assets/default-icon/icon_72.png +0 -0
- package/dist/build-spk/dsm-icons.d.ts +31 -2
- package/dist/build-spk/dsm-icons.js +135 -56
- package/dist/build-spk/generator.js +17 -3
- package/dist/build-spk/png.d.ts +14 -0
- package/dist/build-spk/png.js +161 -0
- package/dist/build-spk/ui-texts.d.ts +23 -0
- package/dist/build-spk/ui-texts.js +46 -0
- package/dist/cli.js +26 -2
- package/dist/contract/i18n.d.ts +5 -0
- package/dist/contract/i18n.js +45 -0
- package/dist/contract/icon.d.ts +5 -0
- package/dist/contract/icon.js +27 -0
- package/dist/contract/validate.js +4 -0
- package/dist/contract/web-launch.js +3 -2
- package/dist/error-registry.js +3 -0
- package/dist/standalone/dsm-window.d.ts +2 -0
- package/dist/standalone/dsm-window.js +19 -6
- package/dist/standalone/i18n.d.ts +12 -0
- package/dist/standalone/i18n.js +30 -0
- package/dist/standalone/inspect-spk.d.ts +5 -0
- package/dist/standalone/inspect-spk.js +36 -1
- package/dist/standalone/manifest.d.ts +5 -6
- package/dist/standalone/manifest.js +21 -34
- package/dist/standalone/native-layout.d.ts +2 -0
- package/dist/standalone/native-layout.js +22 -1
- package/dist/standalone/spk.js +7 -4
- package/dist/templates/binary/dsm-package.yaml +3 -0
- package/dist/templates/compose/synology-app.yaml +3 -0
- package/dist/templates/native-go/dsm-package.yaml +3 -0
- package/package.json +2 -1
- package/schema/synology-app.schema.json +22 -0
- package/templates/binary/dsm-package.yaml +3 -0
- package/templates/compose/synology-app.yaml +3 -0
- package/templates/native-go/dsm-package.yaml +3 -0
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { parse } from "yaml";
|
|
6
6
|
import { isNativeArchFamily } from "../build-spk/dsm-arch.js";
|
|
7
|
-
import { checkGlossary,
|
|
7
|
+
import { checkGlossary, loadCatalogs, readInfoTranslations } from "./i18n.js";
|
|
8
8
|
import { loadRootOps, ROOT_OPS_DIR } from "./root-ops.js";
|
|
9
9
|
import { readDaemon } from "./daemon.js";
|
|
10
10
|
import { readBackground } from "./background.js";
|
|
@@ -12,6 +12,7 @@ import { DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM, portalMinDsmMessage } fr
|
|
|
12
12
|
import { readSharedFolder } from "./shared-folder.js";
|
|
13
13
|
import { floorAtAdministrators, permitsOrdinaryUsers, readApiPermissions } from "./api-permissions.js";
|
|
14
14
|
import { readPortalDeclaration } from "../portal/alias.js";
|
|
15
|
+
import { renderPackageIcons } from "../build-spk/dsm-icons.js";
|
|
15
16
|
/**
|
|
16
17
|
* `dsm-package.yaml`: a Standalone Package, i.e. an unsigned native SPK that
|
|
17
18
|
* runs as its own DSM package account with a CGI backend and a DSM window UI.
|
|
@@ -19,10 +20,9 @@ import { readPortalDeclaration } from "../portal/alias.js";
|
|
|
19
20
|
export const STANDALONE_MANIFEST = "dsm-package.yaml";
|
|
20
21
|
export const KNOWN_FIELDS = new Set([
|
|
21
22
|
"schema", "package", "version", "build", "displayname", "description",
|
|
22
|
-
"metadata", "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install", "entry", "portal",
|
|
23
|
+
"metadata", "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install", "entry", "portal", "icon",
|
|
23
24
|
]);
|
|
24
25
|
const I18N_FIELDS = new Set(["messages", "glossary", "info"]);
|
|
25
|
-
const INFO_TEXT_FIELDS = new Set(["displayname", "description"]);
|
|
26
26
|
const PACKAGE_ID = /^[A-Za-z][A-Za-z0-9]{1,63}$/;
|
|
27
27
|
const VERSION = /^\d+(\.\d+){0,3}$/;
|
|
28
28
|
const OS_MIN_VER = /^\d+\.\d+(\.\d+)?-\d+$/;
|
|
@@ -154,6 +154,7 @@ function readManifest(raw, c) {
|
|
|
154
154
|
if (capabilities.delegate?.scope === "install-selected" && ui && !ui.build)
|
|
155
155
|
c.fail("CAPABILITY_COMBINATION_INVALID", "delegate scope install-selected 只能在 Vue 面板確認;plain UI 不支援,請改用 vue UI(init --ui vue)。");
|
|
156
156
|
const i18n = raw.i18n === undefined ? undefined : readI18n(raw.i18n, c);
|
|
157
|
+
const icon = raw.icon === undefined ? undefined : readIcon(raw.icon, c);
|
|
157
158
|
const portal = raw.portal === undefined || !packageId ? undefined : readPortalDeclaration(raw.portal, packageId)
|
|
158
159
|
?? c.fail("STANDALONE_PORTAL_INVALID", "portal 必須是 {} 或 { alias: <小寫英數與 -,1–64 字,不可為 DSM 保留路徑> }。");
|
|
159
160
|
if (portal && osMinVer && !isDsmAtLeast(osMinVer, PORTAL_MIN_DSM)) {
|
|
@@ -185,8 +186,21 @@ function readManifest(raw, c) {
|
|
|
185
186
|
...(sharedFolder ? { sharedFolder } : {}),
|
|
186
187
|
...(entry ? { entry } : {}),
|
|
187
188
|
...(portal ? { portal } : {}),
|
|
189
|
+
...(icon ? { icon } : {}),
|
|
188
190
|
};
|
|
189
191
|
}
|
|
192
|
+
function readIcon(value, c) {
|
|
193
|
+
const path = c.path(value, "icon");
|
|
194
|
+
if (!path)
|
|
195
|
+
return undefined;
|
|
196
|
+
try {
|
|
197
|
+
renderPackageIcons(path);
|
|
198
|
+
return path;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return c.fail("STANDALONE_ICON_INVALID", `icon:${error.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
190
204
|
/**
|
|
191
205
|
* The CGI enforces the entry mode only when the project's backend carries the
|
|
192
206
|
* SDK gate and main wires it; `-X main.entry` on an older main would be
|
|
@@ -282,7 +296,10 @@ function readI18n(value, c) {
|
|
|
282
296
|
if (!isRecord(value) || Object.keys(value).some((key) => !I18N_FIELDS.has(key))) {
|
|
283
297
|
return invalid("只接受 messages(訊息目錄)、glossary(用語表)與 info(各語言 INFO 文字)。");
|
|
284
298
|
}
|
|
285
|
-
const info =
|
|
299
|
+
const info = readInfoTranslations(value.info, {
|
|
300
|
+
invalid: (why) => c.fail("STANDALONE_I18N_INVALID", `i18n.info ${why}`),
|
|
301
|
+
text: (text, field) => c.text(text, field),
|
|
302
|
+
});
|
|
286
303
|
if (value.messages === undefined) {
|
|
287
304
|
return value.glossary === undefined ? info && { info } : invalid("glossary 需要搭配 messages。");
|
|
288
305
|
}
|
|
@@ -300,36 +317,6 @@ function readI18n(value, c) {
|
|
|
300
317
|
drift.forEach((e) => c.fail(e.code, e.message));
|
|
301
318
|
return info && drift.length === 0 ? { catalogs: loaded.catalogs, info } : undefined;
|
|
302
319
|
}
|
|
303
|
-
function readInfoText(value, c) {
|
|
304
|
-
if (value === undefined) {
|
|
305
|
-
return {};
|
|
306
|
-
}
|
|
307
|
-
const invalid = (why) => c.fail("STANDALONE_I18N_INVALID", `i18n.info ${why}`);
|
|
308
|
-
if (!isRecord(value)) {
|
|
309
|
-
return invalid("必須以語言為 key,例如 zh-TW: { displayname, description }。");
|
|
310
|
-
}
|
|
311
|
-
const info = {};
|
|
312
|
-
for (const [locale, text] of Object.entries(value)) {
|
|
313
|
-
if (!isLocale(locale) || locale === "en") {
|
|
314
|
-
return invalid(`的「${locale}」不是支援的翻譯語言;英文寫在頂層 displayname / description。`);
|
|
315
|
-
}
|
|
316
|
-
if (!isRecord(text) || Object.keys(text).some((key) => !INFO_TEXT_FIELDS.has(key))) {
|
|
317
|
-
return invalid(`.${locale} 只接受 displayname 與 description。`);
|
|
318
|
-
}
|
|
319
|
-
const entry = {};
|
|
320
|
-
for (const field of INFO_TEXT_FIELDS) {
|
|
321
|
-
if (text[field] !== undefined) {
|
|
322
|
-
const checked = c.text(text[field], `i18n.info.${locale}.${field}`);
|
|
323
|
-
if (!checked) {
|
|
324
|
-
return undefined;
|
|
325
|
-
}
|
|
326
|
-
entry[field] = checked;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
info[locale] = entry;
|
|
330
|
-
}
|
|
331
|
-
return info;
|
|
332
|
-
}
|
|
333
320
|
/**
|
|
334
321
|
* The nearest directory at or above `start` that holds dsm-package.yaml,
|
|
335
322
|
* unless a Managed Profile project (synology-app.yaml) is found first.
|
|
@@ -37,6 +37,8 @@ export interface NativeLayout {
|
|
|
37
37
|
backend: {
|
|
38
38
|
kind: 'native';
|
|
39
39
|
};
|
|
40
|
+
/** Project-relative package icon; the CLI then owns PACKAGE_ICON*.PNG, the payload keeps its own window icons. */
|
|
41
|
+
icon?: string;
|
|
40
42
|
layout: {
|
|
41
43
|
payload: string;
|
|
42
44
|
metadata: string;
|
|
@@ -8,6 +8,7 @@ import { parse } from 'yaml';
|
|
|
8
8
|
import { createReproducibleTar } from '../build-spk/reproducible-archive.js';
|
|
9
9
|
import { runCommand } from './build.js';
|
|
10
10
|
import { resolveMinDsm } from '../build-spk/dsm-version.js';
|
|
11
|
+
import { renderPackageIcons, writePackageCenterIcons } from '../build-spk/dsm-icons.js';
|
|
11
12
|
export const LAYOUT_ARCHES = { x86_64: [2, 62], aarch64: [2, 183], armv8: [2, 183], armv7: [1, 40] };
|
|
12
13
|
function fail(message) { throw Object.assign(new Error(`NATIVE_LAYOUT_INVALID: ${message}`), { code: 'NATIVE_LAYOUT_INVALID' }); }
|
|
13
14
|
const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
@@ -61,6 +62,18 @@ function hook(value) {
|
|
|
61
62
|
fail('invalid hook environment');
|
|
62
63
|
return { command: h.command, cwd: pathText(h.cwd ?? '.'), env };
|
|
63
64
|
}
|
|
65
|
+
function icon(root, value) {
|
|
66
|
+
const path = pathText(value);
|
|
67
|
+
if (existsSync(resolve(root, path)))
|
|
68
|
+
inside(root, path);
|
|
69
|
+
try {
|
|
70
|
+
renderPackageIcons(resolve(root, path));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
fail(`icon: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
64
77
|
export function isNativeLayout(root) {
|
|
65
78
|
try {
|
|
66
79
|
return (() => { const raw = parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')); return raw?.layout !== undefined || raw?.backend?.kind === 'native'; })();
|
|
@@ -71,7 +84,7 @@ export function isNativeLayout(root) {
|
|
|
71
84
|
}
|
|
72
85
|
export function loadNativeLayout(root) {
|
|
73
86
|
try {
|
|
74
|
-
const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'metadata', 'os_min_ver', 'privilege', 'arches', 'layout', 'native']);
|
|
87
|
+
const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'metadata', 'os_min_ver', 'privilege', 'arches', 'layout', 'native', 'icon']);
|
|
75
88
|
if (m.schema !== 2)
|
|
76
89
|
fail('schema 2 required; migrate schema: 1 to schema: 2 and add backend: {kind: native}');
|
|
77
90
|
const backend = object(m.backend, ['kind']);
|
|
@@ -90,6 +103,8 @@ export function loadNativeLayout(root) {
|
|
|
90
103
|
text(m[k]);
|
|
91
104
|
if (!Array.isArray(m.arches) || !m.arches.length || new Set(m.arches).size !== m.arches.length || m.arches.some((a) => !Object.hasOwn(LAYOUT_ARCHES, a)))
|
|
92
105
|
fail('invalid architecture list');
|
|
106
|
+
if (m.icon !== undefined)
|
|
107
|
+
m.icon = icon(root, m.icon);
|
|
93
108
|
const layout = object(m.layout, ['payload', 'metadata', 'binaries', 'anchors']);
|
|
94
109
|
layout.payload = pathText(layout.payload);
|
|
95
110
|
layout.metadata = pathText(layout.metadata);
|
|
@@ -291,6 +306,7 @@ export async function buildNativeLayout(options) {
|
|
|
291
306
|
const arches = options.arches ?? m.arches;
|
|
292
307
|
if (!arches.length || new Set(arches).size !== arches.length || arches.some(a => !m.arches.includes(a)))
|
|
293
308
|
fail('architecture not declared');
|
|
309
|
+
const icons = m.icon === undefined ? undefined : renderPackageIcons(inside(m.root, m.icon));
|
|
294
310
|
const run = options.run ?? runCommand;
|
|
295
311
|
const version = `${m.version}-${String(m.build).padStart(4, '0')}`;
|
|
296
312
|
const env = { SYNOPKGLAND_PACKAGE: m.package, SYNOPKGLAND_VERSION: m.version, SYNOPKGLAND_BUILD: String(m.build), SYNOPKGLAND_SPK_VERSION: version, SYNOPKGLAND_PROJECT_DIR: m.root };
|
|
@@ -313,6 +329,11 @@ export async function buildNativeLayout(options) {
|
|
|
313
329
|
for (const b of binaries)
|
|
314
330
|
checkLayoutBinary(payload, b, arch);
|
|
315
331
|
copyTree(metadata, stage);
|
|
332
|
+
if (icons) {
|
|
333
|
+
if (['PACKAGE_ICON.PNG', 'PACKAGE_ICON_256.PNG'].some(name => existsSync(join(metadata, name))))
|
|
334
|
+
fail('icon and PACKAGE_ICON files in the layout metadata are mutually exclusive; keep one');
|
|
335
|
+
writePackageCenterIcons(stage, icons);
|
|
336
|
+
}
|
|
316
337
|
const target = join(stage, 'payload');
|
|
317
338
|
mkdirSync(target);
|
|
318
339
|
const files = copyTree(payload, target);
|
package/dist/standalone/spk.js
CHANGED
|
@@ -4,7 +4,7 @@ import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync,
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { infoArchValue } from "../build-spk/dsm-arch.js";
|
|
7
|
-
import { writeDsmWindowIcons, writePackageCenterIcons } from "../build-spk/dsm-icons.js";
|
|
7
|
+
import { packageIcons, writeDsmWindowIcons, writePackageCenterIcons } from "../build-spk/dsm-icons.js";
|
|
8
8
|
import { renderNativePrivilege } from "../build-spk/native-lifecycle.js";
|
|
9
9
|
import { renderUninstallWizardFiles } from "../build-spk/uninstall-wizard.js";
|
|
10
10
|
import { renderInstallWizardFiles, renderNativeInstallWizard } from "../build-spk/install-wizard.js";
|
|
@@ -13,7 +13,8 @@ import { renderDelegateScopeSteps } from '../build-spk/delegate-scope.js';
|
|
|
13
13
|
import { createReproducibleTar } from "../build-spk/reproducible-archive.js";
|
|
14
14
|
import { renderStandaloneScript } from "./lifecycle.js";
|
|
15
15
|
import { DSM_LANGS, LOCALES } from "./i18n.js";
|
|
16
|
-
import { CGI_PAYLOAD_PATH, renderUiConfig, renderWindowScript, windowScriptName } from "./dsm-window.js";
|
|
16
|
+
import { appTitle, CGI_PAYLOAD_PATH, renderUiConfig, renderWindowScript, windowScriptName } from "./dsm-window.js";
|
|
17
|
+
import { writeUiTexts } from "../build-spk/ui-texts.js";
|
|
17
18
|
import { copyStandaloneNotices } from "../build-spk/notices.js";
|
|
18
19
|
import { hostScriptName, renderHostAdapterScript } from "./host-adapter.js";
|
|
19
20
|
import { copyDaemonPayload } from "./daemon.js";
|
|
@@ -54,6 +55,7 @@ export async function assembleStandaloneSpk(options) {
|
|
|
54
55
|
try {
|
|
55
56
|
if ((manifest.capabilities.rootOps || manifest.capabilities.delegate) && !options.executor)
|
|
56
57
|
throw new StandaloneBuildError("EXECUTOR_PIN_REQUIRED", "Executor artifact required.");
|
|
58
|
+
const icons = packageIcons(manifest.icon);
|
|
57
59
|
const uiRoot = join(staging, "package", "ui");
|
|
58
60
|
const appDir = join(uiRoot, "app");
|
|
59
61
|
copyUiTree(manifest.ui.dir, appDir);
|
|
@@ -61,7 +63,8 @@ export async function assembleStandaloneSpk(options) {
|
|
|
61
63
|
writeFile(join(uiRoot, "config"), renderUiConfig(manifest));
|
|
62
64
|
writeFile(join(uiRoot, windowScriptName(manifest)), renderWindowScript(manifest, assetVersion));
|
|
63
65
|
writeFile(join(uiRoot, hostScriptName(manifest.build)), renderHostAdapterScript(manifest.package));
|
|
64
|
-
writeDsmWindowIcons(uiRoot);
|
|
66
|
+
writeDsmWindowIcons(uiRoot, icons);
|
|
67
|
+
writeUiTexts(uiRoot, appTitle(manifest));
|
|
65
68
|
const cgiTarget = join(staging, "package", CGI_PAYLOAD_PATH);
|
|
66
69
|
mkdirSync(dirname(cgiTarget), { recursive: true });
|
|
67
70
|
copyFileSync(options.cgiPath, cgiTarget);
|
|
@@ -129,7 +132,7 @@ export async function assembleStandaloneSpk(options) {
|
|
|
129
132
|
...(folder ? renderSharedFolderUpgradeWizardFiles(manifest.package, folder) : {}),
|
|
130
133
|
...renderUninstallWizardFiles(manifest.package, "native", { executor: Boolean(options.executor) }),
|
|
131
134
|
});
|
|
132
|
-
writePackageCenterIcons(staging);
|
|
135
|
+
writePackageCenterIcons(staging, icons);
|
|
133
136
|
mkdirSync(dirname(options.outputPath), { recursive: true });
|
|
134
137
|
await createReproducibleTar(staging, options.outputPath);
|
|
135
138
|
}
|
|
@@ -5,6 +5,9 @@ version: 0.1.0
|
|
|
5
5
|
build: 1
|
|
6
6
|
displayname: __DISPLAY_NAME__
|
|
7
7
|
description: A DSM package with a prebuilt binary CGI and DSM window UI.
|
|
8
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
9
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
10
|
+
# icon: assets/icon.png
|
|
8
11
|
maintainer: Your Name
|
|
9
12
|
# Optional INFO/website metadata. Omit fields you do not need.
|
|
10
13
|
# metadata:
|
|
@@ -3,6 +3,9 @@ package: com.synology.hello.files
|
|
|
3
3
|
version: 0.1.0
|
|
4
4
|
displayname: Hello Files
|
|
5
5
|
description: A minimal headless app with a Shared Folder mount.
|
|
6
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
7
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
8
|
+
# icon: assets/icon.png
|
|
6
9
|
maintainer: Syno Package Land
|
|
7
10
|
os_min_ver: "7.2.1-69057"
|
|
8
11
|
|
|
@@ -10,6 +10,9 @@ version: 0.1.0
|
|
|
10
10
|
build: 1
|
|
11
11
|
displayname: __DISPLAY_NAME__
|
|
12
12
|
description: A native DSM package with a Go backend and a DSM window UI.
|
|
13
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
14
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
15
|
+
# icon: assets/icon.png
|
|
13
16
|
maintainer: Your Name
|
|
14
17
|
# Optional INFO/website metadata. Omit fields you do not need.
|
|
15
18
|
# metadata:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Syno Package Land Developer Kit CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
+
"assets",
|
|
14
15
|
"CHANGELOG.md",
|
|
15
16
|
"templates",
|
|
16
17
|
"schema",
|
|
@@ -43,6 +43,28 @@
|
|
|
43
43
|
"type": "string",
|
|
44
44
|
"minLength": 1
|
|
45
45
|
},
|
|
46
|
+
"icon": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"minLength": 1,
|
|
49
|
+
"description": "Package icon inside the project: a square PNG of at least 256x256, or a directory of icon_<size>.png (16, 24, 32, 48, 64, 72, 256; icon_256.png required). Omit to use the SDK default icon."
|
|
50
|
+
},
|
|
51
|
+
"i18n": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"description": "Translations; only info is allowed.",
|
|
54
|
+
"properties": {
|
|
55
|
+
"info": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"description": "Package Center INFO and DSM main menu and window title per locale (zh-TW, zh-CN, ja, ko, de, fr, es, it); English stays top level.",
|
|
58
|
+
"additionalProperties": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"displayname": { "type": "string", "minLength": 1 },
|
|
62
|
+
"description": { "type": "string", "minLength": 1 }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
46
68
|
"adminprotocol": {
|
|
47
69
|
"type": "string"
|
|
48
70
|
},
|
|
@@ -5,6 +5,9 @@ version: 0.1.0
|
|
|
5
5
|
build: 1
|
|
6
6
|
displayname: __DISPLAY_NAME__
|
|
7
7
|
description: A DSM package with a prebuilt binary CGI and DSM window UI.
|
|
8
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
9
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
10
|
+
# icon: assets/icon.png
|
|
8
11
|
maintainer: Your Name
|
|
9
12
|
# Optional INFO/website metadata. Omit fields you do not need.
|
|
10
13
|
# metadata:
|
|
@@ -3,6 +3,9 @@ package: com.synology.hello.files
|
|
|
3
3
|
version: 0.1.0
|
|
4
4
|
displayname: Hello Files
|
|
5
5
|
description: A minimal headless app with a Shared Folder mount.
|
|
6
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
7
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
8
|
+
# icon: assets/icon.png
|
|
6
9
|
maintainer: Syno Package Land
|
|
7
10
|
os_min_ver: "7.2.1-69057"
|
|
8
11
|
|
|
@@ -10,6 +10,9 @@ version: 0.1.0
|
|
|
10
10
|
build: 1
|
|
11
11
|
displayname: __DISPLAY_NAME__
|
|
12
12
|
description: A native DSM package with a Go backend and a DSM window UI.
|
|
13
|
+
# Package Center and DSM window icon: a square PNG of at least 256x256, or a
|
|
14
|
+
# folder of icon_<size>.png. Omit to use the SDK default icon.
|
|
15
|
+
# icon: assets/icon.png
|
|
13
16
|
maintainer: Your Name
|
|
14
17
|
# Optional INFO/website metadata. Omit fields you do not need.
|
|
15
18
|
# metadata:
|