@synopackageland/cli 0.9.7 → 0.10.1

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +5 -0
  3. package/dist/add.js +7 -8
  4. package/dist/build-spk/dsm-arch.js +1 -1
  5. package/dist/build-spk/dsm-icons.d.ts +8 -1
  6. package/dist/build-spk/dsm-icons.js +26 -12
  7. package/dist/build-spk/dsm-version.d.ts +1 -1
  8. package/dist/build-spk/dsm-version.js +4 -4
  9. package/dist/build-spk/gateway.js +2 -2
  10. package/dist/build-spk/generator.js +1 -1
  11. package/dist/build-spk/png.js +16 -16
  12. package/dist/capabilities.js +1 -1
  13. package/dist/capability-overlays.d.ts +16 -5
  14. package/dist/capability-overlays.js +30 -16
  15. package/dist/cli.js +107 -107
  16. package/dist/compose/analyze.js +23 -23
  17. package/dist/compose/privilege.js +12 -12
  18. package/dist/contract/i18n.js +3 -3
  19. package/dist/contract/icon.js +2 -2
  20. package/dist/contract/posix-all-shares.js +2 -2
  21. package/dist/contract/schema-validate.js +3 -3
  22. package/dist/contract/validate.js +16 -16
  23. package/dist/contract/web-launch.d.ts +2 -1
  24. package/dist/contract/web-launch.js +3 -2
  25. package/dist/deploy/deploy.js +8 -8
  26. package/dist/deploy/homes-values.js +1 -1
  27. package/dist/deploy/identity-shared.js +1 -1
  28. package/dist/deploy/native-install-values.js +1 -1
  29. package/dist/deploy/param-values.js +4 -4
  30. package/dist/deploy/plan.d.ts +1 -1
  31. package/dist/deploy/port-preflight.js +3 -3
  32. package/dist/deploy/run-as-values.js +2 -2
  33. package/dist/deploy/share-permission-preflight.js +6 -6
  34. package/dist/deploy/share-values.js +3 -3
  35. package/dist/error-registry.js +369 -346
  36. package/dist/errors.js +1 -1
  37. package/dist/host/dsm-port-profile.d.ts +1 -1
  38. package/dist/host/dsm-port-profile.js +1 -1
  39. package/dist/info.js +11 -11
  40. package/dist/init.d.ts +2 -0
  41. package/dist/init.js +85 -38
  42. package/dist/inspect.js +5 -5
  43. package/dist/portal/alias.d.ts +1 -1
  44. package/dist/portal/alias.js +4 -4
  45. package/dist/runtime/executor-build.js +12 -1
  46. package/dist/runtime/executor-digests.json +14 -91
  47. package/dist/runtime/executor-inspect.js +11 -4
  48. package/dist/runtime/executor-policy.d.ts +9 -2
  49. package/dist/runtime/executor-policy.js +27 -8
  50. package/dist/runtime/root-programs.d.ts +47 -0
  51. package/dist/runtime/root-programs.js +153 -0
  52. package/dist/runtime/sdk-source.d.ts +5 -4
  53. package/dist/runtime/sdk-source.js +8 -4
  54. package/dist/skill.js +14 -14
  55. package/dist/spk/check-info.js +17 -17
  56. package/dist/spk/check-metadata.js +18 -18
  57. package/dist/spk/check-payload.js +10 -10
  58. package/dist/spk/rules.d.ts +57 -57
  59. package/dist/spk/rules.js +50 -50
  60. package/dist/spk/validate-spk.js +5 -5
  61. package/dist/standalone/api-permissions.js +1 -1
  62. package/dist/standalone/background.d.ts +3 -1
  63. package/dist/standalone/background.js +8 -4
  64. package/dist/standalone/binary.js +7 -7
  65. package/dist/standalone/bridge-app.d.ts +23 -0
  66. package/dist/standalone/bridge-app.js +141 -0
  67. package/dist/standalone/bridge-backend.d.ts +40 -0
  68. package/dist/standalone/bridge-backend.js +95 -0
  69. package/dist/standalone/bridge-build.d.ts +21 -0
  70. package/dist/standalone/bridge-build.js +62 -0
  71. package/dist/standalone/bridge-json.d.ts +16 -0
  72. package/dist/standalone/bridge-json.js +59 -0
  73. package/dist/standalone/bridge-runtime.d.ts +16 -0
  74. package/dist/standalone/bridge-runtime.js +46 -0
  75. package/dist/standalone/bridge-rust.d.ts +16 -0
  76. package/dist/standalone/bridge-rust.js +89 -0
  77. package/dist/standalone/build.js +18 -14
  78. package/dist/standalone/daemon.js +6 -6
  79. package/dist/standalone/dsm-window.d.ts +10 -5
  80. package/dist/standalone/dsm-window.js +69 -18
  81. package/dist/standalone/i18n.js +18 -18
  82. package/dist/standalone/inspect-bridge.d.ts +31 -0
  83. package/dist/standalone/inspect-bridge.js +111 -0
  84. package/dist/standalone/inspect-spk.d.ts +1 -1
  85. package/dist/standalone/inspect-spk.js +77 -69
  86. package/dist/standalone/lifecycle.js +22 -3
  87. package/dist/standalone/manifest.d.ts +17 -9
  88. package/dist/standalone/manifest.js +143 -65
  89. package/dist/standalone/portal-window.js +14 -7
  90. package/dist/standalone/request-timeout.d.ts +9 -0
  91. package/dist/standalone/request-timeout.js +13 -0
  92. package/dist/standalone/root-ops.d.ts +23 -2
  93. package/dist/standalone/root-ops.js +118 -13
  94. package/dist/standalone/sdk-check.js +4 -4
  95. package/dist/standalone/shared-folder.js +7 -7
  96. package/dist/standalone/spk.d.ts +2 -0
  97. package/dist/standalone/spk.js +30 -11
  98. package/dist/standalone/uninstall-hook.js +22 -4
  99. package/dist/standalone/uninstall.js +7 -7
  100. package/dist/standalone/window-size.d.ts +17 -0
  101. package/dist/standalone/window-size.js +31 -0
  102. package/dist/types.d.ts +1 -1
  103. package/dist/validate.js +13 -13
  104. package/package.json +2 -2
  105. package/schema/bridge-manifest.v1.schema.json +86 -0
  106. package/skills/reference/create.md +76 -2
  107. package/skills/reference/ui-json.md +2 -1
  108. package/skills/reference/ui-vue.md +9 -3
  109. package/skills/reference/wrap.md +1 -1
  110. package/dist/standalone/rust-build.d.ts +0 -35
  111. package/dist/standalone/rust-build.js +0 -127
@@ -1,762 +1,785 @@
1
1
  export const ERROR_REGISTRY = {
2
2
  "NATIVE_LAYOUT_INVALID": {
3
3
  code: "NATIVE_LAYOUT_INVALID",
4
- description: "native layout 的 manifest、hook、toolkit 或產物違反 package-only 契約。",
5
- suggestion: "使用 schema 2 與 backend.kind: native;檢查路徑、ELF 連結與 checksum pin。"
4
+ description: "The native layout's manifest, hooks, toolkit or artifacts violate the package-only contract.",
5
+ suggestion: "Use schema 2 with backend.kind: native; check paths, ELF linkage and checksum pins."
6
6
  },
7
7
  "COMPOSE_ROUTE_UNSUPPORTED": {
8
8
  "code": "COMPOSE_ROUTE_UNSUPPORTED",
9
- "description": "Compose 沒有可用的 DSM resource worker 執行路徑。",
10
- "suggestion": "使用 prebuilt image 與支援的安裝參數;移除不支援的特權、secret 或 build 設定。"
9
+ "description": "Compose has no supported DSM resource worker execution path.",
10
+ "suggestion": "Use a prebuilt image and supported install parameters; remove unsupported privileges, secrets or build settings."
11
11
  },
12
12
  "LOCAL_SPK_INSTALL_FAILED": {
13
13
  "code": "LOCAL_SPK_INSTALL_FAILED",
14
- "description": "DSM 本機 SPK 安裝失敗。",
15
- "suggestion": "查看 DSM Package Center 安裝錯誤,確認套件版本及需求。"
14
+ "description": "The local SPK install in DSM failed.",
15
+ "suggestion": "Check the DSM Package Center install error and confirm the package version and requirements."
16
16
  },
17
17
  "CONTRACT_SCHEMA_UNSUPPORTED": {
18
18
  "code": "CONTRACT_SCHEMA_UNSUPPORTED",
19
- "description": "synology-app.yaml 的 schema 版本不受支援。",
20
- "suggestion": "請將 synology-app.yaml 的 schema 欄位設為 1。"
19
+ "description": "The schema version in synology-app.yaml is not supported.",
20
+ "suggestion": "Set the schema field in synology-app.yaml to 1."
21
21
  },
22
22
  "CONTRACT_MISSING_FIELD": {
23
23
  "code": "CONTRACT_MISSING_FIELD",
24
- "description": "synology-app.yaml 缺少必填欄位。",
25
- "suggestion": "請在 synology-app.yaml 補上缺少的必填欄位(例如 package, version, displayname 等)。"
24
+ "description": "synology-app.yaml is missing a required field.",
25
+ "suggestion": "Add the missing required fields to synology-app.yaml (for example package, version, displayname)."
26
26
  },
27
27
  "CONTRACT_SCHEMA_INVALID": {
28
28
  "code": "CONTRACT_SCHEMA_INVALID",
29
- "description": "synology-app.yaml 格式與 JSON Schema 規格不符。",
30
- "suggestion": "請修正 synology-app.yaml 的欄位格式以符合規範。"
29
+ "description": "synology-app.yaml does not match the JSON Schema.",
30
+ "suggestion": "Fix the field formats in synology-app.yaml so they match the schema."
31
31
  },
32
32
  "PACKAGE_IDENTITY_TOO_LONG": {
33
33
  "code": "PACKAGE_IDENTITY_TOO_LONG",
34
- "description": "Package Identity 超過長度上限(正式身分上限 28 字元,最終身分上限 32 字元)。",
35
- "suggestion": "請縮短 synology-app.yaml 中的 package 名稱(正式身分至多 28 字元,以保留 4 字元供 .dev 開發後綴使用)。"
34
+ "description": "The Package Identity exceeds the length limit (28 characters for a formal identity, 32 for the final identity).",
35
+ "suggestion": "Shorten the package name in synology-app.yaml (at most 28 characters for the formal identity, leaving 4 characters for the .dev suffix)."
36
36
  },
37
37
  "CONTRACT_PACKAGE_TOO_LONG": {
38
38
  "code": "CONTRACT_PACKAGE_TOO_LONG",
39
- "description": "Package Identity 超過 DSM 上限 32 字元。",
40
- "suggestion": "請縮短 synology-app.yaml 中的 package 名稱至 32 字元以內。"
39
+ "description": "The Package Identity exceeds the DSM limit of 32 characters.",
40
+ "suggestion": "Shorten the package name in synology-app.yaml to 32 characters or fewer."
41
41
  },
42
42
  "CONTRACT_ADMINPORT_INVALID": {
43
43
  "code": "CONTRACT_ADMINPORT_INVALID",
44
- "description": "adminport 必須是整數。",
45
- "suggestion": "請在 synology-app.yaml 提供與 Compose published port 對應的整數 adminport。"
44
+ "description": "adminport must be an integer.",
45
+ "suggestion": "Provide an integer adminport in synology-app.yaml that matches the Compose published port."
46
46
  },
47
47
  "CONTRACT_ADMINPORT_UNMAPPED": {
48
48
  "code": "CONTRACT_ADMINPORT_UNMAPPED",
49
- "description": "adminport 未在 Compose ports 宣告中對應到容器 port。",
50
- "suggestion": "請在 compose.yaml 的 ports 區塊宣告與 adminport 對應的連接埠映射(例如 \"8080:80\")。"
49
+ "description": "adminport does not map to a container port in the Compose ports declaration.",
50
+ "suggestion": "Declare a port mapping for adminport in the compose.yaml ports block (for example \"8080:80\")."
51
51
  },
52
52
  "CONTRACT_LAUNCH_INVALID": {
53
53
  "code": "CONTRACT_LAUNCH_INVALID",
54
- "description": "integration.web.launch 模式無效。",
55
- "suggestion": "請改為 dsm-window(DSM 視窗,預設)或 new-page(新分頁),Headless App 請省略此欄位。"
54
+ "description": "The integration.web.launch mode is invalid.",
55
+ "suggestion": "Use dsm-window (DSM window, the default) or new-page (new tab); omit this field for a headless App."
56
56
  },
57
57
  "CONTRACT_LAUNCH_REQUIRES_WEB": {
58
58
  "code": "CONTRACT_LAUNCH_REQUIRES_WEB",
59
- "description": "Headless App 不需要宣告 Web Launch Mode。",
60
- "suggestion": "請省略 integration.web.launch,或補上對應 Compose published port 的 adminport。"
59
+ "description": "A headless App does not declare a Web Launch Mode.",
60
+ "suggestion": "Remove integration.web.launch, or add the adminport that matches the Compose published port."
61
61
  },
62
62
  "CONTRACT_PORTAL_REQUIRES_EXECUTOR": {
63
63
  "code": "CONTRACT_PORTAL_REQUIRES_EXECUTOR",
64
- "description": "Compose App 宣告了 integration.web.portal;Compose App 沒有 executor,登入入口別名在解除安裝後要 root 才能讓 nginx 停止提供。",
65
- "suggestion": "請移除 integration.web.portal;需要登入入口別名時,由管理員在 DSM 手動設定反向代理。"
64
+ "description": "The Compose App declares integration.web.portal, but a Compose App has no executor; removing the sign-in alias after uninstall needs root so nginx stops serving it.",
65
+ "suggestion": "Remove integration.web.portal; for a sign-in alias, ask an administrator to configure a reverse proxy in DSM instead."
66
66
  },
67
67
  "CONTRACT_ACCESS_INVALID": {
68
68
  "code": "CONTRACT_ACCESS_INVALID",
69
- "description": "integration.web.access 模式無效。",
70
- "suggestion": "請改為 dsm-users(已登入的 DSM 使用者,預設)、dsm-admins、dsm-groups(需搭配 groups),或明確選 public 以保留匿名 Web Station 入口。"
69
+ "description": "The integration.web.access mode is invalid.",
70
+ "suggestion": "Use dsm-users (signed-in DSM users, the default), dsm-admins, dsm-groups (with groups), or public to keep an anonymous Web Station entry."
71
71
  },
72
72
  "CONTRACT_ACCESS_GROUPS_INVALID": {
73
73
  "code": "CONTRACT_ACCESS_GROUPS_INVALID",
74
- "description": "integration.web.groups 缺少、格式不符,或沒有搭配 access: dsm-groups。",
75
- "suggestion": "請改用 access: dsm-groups 並列出 DSM 群組名稱,或移除 groups。"
74
+ "description": "integration.web.groups is missing, malformed, or not combined with access: dsm-groups.",
75
+ "suggestion": "Use access: dsm-groups and list DSM group names, or remove groups."
76
76
  },
77
77
  "CONTRACT_ACCESS_REQUIRES_WEB": {
78
78
  "code": "CONTRACT_ACCESS_REQUIRES_WEB",
79
- "description": "Headless App 不需要宣告 Web access 規則。",
80
- "suggestion": "請省略 integration.web.access/groups,或補上對應 Compose published port 的 adminport。"
79
+ "description": "A headless App does not declare Web access rules.",
80
+ "suggestion": "Remove integration.web.access/groups, or add the adminport that matches the Compose published port."
81
81
  },
82
82
  "COMPOSE_GATEWAY_PORT_EXPOSED": {
83
83
  "code": "COMPOSE_GATEWAY_PORT_EXPOSED",
84
- "description": "DSM 驗證閘道是容器的唯一入口,但 adminport 對應的 Compose port 綁在所有網路介面上。",
85
- "suggestion": "請把 ports 改成只綁 loopback(例如 \"127.0.0.1:18333:18333\"),或在 integration.web.access 明確選 public。"
84
+ "description": "The DSM verification gateway is the container's only entry point, but the Compose port behind adminport binds to every network interface.",
85
+ "suggestion": "Bind ports to loopback only (for example \"127.0.0.1:18333:18333\"), or choose public explicitly in integration.web.access."
86
86
  },
87
87
  "GATEWAY_ARCH_FAMILY_REQUIRED": {
88
88
  "code": "GATEWAY_ARCH_FAMILY_REQUIRED",
89
- "description": "DSM 驗證閘道是原生執行檔,SPK 必須指定目標架構。",
90
- "suggestion": "請加上 --arch x86_64 或 --arch aarch64(deploy 會由 NAS 判定);不需要 DSM 驗證的 App 才可在 integration.web.access 選 public。"
89
+ "description": "The DSM verification gateway is a native binary, so the SPK must name its target architecture.",
90
+ "suggestion": "Add --arch x86_64 or --arch aarch64 (deploy detects it from the NAS); only Apps without DSM verification may choose public in integration.web.access."
91
91
  },
92
92
  "GATEWAY_GO_MISSING": {
93
93
  "code": "GATEWAY_GO_MISSING",
94
- "description": "建置 DSM 驗證閘道需要 Go,但找不到 go 指令。",
95
- "suggestion": "請安裝 Go 1.21 以上版本(https://go.dev/dl/,建置時固定使用 go1.26.9),或在 integration.web.access 明確選 public。"
94
+ "description": "Building the DSM verification gateway needs Go, but the go command was not found.",
95
+ "suggestion": "Install Go 1.21 or later (https://go.dev/dl/, builds pin go1.26.9), or choose public explicitly in integration.web.access."
96
96
  },
97
97
  "GATEWAY_BUILD_FAILED": {
98
98
  "code": "GATEWAY_BUILD_FAILED",
99
- "description": "DSM 驗證閘道建置失敗或產出不是對應架構的靜態 ELF。",
100
- "suggestion": "請查看 go build 輸出;閘道固定以 go1.26.9 建置,離線時請安裝 go1.26.9 或讓 GOPROXY 可提供 golang.org/toolchain,並確認 CLI 套件的 assets/go/gateway 未被修改。"
99
+ "description": "The DSM verification gateway failed to build, or the output is not a static ELF for the target architecture.",
100
+ "suggestion": "Check the go build output; the gateway always builds with go1.26.9, so install go1.26.9 offline or let GOPROXY serve golang.org/toolchain, and confirm assets/go/gateway in the CLI package is unmodified."
101
101
  },
102
102
  "CONTRACT_INVALID_STRING": {
103
103
  "code": "CONTRACT_INVALID_STRING",
104
- "description": "合約字串含有不允許的控制字元或未允許的插值語法。",
105
- "suggestion": "請移除控制字元與 ${ 插值語法,安裝參數請在 install 區塊宣告。"
104
+ "description": "A contract string contains a control character or the ${ interpolation syntax, which is not allowed.",
105
+ "suggestion": "Remove control characters and ${ interpolation; declare install parameters in the install block."
106
106
  },
107
107
  "CONTRACT_DSM_USER_DUPLICATE": {
108
108
  "code": "CONTRACT_DSM_USER_DUPLICATE",
109
- "description": "每顆 App 至多宣告一個 dsm_user 安裝參數。",
110
- "suggestion": "請在 synology-app.yaml 只保留一個 Container Run-as 參數。"
109
+ "description": "Each App may declare at most one dsm_user install parameter.",
110
+ "suggestion": "Keep only one Container Run-as parameter in synology-app.yaml."
111
111
  },
112
112
  "INSTALL_STRING_TOO_LONG": {
113
113
  "code": "INSTALL_STRING_TOO_LONG",
114
- "description": "Install Parameter 字串預設值超過 256 字元。",
115
- "suggestion": "請縮短 synology-app.yaml 中 install 參數的預設字串長度。"
114
+ "description": "An Install Parameter string default exceeds 256 characters.",
115
+ "suggestion": "Shorten the default string of the install parameter in synology-app.yaml."
116
116
  },
117
117
  "INSTALL_STRING_INVALID": {
118
118
  "code": "INSTALL_STRING_INVALID",
119
- "description": "Install Parameter 字串值含有不合法字元或插值。",
120
- "suggestion": "請移除 install 參數預設值中的控制字元與 ${ 插值語法。"
119
+ "description": "An Install Parameter string value contains an invalid character or interpolation.",
120
+ "suggestion": "Remove control characters and ${ interpolation from the install parameter default."
121
121
  },
122
122
  "CONTRACT_SECRET_UNSUPPORTED": {
123
123
  "code": "CONTRACT_SECRET_UNSUPPORTED",
124
- "description": "install.<NAME>.type: secret 目前沒有可用的安裝路徑:無論是否在 Compose 引用,Package Center 安裝都以 DSM broken/150 結束,且不建立 Container Manager project。",
125
- "suggestion": "請改用 type: string(非敏感值),或把敏感值改成 App 啟動後自行取得/由管理員放進 ${SYNO_APP_SECRETS_DIR} 的檔案。在 DSM worker 的失敗原因可觀測之前,secret 型別不會開放。"
124
+ "description": "install.<NAME>.type: secret has no usable install path today: whether or not Compose references it, Package Center install ends in DSM broken/150 and creates no Container Manager project.",
125
+ "suggestion": "Use type: string (non-sensitive), or have the App fetch the secret itself at startup or read a file an administrator places in ${SYNO_APP_SECRETS_DIR}. The secret type stays closed until the DSM worker failure reason is observable."
126
126
  },
127
127
  "CONTRACT_HOMES_UNSUPPORTED": {
128
128
  "code": "CONTRACT_HOMES_UNSUPPORTED",
129
- "description": "install.<NAME>.type: homes 目前沒有可用的安裝路徑:安裝精靈不產生輸入控制項,Package Center 安裝以 DSM broken/150 結束;CLI deploy 能解析出 /volume1/homes,但 DSM 於 compound install 回 4540 拒絕。",
130
- "suggestion": "若 App 需要一個由管理員選定的資料夾,請改用 type: shared_folder。在補齊安裝精靈控制項與 preinst 路徑解析之前,homes 型別不會開放。"
129
+ "description": "install.<NAME>.type: homes has no usable install path today: the install wizard generates no input control and Package Center install ends in DSM broken/150; CLI deploy can resolve /volume1/homes, but DSM rejects the compound install with 4540.",
130
+ "suggestion": "If the App needs an administrator-chosen folder, use type: shared_folder instead. The homes type stays closed until the wizard control and preinst path resolution exist."
131
131
  },
132
132
  "INSTALL_PARAMETER_UNREFERENCED": {
133
133
  "code": "INSTALL_PARAMETER_UNREFERENCED",
134
- "description": "install 宣告的參數未在 Compose 中被引用。",
135
- "suggestion": "請在 compose.yaml 以 ${參數名稱} 引用該參數,或從 synology-app.yaml 移除未使用的宣告。"
134
+ "description": "An install parameter is declared but not referenced in Compose.",
135
+ "suggestion": "Reference the parameter in compose.yaml as ${NAME}, or remove the unused declaration from synology-app.yaml."
136
136
  },
137
137
  "CONTRACT_HOMES_PATH_FORBIDDEN": {
138
138
  "code": "CONTRACT_HOMES_PATH_FORBIDDEN",
139
- "description": "homes Install Parameter 不得在契約中寫入 NAS 實體路徑或預設值。",
140
- "suggestion": "請只宣告 type: homes 並在 Compose 引用該參數,由套件 在安裝時解析這台 NAS 的 User Home 路徑。"
139
+ "description": "A homes Install Parameter must not write a NAS physical path or default value into the contract.",
140
+ "suggestion": "Declare only type: homes, reference the parameter in Compose, and let the package resolve this NAS's User Home path at install time."
141
141
  },
142
142
  "CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN": {
143
143
  "code": "CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN",
144
- "description": "POSIX all-shares 參數不得在契約中指定 default 或 NAS 實體路徑。",
145
- "suggestion": "請只宣告 type: posix_all_shares,讓 wizard/CLI 在安裝時取得當下的 Share snapshot。"
144
+ "description": "A POSIX all-shares parameter must not specify a default or NAS physical path in the contract.",
145
+ "suggestion": "Declare only type: posix_all_shares and let the wizard/CLI take the current Share snapshot at install time."
146
146
  },
147
147
  "CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED": {
148
148
  "code": "CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED",
149
- "description": "posix_all_shares 需要 CLI 在安裝時以管理員 session 取得這台 NAS 的 Share 清單快照;Package Center 與 Package Source listing 無法履行此合約。",
150
- "suggestion": "請使用 synopkgland deploy --host <name>,由管理員 session 在安裝時取得 Share 清單快照;此型別不能透過 Package Center 或 Package Source listing 上架。"
149
+ "description": "posix_all_shares requires the CLI to take a Share list snapshot of this NAS with an administrator session at install time; Package Center and Package Source listing cannot fulfill this contract.",
150
+ "suggestion": "Use synopkgland deploy --host <name> so an administrator session takes the Share list snapshot at install time; this type cannot ship through Package Center or Package Source listing."
151
151
  },
152
152
  "DSM_USER_REQUIRED": {
153
153
  "code": "DSM_USER_REQUIRED",
154
- "description": "有 Shared Folder 時必須宣告恰好一個 dsm_user 作為 Container Run-as。",
155
- "suggestion": "請在 synology-app.yaml 新增 install.<NAME>.type: dsm_user,並在 Compose 引用 ${NAME_UID}:${NAME_GID}。"
154
+ "description": "A Shared Folder requires exactly one dsm_user as the Container Run-as.",
155
+ "suggestion": "Add install.<NAME>.type: dsm_user to synology-app.yaml and reference ${NAME_UID}:${NAME_GID} in Compose."
156
156
  },
157
157
  "PROJECT_RUNTIME_EXCLUSIVE": {
158
158
  "code": "PROJECT_RUNTIME_EXCLUSIVE",
159
- "description": "專案同時存在 compose.yaml 與 dsm-package.yaml,兩者互斥。",
160
- "suggestion": "請選擇使用 compose.yaml(容器 App)或 dsm-package.yaml(原生 App),並刪除另一個檔案。"
159
+ "description": "The project has both compose.yaml and dsm-package.yaml, which are mutually exclusive.",
160
+ "suggestion": "Choose compose.yaml (container App) or dsm-package.yaml (native App) and delete the other file."
161
161
  },
162
162
  "COMPOSE_MISSING": {
163
163
  "code": "COMPOSE_MISSING",
164
- "description": "找不到 compose.yaml 或 dsm-package.yaml。",
165
- "suggestion": "請在專案根目錄建立 compose.yaml 或 dsm-package.yaml。"
164
+ "description": "compose.yaml or dsm-package.yaml was not found.",
165
+ "suggestion": "Create compose.yaml or dsm-package.yaml in the project root."
166
166
  },
167
167
  "COMPOSE_SERVICE_NOT_FOUND": {
168
168
  "code": "COMPOSE_SERVICE_NOT_FOUND",
169
- "description": "depends_on 引用的服務不存在於 compose.yaml 中。",
170
- "suggestion": "請修正 compose.yaml 中的 depends_on,指向正確已定義的 service 名稱。"
169
+ "description": "A service referenced by depends_on does not exist in compose.yaml.",
170
+ "suggestion": "Fix depends_on in compose.yaml to point at a defined service name."
171
171
  },
172
172
  "COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN": {
173
173
  "code": "COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN",
174
- "description": "會走 DSM Docker Project resource worker 的 Compose command/entrypoint 陣列含純數字字串,可能在 worker YAML round-trip 後變成整數而無法 acquire。",
175
- "suggestion": "請把整個命令包成單一字串,例如 command: [\"sh\", \"-c\", \"httpd -f -p 8080 -h /srv\"],或與 flag 併寫成 \"-p8080\"。"
174
+ "description": "A Compose command/entrypoint array that goes through the DSM Docker Project resource worker contains a pure numeric string, which a YAML round-trip may turn into an integer and fail to acquire.",
175
+ "suggestion": "Wrap the whole command in one string, for example command: [\"sh\", \"-c\", \"httpd -f -p 8080 -h /srv\"], or join it with the flag as \"-p8080\"."
176
176
  },
177
177
  "COMPOSE_DYNAMIC_ROUTE_UNSUPPORTED": {
178
178
  "code": "COMPOSE_DYNAMIC_ROUTE_UNSUPPORTED",
179
- "description": "Compose dynamic route 超出 D1/D2 白名單,或使用 DSM worker 不會載入的 secret/config 來源。",
180
- "suggestion": "請只使用 string/boolean wizard answer 的 ${NAME} interpolation;secret/config 因 DSM worker 不讀 docker-compose.override.yml 而拒收。"
179
+ "description": "A Compose dynamic route is outside the D1/D2 allowlist, or uses a secret/config source the DSM worker does not load.",
180
+ "suggestion": "Use only ${NAME} interpolation of string/boolean wizard answers; secret/config are rejected because the DSM worker does not read docker-compose.override.yml."
181
181
  },
182
182
  "COMPOSE_BUILD_FORBIDDEN": {
183
183
  "code": "COMPOSE_BUILD_FORBIDDEN",
184
- "description": "D0 Compose route 禁止在 NAS install-time 使用 build:;build capability 永久拒收。",
185
- "suggestion": "請移除 build:,改用已存在的 image(最好使用 image@sha256:... 固定 digest),再重新執行 check。"
184
+ "description": "The D0 Compose route forbids build: at NAS install time; the build capability is permanently rejected.",
185
+ "suggestion": "Remove build: and use an existing image (preferably pinned with image@sha256:...), then run check again."
186
186
  },
187
187
  "COMPOSE_SOCKET_MOUNT_FORBIDDEN": {
188
188
  "code": "COMPOSE_SOCKET_MOUNT_FORBIDDEN",
189
- "description": "嚴禁掛載 Docker Socket 或系統敏感 Socket。",
190
- "suggestion": "請從 compose.yaml 移除 docker.sock 掛載;如需存取檔案請改用 shared_folder 或 App 自己的檔案 API。"
189
+ "description": "Mounting the Docker socket or sensitive system sockets is forbidden.",
190
+ "suggestion": "Remove the docker.sock mount from compose.yaml; for file access use a shared_folder or the App's own file API."
191
191
  },
192
192
  "COMPOSE_BIND_SOURCE_FORBIDDEN": {
193
193
  "code": "COMPOSE_BIND_SOURCE_FORBIDDEN",
194
- "description": "掛載來源路徑不合法或屬於受保護的系統路徑。",
195
- "suggestion": "請使用受控的 ${SYNOPKG_PKGVAR}、${SYNO_APP_SECRETS_DIR} 或已宣告的 ${SHARED_FOLDER} 槽位。"
194
+ "description": "A mount source path is invalid or is a protected system path.",
195
+ "suggestion": "Use the controlled ${SYNOPKG_PKGVAR}, ${SYNO_APP_SECRETS_DIR}, or a declared ${SHARED_FOLDER} slot."
196
196
  },
197
197
  "COMPOSE_BIND_SOURCE_UNDECLARED": {
198
198
  "code": "COMPOSE_BIND_SOURCE_UNDECLARED",
199
- "description": "Volume 掛載使用了未宣告的 Shared Folder 變數。",
200
- "suggestion": "請先在 synology-app.yaml 的 install 區塊宣告該 shared_folder 參數。"
199
+ "description": "A volume mount uses an undeclared Shared Folder variable.",
200
+ "suggestion": "Declare the shared_folder parameter in the synology-app.yaml install block first."
201
201
  },
202
202
  "COMPOSE_BIND_SUFFIX_INVALID": {
203
203
  "code": "COMPOSE_BIND_SUFFIX_INVALID",
204
- "description": "Shared Folder 子路徑格式不合法(含有 .. 或控制字元)。",
205
- "suggestion": "請改用合法的相對子路徑,例如 ${MEDIA_SHARE}/data:/data。"
204
+ "description": "A Shared Folder subpath is invalid (it contains .. or a control character).",
205
+ "suggestion": "Use a valid relative subpath, for example ${MEDIA_SHARE}/data:/data."
206
206
  },
207
207
  "COMPOSE_POSIX_ALL_SHARES_UNDECLARED": {
208
208
  "code": "COMPOSE_POSIX_ALL_SHARES_UNDECLARED",
209
- "description": "Compose 使用了未宣告的 POSIX all-shares marker。",
210
- "suggestion": "請宣告 type: posix_all_shares,讓 package-only wizard/CLI 取得安裝當下的 Share snapshot。"
209
+ "description": "Compose uses an undeclared POSIX all-shares marker.",
210
+ "suggestion": "Declare type: posix_all_shares so the package-only wizard/CLI takes the Share snapshot at install time."
211
211
  },
212
212
  "COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN": {
213
213
  "code": "COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN",
214
- "description": "POSIX all-shares marker 不得附加 Share 子路徑。",
215
- "suggestion": "請只使用完整的 ${ALL_SHARES} source;每個 Share 的 container target 由 package-only preinst 展開。"
214
+ "description": "A POSIX all-shares marker must not carry a Share subpath.",
215
+ "suggestion": "Use the complete ${ALL_SHARES} source only; the package-only preinst expands each Share's container target."
216
216
  },
217
217
  "COMPOSE_HOST_BIND_FORBIDDEN": {
218
218
  "code": "COMPOSE_HOST_BIND_FORBIDDEN",
219
- "description": "禁止直接硬編碼本機路徑(例如 /volume1/... 或 ./)。",
220
- "suggestion": "請在 synology-app.yaml 宣告 shared_folder 參數,並在 Compose 改用 ${SLOT} 引用。"
219
+ "description": "Hard-coded host paths (for example /volume1/... or ./) are forbidden.",
220
+ "suggestion": "Declare a shared_folder parameter in synology-app.yaml and reference it as ${SLOT} in Compose."
221
221
  },
222
222
  "COMPOSE_NAMED_VOLUME_UNDECLARED": {
223
223
  "code": "COMPOSE_NAMED_VOLUME_UNDECLARED",
224
- "description": "服務使用了未在頂層 volumes 宣告的 Named Volume。",
225
- "suggestion": "請在 compose.yaml 頂層 volumes 區塊補上該 Volume 宣告,或改用受控目錄。"
224
+ "description": "A service uses a Named Volume that is not declared in the top-level volumes.",
225
+ "suggestion": "Add the volume declaration to the top-level volumes block in compose.yaml, or use a controlled directory."
226
226
  },
227
227
  "COMPOSE_USER_ROOT_FORBIDDEN": {
228
228
  "code": "COMPOSE_USER_ROOT_FORBIDDEN",
229
- "description": "容器以 root (0:0) 執行,違反最小權限原則。",
230
- "suggestion": "請在 synology-app.yaml 宣告 dsm_user 並在 Compose 使用 ${USER_UID}:${USER_GID}。"
229
+ "description": "The container runs as root (0:0), violating least privilege.",
230
+ "suggestion": "Declare dsm_user in synology-app.yaml and use ${USER_UID}:${USER_GID} in Compose."
231
231
  },
232
232
  "COMPOSE_USER_CONFLICTS_DSM_USER": {
233
233
  "code": "COMPOSE_USER_CONFLICTS_DSM_USER",
234
- "description": "Compose 服務自定義了 user 欄位,與 contract 宣告的 dsm_user 衝突。",
235
- "suggestion": "請移除 Compose 中的 user 欄位,交由 dsm_user 自動注入 ${NAME_UID}:${NAME_GID} 與 group_add。"
234
+ "description": "The Compose service defines its own user field, which conflicts with the dsm_user declared in the contract.",
235
+ "suggestion": "Remove the user field from Compose; dsm_user injects ${NAME_UID}:${NAME_GID} and group_add automatically."
236
236
  },
237
237
  "COMPOSE_USER_NUMERIC_FORBIDDEN": {
238
238
  "code": "COMPOSE_USER_NUMERIC_FORBIDDEN",
239
- "description": "禁止在 compose.yaml 硬編碼數字 uid:gid。",
240
- "suggestion": "請在 synology-app.yaml 宣告 dsm_user,並在 Compose 引用 ${NAME_UID}:${NAME_GID}。"
239
+ "description": "Hard-coding a numeric uid:gid in compose.yaml is forbidden.",
240
+ "suggestion": "Declare dsm_user in synology-app.yaml and reference ${NAME_UID}:${NAME_GID} in Compose."
241
241
  },
242
242
  "COMPOSE_PRIVILEGED_FORBIDDEN": {
243
243
  "code": "COMPOSE_PRIVILEGED_FORBIDDEN",
244
- "description": "使用了 privileged: true 但未在合約宣告。",
245
- "suggestion": "請在 synology-app.yaml 加上 privilege.privileged: true,或移除 privileged: true。"
244
+ "description": "privileged: true is used but not declared in the contract.",
245
+ "suggestion": "Add privilege.privileged: true to synology-app.yaml, or remove privileged: true."
246
246
  },
247
247
  "COMPOSE_HOST_NETWORK_FORBIDDEN": {
248
248
  "code": "COMPOSE_HOST_NETWORK_FORBIDDEN",
249
- "description": "使用了 host network 模式但未在合約宣告。",
250
- "suggestion": "請在 synology-app.yaml 加上 privilege.host_network: true,或改用受控網路模式。"
249
+ "description": "host network mode is used but not declared in the contract.",
250
+ "suggestion": "Add privilege.host_network: true to synology-app.yaml, or use a controlled network mode."
251
251
  },
252
252
  "COMPOSE_HOST_PID_FORBIDDEN": {
253
253
  "code": "COMPOSE_HOST_PID_FORBIDDEN",
254
- "description": "使用了 pid: host 但未在合約宣告。",
255
- "suggestion": "請在 synology-app.yaml 加上 privilege.host_pid: true,或移除 pid: host。"
254
+ "description": "pid: host is used but not declared in the contract.",
255
+ "suggestion": "Add privilege.host_pid: true to synology-app.yaml, or remove pid: host."
256
256
  },
257
257
  "COMPOSE_HOST_IPC_FORBIDDEN": {
258
258
  "code": "COMPOSE_HOST_IPC_FORBIDDEN",
259
- "description": "使用了 ipc: host 但未在合約宣告。",
260
- "suggestion": "請在 synology-app.yaml 加上 privilege.host_ipc: true,或移除 ipc: host。"
259
+ "description": "ipc: host is used but not declared in the contract.",
260
+ "suggestion": "Add privilege.host_ipc: true to synology-app.yaml, or remove ipc: host."
261
261
  },
262
262
  "COMPOSE_DEVICES_FORBIDDEN": {
263
263
  "code": "COMPOSE_DEVICES_FORBIDDEN",
264
- "description": "使用了 devices 裝置存取但未在合約宣告。",
265
- "suggestion": "請在 synology-app.yaml 加上 privilege.devices: true,或移除 devices 區塊。"
264
+ "description": "Device access is used but not declared in the contract.",
265
+ "suggestion": "Add privilege.devices: true to synology-app.yaml, or remove the devices block."
266
266
  },
267
267
  "COMPOSE_CAP_ADD_FORBIDDEN": {
268
268
  "code": "COMPOSE_CAP_ADD_FORBIDDEN",
269
- "description": "使用了 cap_add 權限提升但未在合約宣告。",
270
- "suggestion": "請在 synology-app.yaml 加上 privilege.cap_add: true,或移除 cap_add 區塊。"
269
+ "description": "cap_add privilege escalation is used but not declared in the contract.",
270
+ "suggestion": "Add privilege.cap_add: true to synology-app.yaml, or remove the cap_add block."
271
271
  },
272
272
  "COMPOSE_ENV_MISSING": {
273
273
  "code": "COMPOSE_ENV_MISSING",
274
- "description": "專案缺少必要的環境變數目錄結構。",
275
- "suggestion": "請確認 SYNOPKG_PKGVAR 與 secrets 目錄已正確建立。"
274
+ "description": "The project is missing the required environment variable directory structure.",
275
+ "suggestion": "Make sure SYNOPKG_PKGVAR and the secrets directory are created correctly."
276
276
  },
277
277
  "COMPOSE_NOT_HEALTHY": {
278
278
  "code": "COMPOSE_NOT_HEALTHY",
279
- "description": "Compose 服務未能於時限內通過 healthcheck 達到 healthy 狀態。",
280
- "suggestion": "請檢查容器健康檢查設定與應用程式日誌:synopkgland logs --host <nas>。"
279
+ "description": "The Compose service did not reach healthy within the time limit.",
280
+ "suggestion": "Check the container healthcheck and application logs: synopkgland logs --host <nas>."
281
281
  },
282
282
  "COMPOSE_NOT_RUNNING": {
283
283
  "code": "COMPOSE_NOT_RUNNING",
284
- "description": "Compose 服務未能於時限內維持 running 狀態。",
285
- "suggestion": "請檢查容器啟動腳本與日誌以排除崩潰原因:synopkgland logs --host <nas>。"
284
+ "description": "The Compose service did not stay running within the time limit.",
285
+ "suggestion": "Check the container start script and logs for the crash cause: synopkgland logs --host <nas>."
286
286
  },
287
287
  "BUILD_SPK_FAILED": {
288
288
  "code": "BUILD_SPK_FAILED",
289
- "description": "SPK 打包流程失敗。",
290
- "suggestion": "請檢查 check 錯誤與目錄檔案權限後重試。"
289
+ "description": "The SPK packaging flow failed.",
290
+ "suggestion": "Fix the check errors and file permissions, then retry."
291
291
  },
292
292
  "VALIDATION_FAILED": {
293
293
  "code": "VALIDATION_FAILED",
294
- "description": "專案未通過契約或安全驗證,終止部署。",
295
- "suggestion": "請先執行 synopkgland check 排除合約與 Compose 錯誤。"
294
+ "description": "The project failed contract or security validation; deploy stopped.",
295
+ "suggestion": "Run synopkgland check first to fix contract and Compose errors."
296
296
  },
297
297
  "DEPLOY_FAILED": {
298
298
  "code": "DEPLOY_FAILED",
299
- "description": "部署至 NAS 時發生未分類錯誤。",
300
- "suggestion": "請檢查 NAS 網路連線與日誌:synopkgland logs --host <nas>。"
299
+ "description": "An unclassified error occurred while deploying to the NAS.",
300
+ "suggestion": "Check NAS connectivity and logs: synopkgland logs --host <nas>."
301
301
  },
302
302
  "SHARE_VALUE_REQUIRED": {
303
303
  "code": "SHARE_VALUE_REQUIRED",
304
- "description": "部署時缺少合約中宣告的 shared_folder 實際路徑映射。",
305
- "suggestion": "請使用 --share <SLOT>=<邏輯名> 提供對應的 Shared Folder 名稱。"
304
+ "description": "Deploy is missing the physical path mapping for a shared_folder declared in the contract.",
305
+ "suggestion": "Provide the matching Shared Folder name with --share <SLOT>=<logical name>."
306
306
  },
307
307
  "SHARE_LOGICAL_NAME_REQUIRED": {
308
308
  "code": "SHARE_LOGICAL_NAME_REQUIRED",
309
- "description": "Share 參數必須提供邏輯名稱,禁止使用 /volume1/... 等實體路徑。",
310
- "suggestion": "請指定 DSM 控制台中的 Shared Folder 邏輯名稱,例如 --share MEDIA_SHARE=photos。"
309
+ "description": "A Share parameter needs a logical name; physical paths such as /volume1/... are forbidden.",
310
+ "suggestion": "Give the Shared Folder logical name from DSM Control Panel, for example --share MEDIA_SHARE=photos."
311
311
  },
312
312
  "SHARE_NOT_FOUND": {
313
313
  "code": "SHARE_NOT_FOUND",
314
- "description": "在目標 NAS 上找不到指定的 Shared Folder。",
315
- "suggestion": "請在 DSM 控制台建立該 Shared Folder,或檢查名稱拼字。"
314
+ "description": "The named Shared Folder was not found on the target NAS.",
315
+ "suggestion": "Create the Shared Folder in DSM Control Panel, or check the spelling."
316
316
  },
317
317
  "HOMES_NOT_ENABLED": {
318
318
  "code": "HOMES_NOT_ENABLED",
319
- "description": "目標 NAS 未啟用 User Home,或無法解析 homes 的實體 bind source。",
320
- "suggestion": "請在這台 NAS 啟用 DSM User Home,並確認 DSM API 能回傳解析後的路徑,再重新部署。"
319
+ "description": "The target NAS has User Home disabled, or the homes physical bind source cannot be resolved.",
320
+ "suggestion": "Enable DSM User Home on this NAS, confirm the DSM API returns the resolved path, then deploy again."
321
321
  },
322
322
  "DSM_USER_LOGICAL_NAME_REQUIRED": {
323
323
  "code": "DSM_USER_LOGICAL_NAME_REQUIRED",
324
- "description": "dsm_user 參數必須提供 DSM 使用者邏輯名稱,禁止使用數字 UID。",
325
- "suggestion": "請在 --run-as 指定 DSM 使用者帳號名稱(例如 --run-as RUN_AS=alice)。"
324
+ "description": "A dsm_user parameter needs a DSM user logical name; a numeric UID is forbidden.",
325
+ "suggestion": "Pass the DSM account name with --run-as (for example --run-as RUN_AS=alice)."
326
326
  },
327
327
  "SHARE_RUN_AS_NO_ACCESS": {
328
328
  "code": "SHARE_RUN_AS_NO_ACCESS",
329
- "description": "deploy 的安裝前檢查發現選定的 Container Run-as 使用者對選定的 Shared Folder 沒有權限(或被明確拒絕)。容器會以該使用者掛載該 Share,安裝會成功但 runtime 讀寫會拿到 Permission denied。",
330
- "suggestion": "請在 DSM 控制台把該 Shared Folder 的權限授予該使用者(或其群組),或改選別的使用者/Share,再重新部署。此檢查只在 deploy 進行;Package Center 安裝精靈不經過這裡,ACL 仍是管理員的前提責任。"
329
+ "description": "The deploy pre-install check found that the chosen Container Run-as user has no permission on the chosen Shared Folder (or is explicitly denied). The container mounts the Share as that user, so the install succeeds but runtime reads and writes get Permission denied.",
330
+ "suggestion": "Grant that user (or their group) permission on the Shared Folder in DSM Control Panel, or choose another user/Share, then deploy again. This check only runs in deploy; the Package Center install wizard does not go through it, and ACLs remain the administrator's responsibility."
331
331
  },
332
332
  "PORT_ALREADY_IN_USE": {
333
333
  "code": "PORT_ALREADY_IN_USE",
334
- "description": "deploy 的安裝前檢查發現 Compose published TCP port 已被其他服務或容器佔用。",
335
- "suggestion": "請更換 synology-app.yaml/compose.yaml 的 published host port;同一 Package Identity 的既有容器會在升級或重裝時放行。"
334
+ "description": "The deploy pre-install check found the Compose published TCP port already taken by another service or container.",
335
+ "suggestion": "Change the published host port in synology-app.yaml/compose.yaml; existing containers of the same Package Identity are allowed during upgrade or reinstall."
336
336
  },
337
337
  "CATALOG_INSTALL_FAILED": {
338
338
  "code": "CATALOG_INSTALL_FAILED",
339
- "description": "NAS Package Center 安裝套件失敗。",
340
- "suggestion": "請至 DSM Package Center 檢查安裝日誌或手動嘗試安裝。"
339
+ "description": "NAS Package Center failed to install the package.",
340
+ "suggestion": "Check the install log in DSM Package Center or try installing manually."
341
341
  },
342
342
  "DEPLOY_PARAM_UNDECLARED": {
343
343
  "code": "DEPLOY_PARAM_UNDECLARED",
344
- "description": "deploy --param 提供了契約未宣告的 Install Parameter,或 native-go/binary 套件沒有的安裝精靈值。",
345
- "suggestion": "Compose 請只傳 synology-app.yaml install 中宣告的 string 或 boolean 名稱;native-go/binary 只接受 install-selected delegate 的 delegate_share 或 delegate_home。"
344
+ "description": "deploy --param provided an Install Parameter the contract does not declare, or an install wizard value that a native-go/binary package does not have.",
345
+ "suggestion": "For Compose pass only string or boolean names declared in synology-app.yaml install; native-go/binary accept only delegate_share or delegate_home from the install-selected delegate."
346
346
  },
347
347
  "DEPLOY_PARAM_REQUIRED": {
348
348
  "code": "DEPLOY_PARAM_REQUIRED",
349
- "description": "首次安裝 install-selected delegate 套件時沒有提供安裝精靈的範圍值,preinst 會拒絕安裝。",
350
- "suggestion": "加上 --param delegate_share=<共用資料夾> 或 --param delegate_home=true 再部署。"
349
+ "description": "The first install of an install-selected delegate package did not provide the install wizard scope value, so preinst rejects the install.",
350
+ "suggestion": "Add --param delegate_share=<shared folder> or --param delegate_home=true and deploy again."
351
351
  },
352
352
  "DEPLOY_PARAM_TYPE_INVALID": {
353
353
  "code": "DEPLOY_PARAM_TYPE_INVALID",
354
- "description": "deploy --param 的值不符合契約宣告的 string 或 boolean 型別。",
355
- "suggestion": "string 請傳非空字串;boolean 請只傳 true 或 false。"
354
+ "description": "The deploy --param value does not match the string or boolean type declared in the contract.",
355
+ "suggestion": "Pass a non-empty string for string; pass only true or false for boolean."
356
356
  },
357
357
  "DEPLOY_POSIX_ALL_SHARES_LOOKUP_FAILED": {
358
358
  "code": "DEPLOY_POSIX_ALL_SHARES_LOOKUP_FAILED",
359
- "description": "無法以管理員 session 列出部署當下的 Shared Folder 清單。",
360
- "suggestion": "請確認 DSM 管理員 session 可呼叫 SYNO.Core.Share list,或用 --param NAME=... 明確提供清單。"
359
+ "description": "The Shared Folder list at deploy time could not be listed with an administrator session.",
360
+ "suggestion": "Confirm the DSM administrator session can call SYNO.Core.Share list, or provide the list explicitly with --param NAME=...."
361
361
  },
362
362
  "DSM_DEPENDENCY_CONFLICT": {
363
363
  "code": "DSM_DEPENDENCY_CONFLICT",
364
- "description": "DSM 複合安裝請求因執行中的依賴套件而拒絕。",
365
- "suggestion": "請先停止列出的依賴套件再重試升級。"
364
+ "description": "DSM rejected the compound install request because of running dependent packages.",
365
+ "suggestion": "Stop the listed dependent packages, then retry the upgrade."
366
366
  },
367
367
  "DSM_COMPOUND_FAILED": {
368
368
  "code": "DSM_COMPOUND_FAILED",
369
- "description": "DSM 複合安裝請求的子步驟失敗。",
370
- "suggestion": "請檢查回應中的 api、method、errorCode 與 DSM Package Center 日誌後重試。"
369
+ "description": "A substep of the DSM compound install request failed.",
370
+ "suggestion": "Check the api, method and errorCode in the response and the DSM Package Center log, then retry."
371
371
  },
372
372
  "DSM_REQUEST_FAILED": {
373
373
  "code": "DSM_REQUEST_FAILED",
374
- "description": "DSM SYNO.Entry.Request 複合請求失敗。",
375
- "suggestion": "請檢查 DSM session、請求參數與 Package Center 日誌後重試。"
374
+ "description": "The DSM SYNO.Entry.Request compound request failed.",
375
+ "suggestion": "Check the DSM session, request parameters and Package Center log, then retry."
376
376
  },
377
377
  "NOT_INSTALLED": {
378
378
  "code": "NOT_INSTALLED",
379
- "description": "套件尚未在目標 NAS 上安裝部署。",
380
- "suggestion": "請先執行 synopkgland deploy --host <nas> 安裝套件。"
379
+ "description": "The package is not installed on the target NAS yet.",
380
+ "suggestion": "Run synopkgland deploy --host <nas> to install the package first."
381
381
  },
382
382
  "STANDALONE_MANIFEST_MISSING": {
383
383
  "code": "STANDALONE_MANIFEST_MISSING",
384
- "description": "找不到 dsm-package.yaml。",
385
- "suggestion": "請在專案根目錄執行,或以 synopkgland init -t native-go 建立專案。"
384
+ "description": "dsm-package.yaml was not found.",
385
+ "suggestion": "Run from the project root, or create a project with synopkgland init -t native-go."
386
386
  },
387
387
  "STANDALONE_MANIFEST_YAML": {
388
388
  "code": "STANDALONE_MANIFEST_YAML",
389
- "description": "dsm-package.yaml 不是合法的 YAML 物件。",
390
- "suggestion": "請修正 dsm-package.yaml 的 YAML 語法。"
389
+ "description": "dsm-package.yaml is not a valid YAML object.",
390
+ "suggestion": "Fix the YAML syntax in dsm-package.yaml."
391
391
  },
392
392
  "STANDALONE_FIELD_UNKNOWN": {
393
393
  "code": "STANDALONE_FIELD_UNKNOWN",
394
- "description": "dsm-package.yaml 含有不支援的欄位。",
395
- "suggestion": "請移除該欄位;Standalone Package 不接受自訂 lifecycle scripts 或 DSM conf。"
394
+ "description": "dsm-package.yaml contains an unsupported field.",
395
+ "suggestion": "Remove the field; a Standalone Package does not accept custom lifecycle scripts or DSM conf."
396
396
  },
397
397
  "STANDALONE_SCHEMA_INVALID": {
398
398
  "code": "STANDALONE_SCHEMA_INVALID",
399
- "description": "dsm-package.yaml 的 schema 版本不受支援。",
400
- "suggestion": "請將 schema 設為 2,改用 backend.kind 與 capabilities。"
399
+ "description": "The schema version in dsm-package.yaml is not supported.",
400
+ "suggestion": "Set schema to 2 and use backend.kind and capabilities."
401
401
  },
402
402
  "STANDALONE_ROOT_OPS_MISSING": {
403
403
  "code": "STANDALONE_ROOT_OPS_MISSING",
404
- "description": "capabilities.rootOps: true 但沒有任何 root 操作。",
405
- "suggestion": "請在 root/ 放至少一個 <名稱>.sh,或改為 省略 capabilities.rootOps。"
404
+ "description": "capabilities.rootOps: true is set but there are no root operations.",
405
+ "suggestion": "Put at least one <name>.sh in root/, or drop capabilities.rootOps."
406
406
  },
407
407
  "STANDALONE_DAEMON_INVALID": {
408
408
  "code": "STANDALONE_DAEMON_INVALID",
409
- "description": "套件 daemon 的宣告不符合固定 argv、環境與生命週期契約。",
410
- "suggestion": "核對 daemon.exec、各架構 targets、args、env、uninstall_args 與整數秒 timeouts;移除 shell、user 與自訂 conf。"
409
+ "description": "The package daemon declaration does not match the fixed argv, environment and lifecycle contract.",
410
+ "suggestion": "Check daemon.exec, the per-architecture targets, args, env, uninstall_args and integer-second timeouts; remove shell, user and custom conf."
411
411
  },
412
412
  "UNINSTALL_INVALID": {
413
413
  "code": "UNINSTALL_INVALID",
414
- "description": "uninstall 的宣告不符合固定 exec、argv、環境與逾時契約。",
415
- "suggestion": "核對 uninstall.purge 的 exec(套件內、不在 executor/ 或 root/)、args、env(不可用 SYNOPKGLAND_*)與 2–120 秒 timeout,以及 state.items 的唯一 key。"
414
+ "description": "The uninstall declaration does not match the fixed exec, argv, environment and timeout contract.",
415
+ "suggestion": "Check uninstall.purge's exec (inside the package, not in executor/ or root/), args, env (SYNOPKGLAND_* not allowed) and 2-120 second timeout, and the unique keys of state.items."
416
416
  },
417
417
  "UNINSTALL_STATE_REQUIRES_PURGE": {
418
418
  "code": "UNINSTALL_STATE_REQUIRES_PURGE",
419
- "description": "uninstall.state 沒有可刪除狀態的 App 卸載 hook。",
420
- "suggestion": "加上 uninstall.purge,由 App 在 SYNOPKGLAND_DELETE_STATE=1 時刪除所列狀態;或移除 uninstall.state。"
419
+ "description": "uninstall.state has no App uninstall hook that could delete the state.",
420
+ "suggestion": "Add uninstall.purge so the App deletes the listed state when SYNOPKGLAND_DELETE_STATE=1, or remove uninstall.state."
421
421
  },
422
422
  "UNINSTALL_STATE_TEXT_MISSING": {
423
423
  "code": "UNINSTALL_STATE_TEXT_MISSING",
424
- "description": "卸載精靈無法列出 uninstall.state 項目的文字。",
425
- "suggestion": "在 i18n.messages 的 en.json(與其他語言)加入 uninstall.state.<key>。"
424
+ "description": "The uninstall wizard cannot list the text of the uninstall.state items.",
425
+ "suggestion": "Add uninstall.state.<key> to i18n.messages en.json (and the other languages)."
426
426
  },
427
427
  "UNINSTALL_ROOT_REQUIRES_ROOT_OPS": {
428
428
  "code": "UNINSTALL_ROOT_REQUIRES_ROOT_OPS",
429
- "description": "uninstall.purge.root 需要管理員核准的 root 操作。",
430
- "suggestion": "宣告 capabilities.rootOps: true 並提供 root/<名稱>.sh,或移除 uninstall.purge.root。"
429
+ "description": "uninstall.purge.root needs administrator-approved root operations.",
430
+ "suggestion": "Declare capabilities.rootOps: true and provide root/<name>.sh, or remove uninstall.purge.root."
431
431
  },
432
432
  "UNINSTALL_ROOT_OP_MISSING": {
433
433
  "code": "UNINSTALL_ROOT_OP_MISSING",
434
- "description": "uninstall.purge.root 指向不存在的 root 操作。",
435
- "suggestion": "新增 root/<名稱>.sh,或把 uninstall.purge.root 改成既有的 root 操作名稱。"
434
+ "description": "uninstall.purge.root points at a root operation that does not exist.",
435
+ "suggestion": "Add root/<name>.sh, or change uninstall.purge.root to an existing root operation name."
436
436
  },
437
437
  "UNINSTALL_EXEC_MISSING": {
438
438
  "code": "UNINSTALL_EXEC_MISSING",
439
- "description": "uninstall.purge.exec 不是建置後套件內可執行的一般檔案。",
440
- "suggestion": "指向套件內的執行檔,例如 ui/cgi/api.cgi、background/worker 或 daemon/payload/ 下具執行權限的檔案。"
439
+ "description": "uninstall.purge.exec is not a regular executable file inside the built package.",
440
+ "suggestion": "Point at an executable in the package, for example ui/cgi/api.cgi, background/worker, or an executable file under daemon/payload/."
441
441
  },
442
442
  "UNINSTALL_EXEC_UNSAFE": {
443
443
  "code": "UNINSTALL_EXEC_UNSAFE",
444
- "description": "uninstall.purge.exec 是嘗試取得 root 的 shell script。",
445
- "suggestion": "hook 一律以套件帳號執行;需要 root 的清理請放在 root/<名稱>.sh 並以 uninstall.purge.root 宣告。"
444
+ "description": "uninstall.purge.exec is a shell script that tries to gain root.",
445
+ "suggestion": "Hooks always run as the package account; put cleanup that needs root in root/<name>.sh and declare it with uninstall.purge.root."
446
446
  },
447
447
  "STANDALONE_DAEMON_BUILD_FAILED": {
448
448
  "code": "STANDALONE_DAEMON_BUILD_FAILED",
449
- "description": "無法建置套件 daemon 的 Go supervisor。",
450
- "suggestion": "安裝 Go 1.21 以上(建置固定使用 go1.26.9,離線時請先備妥)並檢查 compiler stderr;NAS 不需要 Go toolchain。"
449
+ "description": "The package daemon's Go supervisor could not be built.",
450
+ "suggestion": "Install Go 1.21 or later (builds pin go1.26.9 and need it offline) and check the compiler stderr; the NAS does not need a Go toolchain."
451
451
  },
452
452
  "STANDALONE_DAEMON_PAYLOAD_INVALID": {
453
453
  "code": "STANDALONE_DAEMON_PAYLOAD_INVALID",
454
- "description": "daemon payload 不是專案內的有界、無連結目錄。",
455
- "suggestion": "重建各架構 bundle;移除 symlink、特殊檔與 setid,並將大小限制在 512 MiB。"
454
+ "description": "The daemon payload is not a bounded, link-free directory inside the project.",
455
+ "suggestion": "Rebuild the per-architecture bundles; remove symlinks, special files and setid bits, and keep the size under 512 MiB."
456
456
  },
457
457
  "STANDALONE_DAEMON_ARTIFACT_REQUIRED": {
458
458
  "code": "STANDALONE_DAEMON_ARTIFACT_REQUIRED",
459
- "description": "組裝 daemon SPK 時缺少已建置的 supervisor。",
460
- "suggestion": "透過 synopkgland build 建置完整產物,不直接呼叫缺少 controller 的 assembler。"
459
+ "description": "The built supervisor is missing while assembling the daemon SPK.",
460
+ "suggestion": "Build the complete artifacts with synopkgland build; do not call the assembler directly without the controller."
461
461
  },
462
462
  "STANDALONE_SHARED_FOLDER_INVALID": {
463
463
  "code": "STANDALONE_SHARED_FOLDER_INVALID",
464
- "description": "dsm-package.yaml 的 install.sharedFolder 宣告不合法。",
465
- "suggestion": "寫 install: { sharedFolder: { mode: select|create, key: <小寫英數與底線>, permission: ro|rw } };create 需另加 name(DSM 共用資料夾名稱),select 不可有 name。"
464
+ "description": "The install.sharedFolder declaration in dsm-package.yaml is invalid.",
465
+ "suggestion": "Write install: { sharedFolder: { mode: select|create, key: <lowercase alphanumeric and underscore>, permission: ro|rw } }; create also needs name (the DSM shared folder name) and select must not have one."
466
466
  },
467
467
  "STANDALONE_BACKGROUND_INVALID": {
468
468
  "code": "STANDALONE_BACKGROUND_INVALID",
469
- "description": "套件背景工作的宣告不符合固定 worker 契約。",
470
- "suggestion": "只寫 capabilities.jobs: true 或 { worker: sha256-v1 },使用 backend.kind: go,且不要與 daemon 或 integration 並用;背景程序固定以套件帳號執行。"
469
+ "description": "The package background job declaration does not match the fixed worker contract.",
470
+ "suggestion": "Write only capabilities.jobs: true or { worker: sha256-v1 }, use backend.kind: go, and do not combine it with daemon or integration; background processes always run as the package account."
471
471
  },
472
472
  "STANDALONE_BACKGROUND_BUILD_FAILED": {
473
473
  "code": "STANDALONE_BACKGROUND_BUILD_FAILED",
474
- "description": "無法建置套件的背景 worker。",
475
- "suggestion": "安裝 Go 1.25 以上,確認 backend 含 cmd/background-worker(可用 synopkgland init -t native-go --with jobs 建立),並檢查 compiler stderr。"
474
+ "description": "The package background worker could not be built.",
475
+ "suggestion": "Install Go 1.25 or later, confirm the backend has cmd/background-worker (create it with synopkgland init -t native-go --with jobs), and check the compiler stderr."
476
476
  },
477
477
  "STANDALONE_BACKGROUND_ARTIFACT_REQUIRED": {
478
478
  "code": "STANDALONE_BACKGROUND_ARTIFACT_REQUIRED",
479
- "description": "組裝背景工作 SPK 時缺少已建置的 worker。",
480
- "suggestion": "透過 synopkgland build 建置完整產物,不直接呼叫缺少 worker 的 assembler。"
479
+ "description": "The built worker is missing while assembling the background job SPK.",
480
+ "suggestion": "Build the complete artifacts with synopkgland build; do not call the assembler directly without the worker."
481
481
  },
482
482
  "STANDALONE_ROOT_OP_INVALID": {
483
483
  "code": "STANDALONE_ROOT_OP_INVALID",
484
- "description": "root/ 內有不合法的 root 操作檔案。",
485
- "suggestion": "每個檔案必須是 root/<小寫名稱>.sh 一般檔案、以 #!/bin/sh 開頭、16 KiB 以內。"
484
+ "description": "root/ contains an invalid root operation file.",
485
+ "suggestion": "A script must be a regular root/<lowercase name>.sh starting with #!/bin/sh and under 16 KiB; a binary must be root/<arch>/<lowercase name>, an ELF for that architecture, under 64 MiB, and must not share a script's name."
486
+ },
487
+ "STANDALONE_ROOT_PROGRAMS_INVALID": {
488
+ "code": "STANDALONE_ROOT_PROGRAMS_INVALID",
489
+ "description": "The root settings in dsm-package.yaml are invalid, or the executor does not accept them.",
490
+ "suggestion": "root accepts only programs (<name>: {kind, schema, run, job} matching root/<name>.sh or root/<arch>/<name>), stop (a program in root/) and state: true, and requires capabilities.rootOps: true."
486
491
  },
487
492
  "STANDALONE_ROOT_OPS_UNUSED": {
488
493
  "code": "STANDALONE_ROOT_OPS_UNUSED",
489
- "description": "專案有 root/ 資料夾,但 capabilities.rootOps 未啟用。",
490
- "suggestion": "要使用 root 操作請設 capabilities.rootOps: true;不需要就刪除 root/。"
494
+ "description": "The project has a root/ directory but capabilities.rootOps is not enabled.",
495
+ "suggestion": "Set capabilities.rootOps: true to use root operations, or delete root/."
491
496
  },
492
497
  "STANDALONE_PACKAGE_INVALID": {
493
498
  "code": "STANDALONE_PACKAGE_INVALID",
494
- "description": "package 名稱不合法。",
495
- "suggestion": "請使用英文字母開頭、只含英數字的 2–64 字名稱,例如 PhotoTagger。"
499
+ "description": "The package name is invalid.",
500
+ "suggestion": "Use a 2-64 character name that starts with a letter and contains only alphanumerics, for example PhotoTagger."
496
501
  },
497
502
  "STANDALONE_VERSION_INVALID": {
498
503
  "code": "STANDALONE_VERSION_INVALID",
499
- "description": "version 不是數字版本。",
500
- "suggestion": "請使用 1.2.0 這類版本,build 號碼另外填在 build。"
504
+ "description": "version is not a numeric version.",
505
+ "suggestion": "Use a version such as 1.2.0 and put the build number in build."
501
506
  },
502
507
  "STANDALONE_BUILD_INVALID": {
503
508
  "code": "STANDALONE_BUILD_INVALID",
504
- "description": "build 不是 1–9999 的整數。",
505
- "suggestion": "請將 build 設為 1–9999 的整數,每次發出 SPK 時遞增。"
509
+ "description": "build is not an integer from 1 to 9999.",
510
+ "suggestion": "Set build to an integer from 1 to 9999 and increment it for each SPK release."
506
511
  },
507
512
  "STANDALONE_OS_MIN_VER_INVALID": {
508
513
  "code": "STANDALONE_OS_MIN_VER_INVALID",
509
- "description": "os_min_ver 格式不正確。",
510
- "suggestion": "請使用 <major>.<minor>-<build>,例如 7.2-64570。"
514
+ "description": "os_min_ver has the wrong format.",
515
+ "suggestion": "Use <major>.<minor>-<build>, for example 7.2-64570."
511
516
  },
512
517
  "STANDALONE_TEXT_INVALID": {
513
518
  "code": "STANDALONE_TEXT_INVALID",
514
- "description": "顯示文字欄位不合法。",
515
- "suggestion": "請使用單行、非空且不含 \" 或 \\ 的文字。"
519
+ "description": "A display text field is invalid.",
520
+ "suggestion": "Use single-line, non-empty text without \" or \\."
516
521
  },
517
522
  "STANDALONE_ARCHES_INVALID": {
518
523
  "code": "STANDALONE_ARCHES_INVALID",
519
- "description": "arches 不合法。",
520
- "suggestion": "請列出不重複的 x86_64、aarch64 或 armv7,至少一個。"
524
+ "description": "arches is invalid.",
525
+ "suggestion": "List one or more unique values from x86_64, aarch64 or armv7."
521
526
  },
522
527
  "STANDALONE_ARCH_NOT_DECLARED": {
523
528
  "code": "STANDALONE_ARCH_NOT_DECLARED",
524
- "description": "要求建置的架構不在 arches 內。",
525
- "suggestion": "請把該架構加入 dsm-package.yaml 的 arches,或改用已宣告的 --arch。"
529
+ "description": "The requested build architecture is not in arches.",
530
+ "suggestion": "Add the architecture to arches in dsm-package.yaml, or use a declared --arch."
526
531
  },
527
532
  "STANDALONE_BACKEND_INVALID": {
528
533
  "code": "STANDALONE_BACKEND_INVALID",
529
- "description": "backend 設定不合法。",
530
- "suggestion": "backend 需恰好包含 go(dir 與 ./ 開頭的 main)或 binary(以架構為 key)其中之一。"
534
+ "description": "The backend configuration is invalid.",
535
+ "suggestion": "backend must contain exactly one of go (dir and a ./ main) or binary (keyed by architecture)."
531
536
  },
532
537
  "STANDALONE_BINARY_ARCH_MISSING": {
533
538
  "code": "STANDALONE_BINARY_ARCH_MISSING",
534
- "description": "arches 宣告的架構沒有對應的 backend.files 執行檔。",
535
- "suggestion": "請在 backend.files 補上該架構的執行檔,或從 arches 移除它。"
539
+ "description": "An architecture declared in arches has no backend.files executable.",
540
+ "suggestion": "Add the executable for that architecture to backend.files, or remove it from arches."
536
541
  },
537
542
  "STANDALONE_BINARY_MISSING": {
538
543
  "code": "STANDALONE_BINARY_MISSING",
539
- "description": "找不到 backend 執行檔。",
540
- "suggestion": "請確認 backend.files 指向的檔案存在。"
544
+ "description": "The backend executable was not found.",
545
+ "suggestion": "Confirm the file backend.files points at exists."
541
546
  },
542
547
  "STANDALONE_BINARY_NOT_REGULAR": {
543
548
  "code": "STANDALONE_BINARY_NOT_REGULAR",
544
- "description": "backend 執行檔不是一般檔案。",
545
- "suggestion": "請改用實際檔案,不可是 symlink、目錄或特殊檔案。"
549
+ "description": "The backend executable is not a regular file.",
550
+ "suggestion": "Use a real file; no symlinks, directories or special files."
546
551
  },
547
552
  "STANDALONE_BINARY_TOO_LARGE": {
548
553
  "code": "STANDALONE_BINARY_TOO_LARGE",
549
- "description": "backend 執行檔超過 256 MiB。",
550
- "suggestion": "請縮小執行檔(例如 -ldflags \"-s -w\")。"
554
+ "description": "The backend executable exceeds 256 MiB.",
555
+ "suggestion": "Shrink the executable (for example -ldflags \"-s -w\")."
551
556
  },
552
557
  "STANDALONE_BINARY_NOT_ELF": {
553
558
  "code": "STANDALONE_BINARY_NOT_ELF",
554
- "description": "backend 執行檔不是該架構需要的 little-endian Linux ELF(x86_64、aarch64 為 64-bit,armv7 為 32-bit)。",
555
- "suggestion": "請提供為 Linux 編譯的 x86_64、aarch64 或 armv7 執行檔。"
559
+ "description": "The backend executable is not the little-endian Linux ELF the architecture needs (64-bit for x86_64 and aarch64, 32-bit for armv7).",
560
+ "suggestion": "Provide an x86_64, aarch64 or armv7 executable built for Linux."
556
561
  },
557
562
  "STANDALONE_BINARY_ARCH_MISMATCH": {
558
563
  "code": "STANDALONE_BINARY_ARCH_MISMATCH",
559
- "description": "backend 執行檔的 CPU 架構與目標不符。",
560
- "suggestion": "請為該架構重新編譯,或修正 backend.files 的對應。"
564
+ "description": "The backend executable's CPU architecture does not match the target.",
565
+ "suggestion": "Rebuild for that architecture, or fix the backend.files mapping."
561
566
  },
562
567
  "STANDALONE_BINARY_DYNAMIC": {
563
568
  "code": "STANDALONE_BINARY_DYNAMIC",
564
- "description": "backend 執行檔是動態連結。",
565
- "suggestion": "請靜態連結(Go 用 CGO_ENABLED=0),DSM 各機型的 libc 版本不同。"
569
+ "description": "The backend executable is dynamically linked.",
570
+ "suggestion": "Link statically (set CGO_ENABLED=0 for Go); the libc version differs across DSM models."
566
571
  },
567
572
  "STANDALONE_PATH_INVALID": {
568
573
  "code": "STANDALONE_PATH_INVALID",
569
- "description": "路徑欄位不是專案內的相對路徑。",
570
- "suggestion": "請使用相對於專案根目錄的路徑。"
574
+ "description": "The path field is not a relative path inside the project.",
575
+ "suggestion": "Use a path relative to the project root."
571
576
  },
572
577
  "STANDALONE_PATH_OUTSIDE_PROJECT": {
573
578
  "code": "STANDALONE_PATH_OUTSIDE_PROJECT",
574
- "description": "路徑指向專案目錄之外。",
575
- "suggestion": "請把檔案放在專案目錄內(symlink 解析後也必須在專案內)。"
579
+ "description": "The path points outside the project directory.",
580
+ "suggestion": "Keep the files inside the project directory (also after resolving symlinks)."
576
581
  },
577
582
  "STANDALONE_UI_INVALID": {
578
583
  "code": "STANDALONE_UI_INVALID",
579
- "description": "ui 設定不合法。",
580
- "suggestion": "請提供 ui.dir。"
584
+ "description": "The ui configuration is invalid.",
585
+ "suggestion": "Provide ui.dir."
581
586
  },
582
587
  "STANDALONE_UI_BUILD_INVALID": {
583
588
  "code": "STANDALONE_UI_BUILD_INVALID",
584
- "description": "ui.build 設定不合法。",
585
- "suggestion": "ui.build.command 必須是 argv 字串陣列(不經 shell),例如 [npm, run, build]。"
589
+ "description": "The ui.build configuration is invalid.",
590
+ "suggestion": "ui.build.command must be an argv string array (not going through a shell), for example [npm, run, build]."
591
+ },
592
+ "STANDALONE_TIMEOUT_UNSUPPORTED": {
593
+ "code": "STANDALONE_TIMEOUT_UNSUPPORTED",
594
+ "description": "backend.timeout_seconds needs a Go main that reads the requestTimeout the build passes.",
595
+ "suggestion": "Declare var requestTimeout = \"\" in main.go and pass its seconds to api.WithRequestDeadline(handler, api.Timeout(...)), or remove timeout_seconds."
596
+ },
597
+ "STANDALONE_UI_WINDOW_INVALID": {
598
+ "code": "STANDALONE_UI_WINDOW_INVALID",
599
+ "description": "ui.window does not give a valid DSM window size.",
600
+ "suggestion": "Give width and height as whole numbers from 320 to 3840 and 240 to 2160, keep minWidth and minHeight (700 and 420 by default) no larger than them, and write only the fields you change."
586
601
  },
587
602
  "STANDALONE_UI_BUILD_FAILED": {
588
603
  "code": "STANDALONE_UI_BUILD_FAILED",
589
- "description": "UI build 指令失敗。",
590
- "suggestion": "請在 ui.build.cwd 安裝 UI 依賴(有 package-lock.json 用 npm ci,否則 npm install),再執行 UI build 指令並修正錯誤。"
604
+ "description": "The UI build command failed.",
605
+ "suggestion": "Install the UI dependencies in ui.build.cwd (npm ci when package-lock.json exists, otherwise npm install), then run the UI build command and fix the errors."
591
606
  },
592
607
  "STANDALONE_UI_ENTRY_MISSING": {
593
608
  "code": "STANDALONE_UI_ENTRY_MISSING",
594
- "description": "UI 目錄缺少 app.js 或 app.css。",
595
- "suggestion": "請讓 ui.dir(或 UI build 輸出)包含 app.js 與 app.css。"
609
+ "description": "The UI directory is missing app.js or app.css.",
610
+ "suggestion": "Make ui.dir (or the UI build output) contain app.js and app.css."
596
611
  },
597
612
  "STANDALONE_UI_SYMLINK": {
598
613
  "code": "STANDALONE_UI_SYMLINK",
599
- "description": "UI 目錄含有 symlink。",
600
- "suggestion": "請以實際檔案取代 symlink。"
614
+ "description": "The UI directory contains a symlink.",
615
+ "suggestion": "Replace the symlink with a real file."
601
616
  },
602
617
  "STANDALONE_UI_SPECIAL_FILE": {
603
618
  "code": "STANDALONE_UI_SPECIAL_FILE",
604
- "description": "UI 目錄含有特殊檔案。",
605
- "suggestion": "UI 目錄只能有一般檔案與目錄。"
619
+ "description": "The UI directory contains a special file.",
620
+ "suggestion": "The UI directory may contain only regular files and directories."
606
621
  },
607
622
  "STANDALONE_UI_TOO_LARGE": {
608
623
  "code": "STANDALONE_UI_TOO_LARGE",
609
- "description": "UI 檔案總大小超過 64 MiB。",
610
- "suggestion": "請縮小 UI 資產。"
624
+ "description": "The total UI file size exceeds 64 MiB.",
625
+ "suggestion": "Shrink the UI assets."
611
626
  },
612
627
  "STANDALONE_GO_MISSING": {
613
628
  "code": "STANDALONE_GO_MISSING",
614
- "description": "找不到 go 指令。",
615
- "suggestion": "請安裝 Go 1.25 以上版本(https://go.dev/dl/),或改用 backend.files 打包既有執行檔。"
629
+ "description": "The go command was not found.",
630
+ "suggestion": "Install Go 1.25 or later (https://go.dev/dl/), or package an existing executable with backend.files."
616
631
  },
617
632
  "STANDALONE_GO_BUILD_FAILED": {
618
633
  "code": "STANDALONE_GO_BUILD_FAILED",
619
- "description": "go build 失敗。",
620
- "suggestion": "請在 backend 目錄執行 go build ./... 並修正錯誤。取不到 github.com/synopackageland/syno-pkg-land-sdk 時,設定 GOPRIVATE=github.com/synopackageland/* 並讓 git 能以憑證讀 github.com。"
621
- },
622
- "STANDALONE_RUST_BUILD_FAILED": {
623
- "code": "STANDALONE_RUST_BUILD_FAILED",
624
- "description": "cargo build 失敗。",
625
- "suggestion": "請在 backend 目錄執行 cargo build --release --locked 並修正錯誤;Cargo.lock 必須已提交。取不到 github.com/synopackageland/syno-pkg-land-sdk 時,設定 CARGO_NET_GIT_FETCH_WITH_CLI=true 並讓 git 能以憑證讀 github.com。"
634
+ "description": "go build failed.",
635
+ "suggestion": "Run go build ./... in the backend directory and fix the errors. If github.com/synopackageland/syno-pkg-land-sdk is unreachable, set GOPRIVATE=github.com/synopackageland/* and make sure git can read github.com with credentials."
626
636
  },
627
637
  "STANDALONE_PORTAL_INVALID": {
628
638
  "code": "STANDALONE_PORTAL_INVALID",
629
- "description": "dsm-package.yaml 的 portal 格式或 alias 不正確。",
630
- "suggestion": "請使用 portal: {} 或 portal: { alias: my-app };alias 為 1–64 個小寫英數字或 -,且不是 DSM 保留路徑。"
639
+ "description": "The portal format or alias in dsm-package.yaml is invalid.",
640
+ "suggestion": "Use portal: {} or portal: { alias: my-app }; alias is 1-64 lowercase alphanumerics or -, and not a DSM reserved path."
631
641
  },
632
642
  "STANDALONE_PORTAL_REQUIRES_EXECUTOR": {
633
643
  "code": "STANDALONE_PORTAL_REQUIRES_EXECUTOR",
634
- "description": "沒有 executor(capabilities.rootOps 或 delegate)的 App 宣告了 portal。",
635
- "suggestion": "登入入口別名在解除安裝後要 root 才能讓 nginx 停止提供,而無升權 App 任何階段都不使用 root。請加 capabilities.rootOps 或 delegate(executor 層級),或移除 portal,改由管理員在 DSM 手動設定反向代理。"
644
+ "description": "An App without an executor (capabilities.rootOps or delegate) declares portal.",
645
+ "suggestion": "Removing the sign-in alias after uninstall needs root so nginx stops serving it, but an App without escalation uses no root at any stage. Add capabilities.rootOps or delegate (executor level), or remove portal and let an administrator configure a reverse proxy in DSM."
636
646
  },
637
647
  "STANDALONE_I18N_INVALID": {
638
648
  "code": "STANDALONE_I18N_INVALID",
639
- "description": "dsm-package.yaml 的 i18n 設定不合法。",
640
- "suggestion": "i18n 只接受 messages、glossary 與 info;info 以 zh-TW、ja 等支援語言為 key(英文寫在頂層)。"
649
+ "description": "The i18n configuration in dsm-package.yaml is invalid.",
650
+ "suggestion": "i18n accepts only messages, glossary and info; info is keyed by a supported language such as zh-TW or ja (English goes at the top level)."
641
651
  },
642
652
  "STANDALONE_I18N_MESSAGES_MISSING": {
643
653
  "code": "STANDALONE_I18N_MESSAGES_MISSING",
644
- "description": "i18n.messages 目錄缺少 en.json。",
645
- "suggestion": "請加入 en.json;它是所有語言的後備文字與 key 清單。"
654
+ "description": "The i18n.messages directory is missing en.json.",
655
+ "suggestion": "Add en.json; it is the fallback text and key list for every language."
646
656
  },
647
657
  "STANDALONE_I18N_LOCALE_UNSUPPORTED": {
648
658
  "code": "STANDALONE_I18N_LOCALE_UNSUPPORTED",
649
- "description": "訊息檔的語言不在支援的九種語言內。",
650
- "suggestion": "請使用 en、zh-TW、zh-CN、ja、ko、de、fr、es、it;其他 DSM 語言會顯示英文。"
659
+ "description": "A message file's language is not one of the nine supported locales.",
660
+ "suggestion": "Use en, zh-TW, zh-CN, ja, ko, de, fr, es, it; every other DSM language shows English."
651
661
  },
652
662
  "STANDALONE_I18N_CATALOG_INVALID": {
653
663
  "code": "STANDALONE_I18N_CATALOG_INVALID",
654
- "description": "訊息檔不是扁平的 key → 文字 JSON 物件。",
655
- "suggestion": "請改成 { \"greeting.save\": \"Save\" } 這種格式。"
664
+ "description": "A message file is not a flat key -> text JSON object.",
665
+ "suggestion": "Use a format like { \"greeting.save\": \"Save\" }."
656
666
  },
657
667
  "STANDALONE_I18N_KEYS_MISMATCH": {
658
668
  "code": "STANDALONE_I18N_KEYS_MISMATCH",
659
- "description": "某語言的訊息 key 與 en.json 不同。",
660
- "suggestion": "請讓每個語言檔都恰好有 en.json 的所有 key。"
669
+ "description": "One language's message keys differ from en.json.",
670
+ "suggestion": "Give every language file exactly the keys of en.json."
661
671
  },
662
672
  "STANDALONE_I18N_PLACEHOLDER_MISMATCH": {
663
673
  "code": "STANDALONE_I18N_PLACEHOLDER_MISMATCH",
664
- "description": "翻譯少了或多了 {placeholder}。",
665
- "suggestion": "請保留 en.json 文字中的每個 {name} 佔位符。"
674
+ "description": "A translation has a missing or extra {placeholder}.",
675
+ "suggestion": "Keep every {name} placeholder from the en.json text."
666
676
  },
667
677
  "STANDALONE_I18N_GLOSSARY_INVALID": {
668
678
  "code": "STANDALONE_I18N_GLOSSARY_INVALID",
669
- "description": "用語表格式錯誤,或指向不存在的訊息 key。",
670
- "suggestion": "請修正 glossary.json 的 terms(id、text、keys)。"
679
+ "description": "The glossary format is wrong, or it points at a message key that does not exist.",
680
+ "suggestion": "Fix the terms (id, text, keys) in glossary.json."
671
681
  },
672
682
  "STANDALONE_I18N_GLOSSARY_MISMATCH": {
673
683
  "code": "STANDALONE_I18N_GLOSSARY_MISMATCH",
674
- "description": "訊息沒有使用用語表中的 DSM 介面用語。",
675
- "suggestion": "請改用 glossary.json 中該語言的用語,與 DSM 介面保持一致。"
684
+ "description": "A message does not use the DSM interface term from the glossary.",
685
+ "suggestion": "Use that language's term from glossary.json to stay consistent with the DSM interface."
676
686
  },
677
687
  "DEPLOY_VERSION_INVALID": {
678
688
  "code": "DEPLOY_VERSION_INVALID",
679
- "description": "套件版本無效或低於已安裝版本。",
680
- "suggestion": "設定更高的有效 version/build 再部署。"
689
+ "description": "The package version is invalid or lower than the installed version.",
690
+ "suggestion": "Set a higher valid version/build and deploy again."
681
691
  }
682
692
  };
683
693
  for (const [code, description, suggestion] of [
684
- ["CAPABILITY_INVALID", "能力宣告不合法。", "使用 files: session、jobs: true 或 {worker: sha256-v1}、identity: standalone、rootOps: true。"],
685
- ["CAPABILITY_UNSUPPORTED", "這個 runtime 不支援所選能力。", "files、jobs、identity 需要 backend.kind: go;compose 不支援原生能力。"],
686
- ["CAPABILITY_COMBINATION_INVALID", "能力與 runtime 或 UI 組合不合法。", "daemon 不能搭配 capabilities.jobs;identity/login 需要 vue UI(--ui vue)。"],
687
- ["ADD_FILE_CONFLICT", "既有檔案與 SDK overlay 衝突。", "先移動或合併列出的衝突檔案再執行 add;CLI 不覆寫不同內容。"],
694
+ ["CAPABILITY_INVALID", "The capability declaration is invalid.", "Use files: session, jobs: true or {worker: sha256-v1}, identity: standalone, rootOps: true."],
695
+ ["CAPABILITY_UNSUPPORTED", "This runtime does not support the chosen capability.", "files, jobs and identity need backend.kind: go; compose does not support native capabilities."],
696
+ ["CAPABILITY_COMBINATION_INVALID", "The capability combination with the runtime or UI is invalid.", "daemon cannot combine with capabilities.jobs; identity/login needs a vue UI (--ui vue)."],
697
+ ["ADD_FILE_CONFLICT", "Existing files conflict with the SDK overlay.", "Move or merge the listed conflicting files before running add; the CLI never overwrites different content."],
688
698
  ])
689
699
  ERROR_REGISTRY[code] = { code, description, suggestion };
690
- ERROR_REGISTRY["ADD_LAYOUT_UNSUPPORTED"] = { "code": "ADD_LAYOUT_UNSUPPORTED", "description": "SDK overlay 需要標準 backend 目錄。", "suggestion": "使用 backend.dir: backend,或手動合併 SDK overlay。" };
691
- ERROR_REGISTRY["MIN_DSM_CONTAINER_FLOOR"] = { "code": "MIN_DSM_CONTAINER_FLOOR", "description": "Compose 最低 DSM 版本過低。", "suggestion": "Compose 需要 DSM 7.2.1-69057 或以上。" };
692
- ERROR_REGISTRY.STANDALONE_PORTAL_ENTRY_INVALID = { "code": "STANDALONE_PORTAL_ENTRY_INVALID", "description": "os_min_ver 低於 DSM 7.2-64570 時由 executor 註冊 portal,套件名稱或 displayname 不符合它的規則。", "suggestion": "套件名稱只能用英數字,以 . _ - 分隔,最多 64 字;displayname 1–64 個字,不可含控制字元。" };
693
- ERROR_REGISTRY.STANDALONE_PORTAL_MIN_DSM = { "code": "STANDALONE_PORTAL_MIN_DSM", "description": "宣告 portal 的套件 os_min_ver 低於 DSM 7.1-42661。", "suggestion": "DSM 7.2-64570 以前由 executor 寫入 DSM 的 app.d 註冊入口,這個格式只在 DSM 7.1 起驗證過。把 os_min_ver 提高到 7.1-42661 以上,或移除 portal。" };
694
- ERROR_REGISTRY["MIN_DSM_INVALID"] = { "code": "MIN_DSM_INVALID", "description": "最低 DSM 版本格式不合法。", "suggestion": "指定 7.x release 或完整 7.x-build。" };
695
- ERROR_REGISTRY["NOTICE_UNSAFE"] = { "code": "NOTICE_UNSAFE", "description": "授權檔案不符合一般檔案限制。", "suggestion": "移除 symlink、特殊檔與超大授權檔案。" };
696
- ERROR_REGISTRY["NOTICE_MISSING"] = { "code": "NOTICE_MISSING", "description": "缺少第三方授權資訊。", "suggestion": "重新安裝模板依賴並產生 third-party notices。" };
697
- ERROR_REGISTRY["PROJECT_NOT_FOUND"] = { "code": "PROJECT_NOT_FOUND", "description": "找不到專案 manifest。", "suggestion": "在專案根目錄執行或使用 --project。" };
698
- ERROR_REGISTRY["CLI_OPTION_INVALID"] = { "code": "CLI_OPTION_INVALID", "description": "指令選項不合法。", "suggestion": "使用 --help 核對選項與可用 runtime。" };
699
- ERROR_REGISTRY["CLI_OUTPUT_ARCH_REQUIRED"] = { "code": "CLI_OUTPUT_ARCH_REQUIRED", "description": "輸出單一檔案需要指定架構。", "suggestion": "-o 搭配單一 --arch,或使用 --out-dir。" };
700
- ERROR_REGISTRY["HOST_REQUIRED"] = { "code": "HOST_REQUIRED", "description": "沒有指定 NAS。", "suggestion": "使用 --host <profile>;deploy 本地檢查可用 --dry-run。" };
701
- ERROR_REGISTRY["NAS_CREDENTIALS_MISSING"] = { "code": "NAS_CREDENTIALS_MISSING", "description": "找不到 NAS 憑證。", "suggestion": "設定 synoagentcli profile 或 DSM_URL/DSM_PASSWORD。" };
702
- ERROR_REGISTRY["NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE"] = { "code": "NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE", "description": "native layout metadata 的 scripts/ 生命週期腳本沒有執行權限(inspect 的 SPK_SCRIPT_NOT_EXECUTABLE)。", "suggestion": "在 layout metadata 目錄對該腳本執行 chmod +x 後重新 build。" };
703
- ERROR_REGISTRY["NATIVE_TOOLKIT_TAR_UNSUPPORTED"] = { "code": "NATIVE_TOOLKIT_TAR_UNSUPPORTED", "description": "解開 native.toolkits 需要 GNU tar 或 bsdtar,但 PATH 上的 tar 兩者都不是。", "suggestion": "讓 PATH 上的 tar 指向 GNU tar 或 bsdtar(macOS 內建 bsdtar、多數 Linux 是 GNU tar)後重新 build。" };
704
- ERROR_REGISTRY["SPK_INSPECTION_FAILED"] = { "code": "SPK_INSPECTION_FAILED", "description": "SPK 不符合 DSM 7 套件格式(dsm.errors),或 synopkgland 建置的 SPK 不符合建置合約(findings)。", "suggestion": "依 SPK_* 規則碼與 findings 修正後重新打包;warnings 不影響結果。" };
705
- ERROR_REGISTRY["IMAGE_PIN_REQUIRED"] = { "code": "IMAGE_PIN_REQUIRED", "description": "映像 digest 尚未固定。", "suggestion": "執行 images pin。" };
706
- ERROR_REGISTRY["INIT_TEMPLATE_REQUIRED"] = { "code": "INIT_TEMPLATE_REQUIRED", "description": "未指定模板。", "suggestion": "使用 -t native-go、binary 或 compose。" };
707
- ERROR_REGISTRY["INIT_TEMPLATE_UNKNOWN"] = { "code": "INIT_TEMPLATE_UNKNOWN", "description": "未知模板。", "suggestion": "使用 -t native-go、binary 或 compose。" };
708
- ERROR_REGISTRY["CLI_ARCH_INVALID"] = { "code": "CLI_ARCH_INVALID", "description": "架構選項不合法。", "suggestion": "使用 x86_64、aarch64 或 armv7(armv7 用於 native-go 與 binary,不用於 Container Manager App)。" };
709
- ERROR_REGISTRY["CLI_JOBS_INVALID"] = { "code": "CLI_JOBS_INVALID", "description": "--jobs 不是正整數。", "suggestion": "使用 --jobs 1(預設)或更大的整數。" };
710
- ERROR_REGISTRY["BUILD_NOT_REPRODUCIBLE"] = { "code": "BUILD_NOT_REPRODUCIBLE", "description": "build --verify-reproducible 再建置一次的 SPK 與第一次不同。", "suggestion": "查看列出的 payload 與 metadata 檔案,移除每次建置都會變的內容(時間戳、隨機或絕對路徑、未鎖定的依賴)。" };
711
- ERROR_REGISTRY["REPRODUCIBLE_INVALID"] = { "code": "REPRODUCIBLE_INVALID", "description": "reproducible 設定格式不對,或列出的 input 不是專案內的檔案。", "suggestion": "使用 reproducible: {inputs: [檔案...], clean: [路徑...]},路徑相對於專案;{arch} 只能用在 clean。" };
712
- ERROR_REGISTRY["NATIVE_ARCH_UNDETERMINED"] = { "code": "NATIVE_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "指定 --arch x86_64、aarch64 或 armv7。" };
713
- ERROR_REGISTRY["DEVELOPMENT_INSTANCE_UNSUPPORTED"] = { "code": "DEVELOPMENT_INSTANCE_UNSUPPORTED", "description": "原生專案不支援 --dev。", "suggestion": "init 時指定不同 package ID。" };
714
- ERROR_REGISTRY["INIT_OPTIONS_UNSUPPORTED"] = { "code": "INIT_OPTIONS_UNSUPPORTED", "description": "選項不適用於這個模板。", "suggestion": "--from 僅 compose;--ui 僅 native-go。" };
715
- ERROR_REGISTRY["INIT_TARGET_NOT_EMPTY"] = { "code": "INIT_TARGET_NOT_EMPTY", "description": "init 目錄已有衝突檔案。", "suggestion": "使用空目錄;既有專案新增能力使用 add。" };
716
- ERROR_REGISTRY["INIT_COMPOSE_INVALID"] = { "code": "INIT_COMPOSE_INVALID", "description": "匯入的 Compose 不合法。", "suggestion": "--from 指向有 services 的 YAML 檔案。" };
717
- ERROR_REGISTRY["INIT_BINARY_BUILD_FAILED"] = { "code": "INIT_BINARY_BUILD_FAILED", "description": "binary 範例編譯失敗。", "suggestion": "安裝 Go 或提供自己的靜態 CGI ELF。" };
718
- ERROR_REGISTRY["INIT_SHARED_FOLDER_INVALID"] = { "code": "INIT_SHARED_FOLDER_INVALID", "description": "共用資料夾選項不合法。", "suggestion": "使用 select 或 create;create 加上 --shared-folder-name。" };
719
- ERROR_REGISTRY["INIT_UI_INVALID"] = { "code": "INIT_UI_INVALID", "description": "UI 選項不合法。", "suggestion": "使用 --ui vue 或 plain。" };
720
- ERROR_REGISTRY["INIT_PACKAGE_INVALID"] = { "code": "INIT_PACKAGE_INVALID", "description": "套件名稱不合法。", "suggestion": "使用英文字母開頭的 2–64 字英數字名稱。" };
721
- ERROR_REGISTRY["INIT_DISPLAY_NAME_INVALID"] = { "code": "INIT_DISPLAY_NAME_INVALID", "description": "顯示名稱不合法。", "suggestion": "使用單行且不含引號或反斜線的文字。" };
722
- ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_CHANGED"] = { "code": "STANDALONE_API_PERMISSIONS_CHANGED", "description": "建置中 API policy 變動。", "suggestion": "固定 permissions.json 後重新 check/build。" };
723
- ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_INVALID"] = { "code": "STANDALONE_API_PERMISSIONS_INVALID", "description": "API policy 不合法。", "suggestion": "修正 backend/internal/api/permissions.json。" };
724
- ERROR_REGISTRY["CLI_OPTION_UNKNOWN"] = { "code": "CLI_OPTION_UNKNOWN", "description": "未知 CLI 選項。", "suggestion": "使用 --help 查看目前支援的選項。" };
725
- ERROR_REGISTRY["CLI_ARGUMENT_MISSING"] = { "code": "CLI_ARGUMENT_MISSING", "description": "缺少指令引數。", "suggestion": "使用 --help 查看必填引數。" };
726
- ERROR_REGISTRY["CLI_OPTION_MISSING"] = { "code": "CLI_OPTION_MISSING", "description": "缺少必要選項。", "suggestion": "使用 --help 查看必要選項。" };
727
- ERROR_REGISTRY["CLI_OPTION_ARGUMENT_MISSING"] = { "code": "CLI_OPTION_ARGUMENT_MISSING", "description": "選項缺少值。", "suggestion": "補上選項需要的值。" };
728
- ERROR_REGISTRY["CLI_COMMAND_UNKNOWN"] = { "code": "CLI_COMMAND_UNKNOWN", "description": "未知指令。", "suggestion": "使用 synopkgland --help 查看指令。" };
729
- ERROR_REGISTRY["COMMAND_FAILED"] = { "code": "COMMAND_FAILED", "description": "指令執行失敗。", "suggestion": "查看 message 與 stderr,修正問題後重試。" };
730
- ERROR_REGISTRY["DEPLOY_PLAN_INVALID"] = { "code": "DEPLOY_PLAN_INVALID", "description": "部署計畫與 runtime 不符。", "suggestion": "重新 check 並產生部署計畫。" };
731
- ERROR_REGISTRY["DEPLOY_ARCH_UNDETERMINED"] = { "code": "DEPLOY_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "使用 --arch x86_64、aarch64 或 armv7。" };
732
- ERROR_REGISTRY["CONTAINER_ARCH_UNSUPPORTED"] = { "code": "CONTAINER_ARCH_UNSUPPORTED", "description": "Container Manager App 只支援 x86_64 與 aarch64;DSM 不為 armv7 提供 Container Manager。", "suggestion": "改用 --arch x86_64 或 aarch64,或改用 native-go 模板。" };
733
- ERROR_REGISTRY["DEPLOY_BUILD_OUTPUT_INVALID"] = { "code": "DEPLOY_BUILD_OUTPUT_INVALID", "description": "部署建置輸出數量不符。", "suggestion": "指定單一架構並重新 build。" };
700
+ ERROR_REGISTRY["ADD_LAYOUT_UNSUPPORTED"] = { "code": "ADD_LAYOUT_UNSUPPORTED", "description": "The SDK overlay needs the standard backend directory.", "suggestion": "Use backend.dir: backend, or merge the SDK overlay manually." };
701
+ ERROR_REGISTRY["MIN_DSM_CONTAINER_FLOOR"] = { "code": "MIN_DSM_CONTAINER_FLOOR", "description": "The Compose minimum DSM version is too low.", "suggestion": "Compose needs DSM 7.2.1-69057 or later." };
702
+ ERROR_REGISTRY.STANDALONE_PORTAL_ENTRY_INVALID = { "code": "STANDALONE_PORTAL_ENTRY_INVALID", "description": "When os_min_ver is below DSM 7.2-64570 the executor registers the portal, and the package name or displayname does not match its rules.", "suggestion": "The package name may contain only alphanumerics separated by . _ -, at most 64 characters; displayname is 1-64 characters without control characters." };
703
+ ERROR_REGISTRY.STANDALONE_PORTAL_MIN_DSM = { "code": "STANDALONE_PORTAL_MIN_DSM", "description": "A package that declares portal has os_min_ver below DSM 7.1-42661.", "suggestion": "Before DSM 7.2-64570 the executor writes the entry into DSM's app.d, and that format is only verified from DSM 7.1. Raise os_min_ver to 7.1-42661 or later, or remove portal." };
704
+ ERROR_REGISTRY["MIN_DSM_INVALID"] = { "code": "MIN_DSM_INVALID", "description": "The minimum DSM version format is invalid.", "suggestion": "Specify a 7.x release or a full 7.x-build." };
705
+ ERROR_REGISTRY["NOTICE_UNSAFE"] = { "code": "NOTICE_UNSAFE", "description": "A license file does not meet the regular-file limits.", "suggestion": "Remove symlinks, special files and oversized license files." };
706
+ ERROR_REGISTRY["NOTICE_MISSING"] = { "code": "NOTICE_MISSING", "description": "Third-party license information is missing.", "suggestion": "Reinstall the template dependencies and generate the third-party notices." };
707
+ ERROR_REGISTRY["PROJECT_NOT_FOUND"] = { "code": "PROJECT_NOT_FOUND", "description": "The project manifest was not found.", "suggestion": "Run from the project root or use --project." };
708
+ ERROR_REGISTRY["CLI_OPTION_INVALID"] = { "code": "CLI_OPTION_INVALID", "description": "A command option is invalid.", "suggestion": "Use --help to check the options and the available runtime." };
709
+ ERROR_REGISTRY["CLI_OUTPUT_ARCH_REQUIRED"] = { "code": "CLI_OUTPUT_ARCH_REQUIRED", "description": "Writing a single file needs an architecture.", "suggestion": "Combine -o with a single --arch, or use --out-dir." };
710
+ ERROR_REGISTRY["HOST_REQUIRED"] = { "code": "HOST_REQUIRED", "description": "No NAS was specified.", "suggestion": "Use --host <profile>; for a local deploy check use --dry-run." };
711
+ ERROR_REGISTRY["NAS_CREDENTIALS_MISSING"] = { "code": "NAS_CREDENTIALS_MISSING", "description": "NAS credentials were not found.", "suggestion": "Set a synoagentcli profile or DSM_URL/DSM_PASSWORD." };
712
+ ERROR_REGISTRY["NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE"] = { "code": "NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE", "description": "A scripts/ lifecycle script in the native layout metadata is not executable (inspect's SPK_SCRIPT_NOT_EXECUTABLE).", "suggestion": "chmod +x the script in the layout metadata directory, then build again." };
713
+ ERROR_REGISTRY["NATIVE_TOOLKIT_TAR_UNSUPPORTED"] = { "code": "NATIVE_TOOLKIT_TAR_UNSUPPORTED", "description": "Extracting native.toolkits needs GNU tar or bsdtar, but the tar on PATH is neither.", "suggestion": "Point the tar on PATH at GNU tar or bsdtar (macOS ships bsdtar, most Linux distros GNU tar), then build again." };
714
+ ERROR_REGISTRY["SPK_INSPECTION_FAILED"] = { "code": "SPK_INSPECTION_FAILED", "description": "The SPK does not match the DSM 7 package format (dsm.errors), or a synopkgland-built SPK does not match the build contract (findings).", "suggestion": "Fix the SPK_* rule codes and findings, then repackage; warnings do not affect the result." };
715
+ ERROR_REGISTRY["IMAGE_PIN_REQUIRED"] = { "code": "IMAGE_PIN_REQUIRED", "description": "The image digest is not pinned.", "suggestion": "Run images pin." };
716
+ ERROR_REGISTRY["INIT_TEMPLATE_REQUIRED"] = { "code": "INIT_TEMPLATE_REQUIRED", "description": "No template was specified.", "suggestion": "Use -t native-go, native-python, native-node, native-rust, binary or compose." };
717
+ ERROR_REGISTRY["INIT_TEMPLATE_UNKNOWN"] = { "code": "INIT_TEMPLATE_UNKNOWN", "description": "Unknown template.", "suggestion": "Use -t native-go, native-python, native-node, native-rust, binary or compose." };
718
+ ERROR_REGISTRY["CLI_ARCH_INVALID"] = { "code": "CLI_ARCH_INVALID", "description": "The architecture option is invalid.", "suggestion": "Use x86_64, aarch64 or armv7 (armv7 is for native-go and binary, not for Container Manager Apps)." };
719
+ ERROR_REGISTRY["CLI_JOBS_INVALID"] = { "code": "CLI_JOBS_INVALID", "description": "--jobs is not a positive integer.", "suggestion": "Use --jobs 1 (the default) or a larger integer." };
720
+ ERROR_REGISTRY["BUILD_NOT_REPRODUCIBLE"] = { "code": "BUILD_NOT_REPRODUCIBLE", "description": "A second SPK built by build --verify-reproducible differs from the first.", "suggestion": "Look at the listed payload and metadata files and remove anything that changes per build (timestamps, random or absolute paths, unpinned dependencies)." };
721
+ ERROR_REGISTRY["REPRODUCIBLE_INVALID"] = { "code": "REPRODUCIBLE_INVALID", "description": "The reproducible configuration is malformed, or a listed input is not a file inside the project.", "suggestion": "Use reproducible: {inputs: [files...], clean: [paths...]} with project-relative paths; {arch} is only allowed in clean." };
722
+ ERROR_REGISTRY["NATIVE_ARCH_UNDETERMINED"] = { "code": "NATIVE_ARCH_UNDETERMINED", "description": "The NAS architecture could not be determined.", "suggestion": "Specify --arch x86_64, aarch64 or armv7." };
723
+ ERROR_REGISTRY["DEVELOPMENT_INSTANCE_UNSUPPORTED"] = { "code": "DEVELOPMENT_INSTANCE_UNSUPPORTED", "description": "A native project does not support --dev.", "suggestion": "Use a different package ID when running init." };
724
+ ERROR_REGISTRY["INIT_OPTIONS_UNSUPPORTED"] = { "code": "INIT_OPTIONS_UNSUPPORTED", "description": "The option does not apply to this template.", "suggestion": "--from is compose only; --ui is native templates only (native-go, native-python, native-node, native-rust)." };
725
+ ERROR_REGISTRY["INIT_TARGET_NOT_EMPTY"] = { "code": "INIT_TARGET_NOT_EMPTY", "description": "The init directory already has conflicting files.", "suggestion": "Use an empty directory; add capabilities to an existing project with add." };
726
+ ERROR_REGISTRY["INIT_COMPOSE_INVALID"] = { "code": "INIT_COMPOSE_INVALID", "description": "The imported Compose is invalid.", "suggestion": "Point --from at a YAML file with services." };
727
+ ERROR_REGISTRY["INIT_BINARY_BUILD_FAILED"] = { "code": "INIT_BINARY_BUILD_FAILED", "description": "The binary example failed to compile.", "suggestion": "Install Go or provide your own static CGI ELF." };
728
+ ERROR_REGISTRY["INIT_SHARED_FOLDER_INVALID"] = { "code": "INIT_SHARED_FOLDER_INVALID", "description": "The shared folder option is invalid.", "suggestion": "Use select or create; add --shared-folder-name with create." };
729
+ ERROR_REGISTRY["INIT_UI_INVALID"] = { "code": "INIT_UI_INVALID", "description": "The UI option is invalid.", "suggestion": "Use --ui vue or plain." };
730
+ ERROR_REGISTRY["INIT_PACKAGE_INVALID"] = { "code": "INIT_PACKAGE_INVALID", "description": "The package name is invalid.", "suggestion": "Use a 2-64 character alphanumeric name that starts with a letter." };
731
+ ERROR_REGISTRY["INIT_DISPLAY_NAME_INVALID"] = { "code": "INIT_DISPLAY_NAME_INVALID", "description": "The display name is invalid.", "suggestion": "Use single-line text without quotes or backslashes." };
732
+ ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_CHANGED"] = { "code": "STANDALONE_API_PERMISSIONS_CHANGED", "description": "The API policy changed during the build.", "suggestion": "Freeze permissions.json, then run check/build again." };
733
+ ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_INVALID"] = { "code": "STANDALONE_API_PERMISSIONS_INVALID", "description": "The API policy is invalid.", "suggestion": "Fix backend/internal/api/permissions.json." };
734
+ ERROR_REGISTRY["CLI_OPTION_UNKNOWN"] = { "code": "CLI_OPTION_UNKNOWN", "description": "Unknown CLI option.", "suggestion": "Use --help to see the currently supported options." };
735
+ ERROR_REGISTRY["CLI_ARGUMENT_MISSING"] = { "code": "CLI_ARGUMENT_MISSING", "description": "A command argument is missing.", "suggestion": "Use --help to see the required arguments." };
736
+ ERROR_REGISTRY["CLI_OPTION_MISSING"] = { "code": "CLI_OPTION_MISSING", "description": "A required option is missing.", "suggestion": "Use --help to see the required options." };
737
+ ERROR_REGISTRY["CLI_OPTION_ARGUMENT_MISSING"] = { "code": "CLI_OPTION_ARGUMENT_MISSING", "description": "An option is missing its value.", "suggestion": "Pass the value the option needs." };
738
+ ERROR_REGISTRY["CLI_COMMAND_UNKNOWN"] = { "code": "CLI_COMMAND_UNKNOWN", "description": "Unknown command.", "suggestion": "Use synopkgland --help to see the commands." };
739
+ ERROR_REGISTRY["COMMAND_FAILED"] = { "code": "COMMAND_FAILED", "description": "The command failed.", "suggestion": "Check the message and stderr, fix the problem and retry." };
740
+ ERROR_REGISTRY["DEPLOY_PLAN_INVALID"] = { "code": "DEPLOY_PLAN_INVALID", "description": "The deploy plan does not match the runtime.", "suggestion": "Run check again and regenerate the deploy plan." };
741
+ ERROR_REGISTRY["DEPLOY_ARCH_UNDETERMINED"] = { "code": "DEPLOY_ARCH_UNDETERMINED", "description": "The NAS architecture could not be determined.", "suggestion": "Use --arch x86_64, aarch64 or armv7." };
742
+ ERROR_REGISTRY["CONTAINER_ARCH_UNSUPPORTED"] = { "code": "CONTAINER_ARCH_UNSUPPORTED", "description": "Container Manager Apps support only x86_64 and aarch64; DSM does not provide Container Manager for armv7.", "suggestion": "Use --arch x86_64 or aarch64, or switch to the native-go template." };
743
+ ERROR_REGISTRY["DEPLOY_BUILD_OUTPUT_INVALID"] = { "code": "DEPLOY_BUILD_OUTPUT_INVALID", "description": "The number of deploy build outputs does not match.", "suggestion": "Specify a single architecture and build again." };
734
744
  export function getErrorCode(code) {
735
745
  return ERROR_REGISTRY[code];
736
746
  }
737
747
  for (const [code, description, suggestion] of [
738
- ["EXECUTOR_BUILD_FAILED", "從 App 相依的 SDK 版本編譯 executor 或 lifecycle helper 失敗。", "確認已安裝 Go 1.21 以上且可取得 go1.26.9(離線時請先備妥),查看訊息中的編譯器輸出。"],
739
- ["EXECUTOR_DIGEST_MISMATCH", "由 SDK 版本建出的 executor 與 CLI 內建的 sha256 表不符,或表中沒有這個 SDK 版本。", "確認 backend/go.mod 的 github.com/synopackageland/syno-pkg-land-sdk 是正式版本且 Go 為 go1.26.9;表中沒有該版本時升級 CLI。"],
740
- ["EXECUTOR_DIGEST_UNVERIFIED", "SYNOPKGLAND_SDK_DIR 指向本機 SDK,executor 未對照 sha256 表(警告)。", "只用於開發;要發佈的 SPK 請取消 SYNOPKGLAND_SDK_DIR 後重新 build。"],
741
- ["SDK_REQUIRED", "rootOps 或 delegate 需要 SDK,但後端沒有相依 syno-pkg-land-sdk(Go:backend/go.mod;Rust:backend/Cargo.toml)。", "Go 在 backend/go.mod 加入 require github.com/synopackageland/syno-pkg-land-sdk;Rust 在 backend/Cargo.toml 以 git+tag 相依 syno-pkg-land-sdk;版本與 CLI 同一 minor,或改用 init 產生的後端。"],
742
- ["SDK_TAG_REQUIRED", "Rust 後端的 backend/Cargo.lock 沒有以正式 release tag 鎖定唯一一個 syno-pkg-land-sdk(branch、rev、path、registry 或多個版本都不接受)。", "在 backend/Cargo.toml 寫 syno-pkg-land-sdk = { git = \"https://github.com/synopackageland/syno-pkg-land-sdk\", tag = \"vX.Y.Z\" },執行 cargo update -p syno-pkg-land-sdk 並提交 Cargo.lock。"],
743
- ["SDK_VERSION_MISMATCH", "frontend/package.json 的 @synopackageland/sdk 與後端(backend/go.mod 或 backend/Cargo.lock)要求的 SDK 版本不同。", "把兩邊改成同一個版本;file: 相依時,比對的是該目錄 package.json 的 version。"],
744
- ["SDK_UNSUPPORTED", "後端(backend/go.mod 或 backend/Cargo.lock)要求的 SDK 不是這個 CLI 的 minor 版本線。", "改用與 SDK 同一 minor 的 CLI,或把 SDK 升到 CLI 支援的版本線(訊息列出)。"],
745
- ["SDK_DIR_NOT_READY", "SYNOPKGLAND_SDK_DIR 指向的 SDK checkout 根目錄缺少 go.mod,或 Vue 專案需要的 ts 套件尚未建置(沒有 ts/dist)。", "指向 syno-pkg-land-sdk checkout;Vue 專案先在 $SYNOPKGLAND_SDK_DIR/ts 執行 npm ci && npm run build(npm 安裝 file: 套件時不會替它 build)。"],
746
- ["SDK_DOWNLOAD_FAILED", "go mod download 取不到 backend/go.mod 要求的 SDK 版本。", "SDK repo 目前是 private,不能經 proxy.golang.org 取得:設定 GOPRIVATE=github.com/synopackageland/*(例如 go env -w),並讓 git 能以憑證讀 github.com(git ls-remote 可驗證);再確認版本存在。離線時先在同一台機器 go mod download 一次。"],
747
- ["TEMPLATES_MISSING", "找不到 @synopackageland/templates。", "重新安裝 @synopackageland/cli,或把 SYNOPKGLAND_TEMPLATES_DIR 指向 templates checkout。"],
748
- ["EXECUTOR_PIN_REQUIRED", "宣告 rootOps 或 delegate 的套件建置時沒有 executor 產物。", "使用 synopkgland build,讓 CLI 編譯並 pin executor;不要直接呼叫低階 SPK API。"],
749
- ["EXECUTOR_POLICY", "capabilities.delegate 的 operations 或 scope 不合法,無法產生 executor policy。", "改用 init/add 產生的 delegate 宣告,或依 manifest 參考修正 scope 與 operations。"],
750
- ["EXECUTOR_PACKAGE", "pin 進 root task 的套件名稱不合法。", "package 必須以英文字母開頭,只含英數字(2–64 字)。"],
751
- ["EXECUTOR_DIGEST", "executor 或 policy 的 sha256 pin 格式不合法。", "重新 build,讓 CLI 重新計算 pin;不要手改 SPK 內的 pin。"],
752
- ["EXECUTOR_TASK_PERCENT", "root task 腳本含有 %,DSM 工作排程器無法保存。", "從 root/ 腳本移除 % 字元(例如 date 格式改用其他寫法)。"],
753
- ["EXECUTOR_TASK_LENGTH", "root task 腳本超過 DSM 工作排程器的長度上限。", "縮短 root/ 腳本,把邏輯移到套件內的檔案再由腳本呼叫。"],
748
+ ["EXECUTOR_BUILD_FAILED", "Compiling the executor or lifecycle helper from the App's SDK version failed.", "Confirm Go 1.21 or later is installed and go1.26.9 is available (prepare it offline), and check the compiler output in the message."],
749
+ ["EXECUTOR_DIGEST_MISMATCH", "The executor built from the SDK version does not match the sha256 table bundled in the CLI, or the table has no entry for this SDK version.", "Confirm github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod is a release version and Go is go1.26.9; upgrade the CLI when the table has no entry for that version."],
750
+ ["EXECUTOR_DIGEST_UNVERIFIED", "SYNOPKGLAND_SDK_DIR points at a local SDK, so the executor was not checked against the sha256 table (warning).", "Development only; for a publishable SPK unset SYNOPKGLAND_SDK_DIR and build again."],
751
+ ["SDK_REQUIRED", "Python, Node and Rust Apps, and rootOps or delegate, need the SDK, but the backend does not name an SDK version (Go: backend/go.mod; Python and Node: backend.sdk; Rust: syno-pkg-land-app in Cargo.toml).", "In Go add require github.com/synopackageland/syno-pkg-land-sdk to backend/go.mod; in Python and Node write backend.sdk in dsm-package.yaml; in Rust depend on syno-pkg-land-app by git+tag in the App's Cargo.toml; keep the version on the same minor as the CLI."],
752
+ ["SDK_TAG_REQUIRED", "The Rust App's Cargo.lock does not pin exactly one syno-pkg-land-app to a release tag (branch, rev, path, registry and multiple versions are all rejected).", "Write syno-pkg-land-app = { git = \"https://github.com/synopackageland/syno-pkg-land-sdk\", tag = \"vX.Y.Z\" } in Cargo.toml, run cargo update -p syno-pkg-land-app and commit Cargo.lock."],
753
+ ["SDK_VERSION_MISMATCH", "The @synopackageland/sdk in frontend/package.json differs from the SDK version the backend requires (backend/go.mod or backend/Cargo.lock).", "Make both sides the same version; for a file: dependency the comparison uses that directory's package.json version."],
754
+ ["SDK_UNSUPPORTED", "The SDK the backend requires (backend/go.mod or backend/Cargo.lock) is not on this CLI's minor line.", "Use a CLI on the same minor as the SDK, or raise the SDK to a version line the CLI supports (listed in the message)."],
755
+ ["SDK_DIR_NOT_READY", "The SDK checkout SYNOPKGLAND_SDK_DIR points at has no go.mod, or the ts package a Vue project needs is not built (no ts/dist).", "Point at a syno-pkg-land-sdk checkout; for a Vue project first run npm ci && npm run build in $SYNOPKGLAND_SDK_DIR/ts (npm does not build file: packages for you)."],
756
+ ["SDK_DOWNLOAD_FAILED", "go mod download cannot fetch the SDK version backend/go.mod requires.", "The SDK repo is private and cannot go through proxy.golang.org: set GOPRIVATE=github.com/synopackageland/* (for example go env -w) and make sure git can read github.com with credentials (verify with git ls-remote); then confirm the version exists. Offline, run go mod download once on the same machine first."],
757
+ ["TEMPLATES_MISSING", "@synopackageland/templates was not found.", "Reinstall @synopackageland/cli, or point SYNOPKGLAND_TEMPLATES_DIR at a templates checkout."],
758
+ ["EXECUTOR_PIN_REQUIRED", "A package that declares rootOps or delegate was built without the executor artifact.", "Use synopkgland build so the CLI compiles and pins the executor; do not call the low-level SPK API directly."],
759
+ ["EXECUTOR_POLICY", "capabilities.delegate has invalid operations or scope, or a binary root program lacks the file for this architecture, so the executor policy cannot be generated.", "Use the delegate declaration init/add produces and fix scope and operations against the manifest reference; for binary root programs provide root/<arch>/<name> for every arch in arches."],
760
+ ["EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED", "The root settings in dsm-package.yaml need an executor that supports root program settings, which the App's SDK version lacks.", "Use an SDK with root programs, stop and root_state (executor/stop_step.go), or remove the root settings."],
761
+ ["EXECUTOR_PACKAGE", "The package name pinned into the root task is invalid.", "package must start with a letter and contain only alphanumerics (2-64 characters)."],
762
+ ["EXECUTOR_DIGEST", "The sha256 pin for the executor or policy is malformed.", "Build again so the CLI recomputes the pin; do not hand-edit pins inside the SPK."],
763
+ ["EXECUTOR_TASK_PERCENT", "The root task script contains %, which the DSM task scheduler cannot store.", "Remove % from the root/ scripts (for example rewrite the date format)."],
764
+ ["EXECUTOR_TASK_LENGTH", "The root task script exceeds the DSM task scheduler's length limit.", "Shorten the root/ scripts; move the logic into a file inside the package and call it from the script."],
765
+ ])
766
+ ERROR_REGISTRY[code] = { code, description, suggestion };
767
+ ERROR_REGISTRY["STANDALONE_ENTRY_INVALID"] = { "code": "STANDALONE_ENTRY_INVALID", "description": "The entry configuration is invalid.", "suggestion": "Set entry to admin-only or app-privilege; app-privilege needs at least one authenticated route." };
768
+ ERROR_REGISTRY["STANDALONE_ENTRY_WIDENED"] = { "code": "STANDALONE_ENTRY_WIDENED", "description": "entry: admin-only conflicts with routes that widen access.", "suggestion": "Change the authenticated or public routes in permissions.json to administrators; to open it to regular users use entry: app-privilege or omit entry." };
769
+ ERROR_REGISTRY["STANDALONE_ENTRY_UNSUPPORTED"] = { "code": "STANDALONE_ENTRY_UNSUPPORTED", "description": "The backend cannot enforce the entry mode.", "suggestion": "Use a Go backend: require github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod and wrap the authenticator with dsm.NewEntryGate in the main.go of backend.main (init defaults to cmd/app/main.go)." };
770
+ ERROR_REGISTRY["DSM_TLS_UNTRUSTED"] = { "code": "DSM_TLS_UNTRUSTED", "description": "The DSM TLS certificate is not trusted by this computer (self-signed, expired or a name mismatch).", "suggestion": "Export the NAS certificate or its CA as PEM and run with NODE_EXTRA_CA_CERTS=<file>; or give DSM a trusted certificate whose name matches the host profile. Do not disable certificate verification." };
771
+ ERROR_REGISTRY["STANDALONE_ICON_INVALID"] = { "code": "STANDALONE_ICON_INVALID", "description": "The icon in dsm-package.yaml is not a usable package icon.", "suggestion": "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon." };
772
+ ERROR_REGISTRY["CONTRACT_ICON_INVALID"] = { "code": "CONTRACT_ICON_INVALID", "description": "The icon in synology-app.yaml is not a usable package icon.", "suggestion": "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon." };
773
+ ERROR_REGISTRY["CONTRACT_I18N_INVALID"] = { "code": "CONTRACT_I18N_INVALID", "description": "The i18n configuration in synology-app.yaml is invalid.", "suggestion": "i18n accepts only info; info is keyed by a supported language such as zh-TW or ja with single-line displayname / description values without \" or \\ (English goes at the top level)." };
774
+ for (const [code, description, suggestion] of [
775
+ ["STANDALONE_BRIDGE_APP_IN_UI", "The backend.dir and ui.dir of a Python, Node or Rust App overlap; DSM serves the files under ui/ directly.", "Put the App in a directory outside ui.dir (for example app/); neither may contain the other."],
776
+ ["STANDALONE_BRIDGE_METHODS_MISMATCH", "The App's methods do not match permissions.json, or use an SDK method name; the runtime refuses to start.", "permissions.json lists only the App's own methods; an App method must not share a name with an SDK method (nas.*, identity.*, portal.status and others)."],
777
+ ["STANDALONE_RUNTIME_BUILD_FAILED", "Compiling the bridge runtime (bridge/cmd/runtime) from the App's SDK version failed, or the result was not built with go1.26.9.", "Confirm Go 1.21 or later is installed and go1.26.9 is available; that SDK version must contain the bridge runtime; check the compiler output in the message."],
778
+ ["STANDALONE_BRIDGE_DESCRIBE_FAILED", "There is no backend.methods, so build starts the App to read hello, but the App did not answer a v1 hello.", "Install python3 or node on the build machine and make sure the App starts; or list backend.methods in dsm-package.yaml."],
779
+ ["STANDALONE_NODE_DEPENDENCY_MISSING", "The Node App's package.json dependencies are not installed in the App's node_modules.", "Run npm ci --omit=dev in the App directory and build again; the CLI places @synopackageland/app, so it does not need installing."],
780
+ ["STANDALONE_BRIDGE_APP_INVALID", "The App directory contains a symlink or special file, or exceeds 256 MiB.", "Keep only regular files and directories in the App directory; move large data out of it."],
781
+ ["STANDALONE_RUST_BUILD_FAILED", "The Rust App's cargo zigbuild cross-compile failed, or it produced no binary for backend.entry.", "Install Rust with rustup and the three targets (x86_64/aarch64-unknown-linux-musl, armv7-unknown-linux-gnueabihf), install cargo-zigbuild and zig; set backend.entry to the Cargo binary name; check the compiler output in the message."],
782
+ ["STANDALONE_BRIDGE_MANIFEST_INVALID", "The bridge.json the CLI produced does not match spec/bridge/v1 manifest.schema.json.", "Check backend.entry, methods and node.package; if it still fails, report the CLI bug."],
783
+ ["STANDALONE_NODE_SDK_NOT_BUILT", "The App's SDK version has no built Node SDK (node/dist).", "Run npm ci && npm run build in node/ of the SYNOPKGLAND_SDK_DIR checkout first; a release SDK must contain node/dist."],
754
784
  ])
755
785
  ERROR_REGISTRY[code] = { code, description, suggestion };
756
- ERROR_REGISTRY["STANDALONE_ENTRY_INVALID"] = { "code": "STANDALONE_ENTRY_INVALID", "description": "entry 設定不合法。", "suggestion": "entry 填 admin-only 或 app-privilege;app-privilege 需要至少一個 authenticated route。" };
757
- ERROR_REGISTRY["STANDALONE_ENTRY_WIDENED"] = { "code": "STANDALONE_ENTRY_WIDENED", "description": "entry: admin-only 與放寬權限的 route 設定衝突。", "suggestion": "把 permissions.json 中 authenticated 或 public 的 route 改為 administrators;要開放給一般使用者請改用 entry: app-privilege 或省略 entry。" };
758
- ERROR_REGISTRY["STANDALONE_ENTRY_UNSUPPORTED"] = { "code": "STANDALONE_ENTRY_UNSUPPORTED", "description": "backend 無法強制 entry 模式。", "suggestion": "使用 Go backend:backend/go.mod require github.com/synopackageland/syno-pkg-land-sdk,並在 backend.main 的 main.go(init 預設為 cmd/app/main.go)以 dsm.NewEntryGate 包住 authenticator。" };
759
- ERROR_REGISTRY["DSM_TLS_UNTRUSTED"] = { "code": "DSM_TLS_UNTRUSTED", "description": "DSM 的 TLS 憑證不受這台電腦信任(自簽、過期或名稱不符)。", "suggestion": "把 NAS 憑證或其 CA 匯出成 PEM,用 NODE_EXTRA_CA_CERTS=<該檔> 執行;或讓 DSM 使用與 host profile 名稱相符的受信任憑證。不要關閉憑證驗證。" };
760
- ERROR_REGISTRY["STANDALONE_ICON_INVALID"] = { "code": "STANDALONE_ICON_INVALID", "description": "dsm-package.yaml 的 icon 不是可用的套件圖示。", "suggestion": "icon 填專案內的正方形 PNG(至少 256×256),或含 icon_256.png 的 icon_<size>.png 目錄;省略則使用 SDK 預設圖示。" };
761
- ERROR_REGISTRY["CONTRACT_ICON_INVALID"] = { "code": "CONTRACT_ICON_INVALID", "description": "synology-app.yaml 的 icon 不是可用的套件圖示。", "suggestion": "icon 填專案內的正方形 PNG(至少 256×256),或含 icon_256.png 的 icon_<size>.png 目錄;省略則使用 SDK 預設圖示。" };
762
- ERROR_REGISTRY["CONTRACT_I18N_INVALID"] = { "code": "CONTRACT_I18N_INVALID", "description": "synology-app.yaml 的 i18n 設定不合法。", "suggestion": "i18n 只接受 info;info 以 zh-TW、ja 等支援語言為 key,值為單行、不含 \" 或 \\ 的 displayname / description(英文寫在頂層)。" };