@synopackageland/cli 0.3.10 → 0.4.0

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 (280) hide show
  1. package/README.md +20 -9
  2. package/dist/bin.js +1 -2
  3. package/dist/build-spk/generator.d.ts +0 -3
  4. package/dist/build-spk/generator.js +135 -287
  5. package/dist/build-spk/native-lifecycle.js +0 -3
  6. package/dist/build-spk/native-payload.js +0 -1
  7. package/dist/cli.js +171 -151
  8. package/dist/compose/analyze.d.ts +1 -2
  9. package/dist/compose/analyze.js +5 -48
  10. package/dist/contract/unsupported-types.d.ts +10 -0
  11. package/dist/contract/unsupported-types.js +49 -0
  12. package/dist/contract/validate.js +25 -2
  13. package/dist/contract/web-launch.js +0 -2
  14. package/dist/deploy/deploy.d.ts +1 -4
  15. package/dist/deploy/deploy.js +45 -156
  16. package/dist/deploy/homes-values.js +1 -1
  17. package/dist/deploy/plan.d.ts +18 -0
  18. package/dist/deploy/plan.js +54 -0
  19. package/dist/deploy/spk-version.d.ts +2 -14
  20. package/dist/deploy/spk-version.js +25 -111
  21. package/dist/dev.d.ts +1 -3
  22. package/dist/dev.js +14 -28
  23. package/dist/error-registry.js +810 -862
  24. package/dist/host/dsm-client.js +12 -272
  25. package/dist/host/dsm-port-profile.d.ts +0 -1
  26. package/dist/host/dsm-port-profile.js +0 -2
  27. package/dist/host/types.d.ts +1 -83
  28. package/dist/index.d.ts +0 -9
  29. package/dist/index.js +0 -6
  30. package/dist/info.d.ts +0 -1
  31. package/dist/info.js +3 -10
  32. package/dist/init.d.ts +11 -1
  33. package/dist/init.js +112 -4
  34. package/dist/log.js +2 -3
  35. package/dist/native/validate.js +0 -1
  36. package/dist/standalone/binary.d.ts +9 -0
  37. package/dist/standalone/binary.js +67 -0
  38. package/dist/standalone/build.d.ts +25 -0
  39. package/dist/standalone/build.js +108 -0
  40. package/dist/standalone/dsm-window.d.ts +26 -0
  41. package/dist/standalone/dsm-window.js +251 -0
  42. package/dist/standalone/helper.d.ts +31 -0
  43. package/dist/standalone/helper.js +104 -0
  44. package/dist/standalone/i18n.d.ts +28 -0
  45. package/dist/standalone/i18n.js +161 -0
  46. package/dist/standalone/inspect-spk.d.ts +21 -0
  47. package/dist/standalone/inspect-spk.js +160 -0
  48. package/dist/standalone/manifest.d.ts +71 -0
  49. package/dist/standalone/manifest.js +276 -0
  50. package/dist/standalone/root-messages.d.ts +7 -0
  51. package/dist/standalone/root-messages.js +250 -0
  52. package/dist/standalone/root-ops.d.ts +42 -0
  53. package/dist/standalone/root-ops.js +101 -0
  54. package/dist/standalone/root-window.d.ts +35 -0
  55. package/dist/standalone/root-window.js +290 -0
  56. package/dist/standalone/spk.d.ts +22 -0
  57. package/dist/standalone/spk.js +205 -0
  58. package/dist/templates/binary/synology-app.yaml +7 -0
  59. package/dist/templates/hello-files/compose.yaml +0 -1
  60. package/dist/templates/hello-web/compose.yaml +13 -10
  61. package/dist/templates/hello-web/html/app.js +1 -23
  62. package/dist/templates/hello-web/html/index.html +1 -3
  63. package/dist/templates/hello-web-page/compose.yaml +13 -10
  64. package/dist/templates/hello-web-page/html/app.js +1 -23
  65. package/dist/templates/hello-web-page/html/index.html +1 -3
  66. package/dist/templates/native-go/README.md +268 -0
  67. package/dist/templates/native-go/backend/cmd/app/main.go +49 -0
  68. package/dist/templates/native-go/backend/cmd/helper/main.go +38 -0
  69. package/dist/templates/native-go/backend/go.mod +3 -0
  70. package/dist/templates/native-go/backend/internal/api/errors.go +33 -0
  71. package/dist/templates/native-go/backend/internal/api/registry.go +94 -0
  72. package/dist/templates/native-go/backend/internal/api/registry_test.go +329 -0
  73. package/dist/templates/native-go/backend/internal/api/server.go +155 -0
  74. package/dist/templates/native-go/backend/internal/app/root_none.go +9 -0
  75. package/dist/templates/native-go/backend/internal/app/routes.go +127 -0
  76. package/dist/templates/native-go/backend/internal/app/routes_test.go +167 -0
  77. package/dist/templates/native-go/backend/internal/dsm/auth.go +118 -0
  78. package/dist/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
  79. package/dist/templates/native-go/backend/internal/helper/control.go +219 -0
  80. package/dist/templates/native-go/backend/internal/helper/control_test.go +242 -0
  81. package/dist/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
  82. package/dist/templates/native-go/backend/internal/helper/peer_other.go +7 -0
  83. package/dist/templates/native-go/backend/internal/helper/process_linux.go +167 -0
  84. package/dist/templates/native-go/backend/internal/helper/process_other.go +5 -0
  85. package/dist/templates/native-go/backend/internal/helper/server_linux.go +83 -0
  86. package/dist/templates/native-go/backend/internal/helper/status.go +271 -0
  87. package/dist/templates/native-go/backend/internal/helper/status_test.go +205 -0
  88. package/dist/templates/native-go/backend/internal/helper/supervision.go +84 -0
  89. package/dist/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
  90. package/dist/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
  91. package/dist/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
  92. package/dist/templates/native-go/dsm-package.yaml +58 -0
  93. package/dist/templates/native-go/gitignore +1 -0
  94. package/dist/templates/native-go/i18n/glossary.json +662 -0
  95. package/dist/templates/native-go/i18n/messages/de.json +28 -0
  96. package/dist/templates/native-go/i18n/messages/en.json +28 -0
  97. package/dist/templates/native-go/i18n/messages/es.json +28 -0
  98. package/dist/templates/native-go/i18n/messages/fr.json +28 -0
  99. package/dist/templates/native-go/i18n/messages/it.json +28 -0
  100. package/dist/templates/native-go/i18n/messages/ja.json +28 -0
  101. package/dist/templates/native-go/i18n/messages/ko.json +28 -0
  102. package/dist/templates/native-go/i18n/messages/zh-CN.json +28 -0
  103. package/dist/templates/native-go/i18n/messages/zh-TW.json +28 -0
  104. package/dist/templates/native-go/ui/app.css +35 -0
  105. package/dist/templates/native-go/ui/app.js +94 -0
  106. package/dist/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
  107. package/dist/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
  108. package/dist/templates/native-go-better-auth/node/app.mjs +72 -0
  109. package/dist/templates/native-go-better-auth/node/build.mjs +2 -0
  110. package/dist/templates/native-go-better-auth/node/package-lock.json +862 -0
  111. package/dist/templates/native-go-better-auth/node/package.json +1 -0
  112. package/dist/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
  113. package/dist/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
  114. package/dist/templates/native-go-integration/backend/internal/app/personal.go +203 -0
  115. package/dist/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
  116. package/dist/templates/native-go-root/backend/internal/app/root.go +107 -0
  117. package/dist/templates/native-go-root/backend/internal/app/root_test.go +124 -0
  118. package/dist/templates/native-go-root/root/whoami.sh +6 -0
  119. package/dist/templates/native-go-vue/README.md +110 -0
  120. package/dist/templates/native-go-vue/frontend/DESIGN.md +78 -0
  121. package/dist/templates/native-go-vue/frontend/package-lock.json +2507 -0
  122. package/dist/templates/native-go-vue/frontend/package.json +6 -0
  123. package/dist/templates/native-go-vue/frontend/src/App.vue +132 -0
  124. package/dist/templates/native-go-vue/frontend/src/context.ts +17 -0
  125. package/dist/templates/native-go-vue/frontend/src/main.ts +26 -0
  126. package/dist/templates/native-go-vue/frontend/src/style.css +14 -0
  127. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
  128. package/dist/templates/native-go-vue/frontend/tsconfig.json +1 -0
  129. package/dist/templates/native-go-vue/frontend/vite.config.ts +8 -0
  130. package/dist/templates/native-go-vue/gitignore +2 -0
  131. package/dist/types.d.ts +1 -1
  132. package/dist/validate.js +11 -7
  133. package/package.json +3 -7
  134. package/schema/synology-app.schema.json +36 -37
  135. package/skills/SKILL.md +4 -48
  136. package/skills/reference/create.md +11 -83
  137. package/skills/reference/deploy.md +7 -78
  138. package/skills/reference/diagnose.md +4 -101
  139. package/skills/reference/files.md +5 -41
  140. package/skills/reference/identity.md +3 -29
  141. package/skills/reference/wrap.md +0 -2
  142. package/templates/binary/synology-app.yaml +7 -0
  143. package/templates/hello-files/compose.yaml +0 -1
  144. package/templates/hello-web/compose.yaml +13 -10
  145. package/templates/hello-web/html/app.js +1 -23
  146. package/templates/hello-web/html/index.html +1 -3
  147. package/templates/hello-web-page/compose.yaml +13 -10
  148. package/templates/hello-web-page/html/app.js +1 -23
  149. package/templates/hello-web-page/html/index.html +1 -3
  150. package/templates/native-go/README.md +268 -0
  151. package/templates/native-go/backend/cmd/app/main.go +49 -0
  152. package/templates/native-go/backend/cmd/helper/main.go +38 -0
  153. package/templates/native-go/backend/go.mod +3 -0
  154. package/templates/native-go/backend/internal/api/errors.go +33 -0
  155. package/templates/native-go/backend/internal/api/registry.go +94 -0
  156. package/templates/native-go/backend/internal/api/registry_test.go +329 -0
  157. package/templates/native-go/backend/internal/api/server.go +155 -0
  158. package/templates/native-go/backend/internal/app/root_none.go +9 -0
  159. package/templates/native-go/backend/internal/app/routes.go +127 -0
  160. package/templates/native-go/backend/internal/app/routes_test.go +167 -0
  161. package/templates/native-go/backend/internal/dsm/auth.go +118 -0
  162. package/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
  163. package/templates/native-go/backend/internal/helper/control.go +219 -0
  164. package/templates/native-go/backend/internal/helper/control_test.go +242 -0
  165. package/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
  166. package/templates/native-go/backend/internal/helper/peer_other.go +7 -0
  167. package/templates/native-go/backend/internal/helper/process_linux.go +167 -0
  168. package/templates/native-go/backend/internal/helper/process_other.go +5 -0
  169. package/templates/native-go/backend/internal/helper/server_linux.go +83 -0
  170. package/templates/native-go/backend/internal/helper/status.go +271 -0
  171. package/templates/native-go/backend/internal/helper/status_test.go +205 -0
  172. package/templates/native-go/backend/internal/helper/supervision.go +84 -0
  173. package/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
  174. package/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
  175. package/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
  176. package/templates/native-go/dsm-package.yaml +58 -0
  177. package/templates/native-go/gitignore +1 -0
  178. package/templates/native-go/i18n/glossary.json +662 -0
  179. package/templates/native-go/i18n/messages/de.json +28 -0
  180. package/templates/native-go/i18n/messages/en.json +28 -0
  181. package/templates/native-go/i18n/messages/es.json +28 -0
  182. package/templates/native-go/i18n/messages/fr.json +28 -0
  183. package/templates/native-go/i18n/messages/it.json +28 -0
  184. package/templates/native-go/i18n/messages/ja.json +28 -0
  185. package/templates/native-go/i18n/messages/ko.json +28 -0
  186. package/templates/native-go/i18n/messages/zh-CN.json +28 -0
  187. package/templates/native-go/i18n/messages/zh-TW.json +28 -0
  188. package/templates/native-go/ui/app.css +35 -0
  189. package/templates/native-go/ui/app.js +94 -0
  190. package/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
  191. package/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
  192. package/templates/native-go-better-auth/node/app.mjs +72 -0
  193. package/templates/native-go-better-auth/node/build.mjs +2 -0
  194. package/templates/native-go-better-auth/node/package-lock.json +862 -0
  195. package/templates/native-go-better-auth/node/package.json +1 -0
  196. package/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
  197. package/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
  198. package/templates/native-go-integration/backend/internal/app/personal.go +203 -0
  199. package/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
  200. package/templates/native-go-root/backend/internal/app/root.go +107 -0
  201. package/templates/native-go-root/backend/internal/app/root_test.go +124 -0
  202. package/templates/native-go-root/root/whoami.sh +6 -0
  203. package/templates/native-go-vue/README.md +110 -0
  204. package/templates/native-go-vue/frontend/DESIGN.md +78 -0
  205. package/templates/native-go-vue/frontend/package-lock.json +2507 -0
  206. package/templates/native-go-vue/frontend/package.json +6 -0
  207. package/templates/native-go-vue/frontend/src/App.vue +132 -0
  208. package/templates/native-go-vue/frontend/src/context.ts +17 -0
  209. package/templates/native-go-vue/frontend/src/main.ts +26 -0
  210. package/templates/native-go-vue/frontend/src/style.css +14 -0
  211. package/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
  212. package/templates/native-go-vue/frontend/tsconfig.json +1 -0
  213. package/templates/native-go-vue/frontend/vite.config.ts +8 -0
  214. package/templates/native-go-vue/gitignore +2 -0
  215. package/dist/attestation/app-package.d.ts +0 -86
  216. package/dist/attestation/app-package.js +0 -206
  217. package/dist/attestation/install-on-host.d.ts +0 -14
  218. package/dist/attestation/install-on-host.js +0 -52
  219. package/dist/attestation/keyring.d.ts +0 -21
  220. package/dist/attestation/keyring.js +0 -168
  221. package/dist/attestation/platform-service.d.ts +0 -70
  222. package/dist/attestation/platform-service.js +0 -250
  223. package/dist/build-spk/broker-glue.d.ts +0 -16
  224. package/dist/build-spk/broker-glue.js +0 -40
  225. package/dist/deploy/catalog-publish.d.ts +0 -3
  226. package/dist/deploy/catalog-publish.js +0 -7
  227. package/dist/doctor.d.ts +0 -22
  228. package/dist/doctor.js +0 -822
  229. package/dist/error-collector.d.ts +0 -2
  230. package/dist/error-collector.js +0 -46
  231. package/dist/host/broker-paths.d.ts +0 -1
  232. package/dist/host/broker-paths.js +0 -1
  233. package/dist/host/broker-upgrade-preflight.d.ts +0 -61
  234. package/dist/host/broker-upgrade-preflight.js +0 -156
  235. package/dist/host/digests.d.ts +0 -5
  236. package/dist/host/digests.js +0 -63
  237. package/dist/host/fake-nas.d.ts +0 -49
  238. package/dist/host/fake-nas.js +0 -193
  239. package/dist/mock-broker/bootstrap.d.ts +0 -2
  240. package/dist/mock-broker/bootstrap.js +0 -8
  241. package/dist/mock-broker/files.d.ts +0 -60
  242. package/dist/mock-broker/files.js +0 -408
  243. package/dist/mock-broker/oidc.d.ts +0 -31
  244. package/dist/mock-broker/oidc.js +0 -161
  245. package/dist/mock-broker/path.d.ts +0 -9
  246. package/dist/mock-broker/path.js +0 -50
  247. package/dist/mock-broker/server.d.ts +0 -19
  248. package/dist/mock-broker/server.js +0 -350
  249. package/dist/publish/build-ledger.d.ts +0 -22
  250. package/dist/publish/build-ledger.js +0 -26
  251. package/dist/publish/community-path.d.ts +0 -6
  252. package/dist/publish/community-path.js +0 -41
  253. package/dist/publish/curated-release.d.ts +0 -78
  254. package/dist/publish/curated-release.js +0 -883
  255. package/dist/publish/file-build-ledger.d.ts +0 -18
  256. package/dist/publish/file-build-ledger.js +0 -268
  257. package/dist/publish/github-pr.d.ts +0 -25
  258. package/dist/publish/github-pr.js +0 -85
  259. package/dist/publish/github-repo.d.ts +0 -8
  260. package/dist/publish/github-repo.js +0 -21
  261. package/dist/publish/platform-service-release.d.ts +0 -60
  262. package/dist/publish/platform-service-release.js +0 -333
  263. package/dist/publish/preflight.d.ts +0 -24
  264. package/dist/publish/preflight.js +0 -138
  265. package/dist/publish/publish.d.ts +0 -27
  266. package/dist/publish/publish.js +0 -136
  267. package/dist/publish/sync-definition.d.ts +0 -4
  268. package/dist/publish/sync-definition.js +0 -48
  269. package/dist/release-bin.d.ts +0 -2
  270. package/dist/release-bin.js +0 -23
  271. package/dist/templates/hello-web/html/sdk.js +0 -68
  272. package/dist/templates/hello-web-page/html/sdk.js +0 -68
  273. package/dist/templates/native/synology-app.yaml +0 -7
  274. package/dist/test-host.d.ts +0 -15
  275. package/dist/test-host.js +0 -37
  276. package/templates/hello-web/html/sdk.js +0 -68
  277. package/templates/hello-web-page/html/sdk.js +0 -68
  278. package/templates/native/synology-app.yaml +0 -7
  279. /package/dist/templates/{native → binary}/native.yaml +0 -0
  280. /package/templates/{native → binary}/native.yaml +0 -0
@@ -0,0 +1,101 @@
1
+ import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export const ROOT_OPS_DIR = "root";
4
+ export const ROOT_OP_END = "SYNOPKGLAND_ROOT_OP_END";
5
+ const MAX_ROOT_OP_BYTES = 16 * 1024;
6
+ const OP_FILE = /^([a-z][a-z0-9_]{0,31})\.sh$/;
7
+ export function rootTaskName(packageId, op) {
8
+ return `${packageId} root: ${op}`;
9
+ }
10
+ /**
11
+ * The fixed start of an operation's task text. It does not carry the version,
12
+ * so the window can tell its own outdated task from someone else's.
13
+ */
14
+ export function rootTaskHeader(packageId, op) {
15
+ return `#!/bin/sh\n# synopkgland root operation "${op}" of ${packageId} `;
16
+ }
17
+ /** Root-owned tmpfs folder the tasks write to; the package account can only read it. */
18
+ export function rootOutputDir(packageId) {
19
+ return `/run/synopkgland-${packageId}`;
20
+ }
21
+ export function loadRootOps(dir) {
22
+ const names = existsSync(dir) ? readdirSync(dir).sort() : [];
23
+ if (names.length === 0) {
24
+ return {
25
+ ok: false,
26
+ errors: [{ code: "STANDALONE_ROOT_OPS_MISSING", message: `privilege: root 需要 ${ROOT_OPS_DIR}/<名稱>.sh,至少一個 root 操作。` }],
27
+ };
28
+ }
29
+ const errors = [];
30
+ const ops = [];
31
+ for (const file of names) {
32
+ const op = readRootOp(join(dir, file), file);
33
+ if ("error" in op) {
34
+ errors.push({ code: "STANDALONE_ROOT_OP_INVALID", message: `${ROOT_OPS_DIR}/${file}:${op.error}` });
35
+ }
36
+ else {
37
+ ops.push(op);
38
+ }
39
+ }
40
+ return errors.length === 0 ? { ok: true, ops } : { ok: false, errors };
41
+ }
42
+ function readRootOp(path, file) {
43
+ const name = OP_FILE.exec(file)?.[1];
44
+ if (!name) {
45
+ return { error: "檔名必須是小寫英數與底線組成的 <名稱>.sh(最多 32 字元),此資料夾不放其他檔案。" };
46
+ }
47
+ const stat = lstatSync(path);
48
+ if (!stat.isFile()) {
49
+ return { error: "必須是一般檔案(不可是 symlink 或資料夾)。" };
50
+ }
51
+ if (stat.size > MAX_ROOT_OP_BYTES) {
52
+ return { error: `不可超過 ${MAX_ROOT_OP_BYTES} bytes。` };
53
+ }
54
+ const body = readFileSync(path, "utf8");
55
+ if (!body.startsWith("#!/bin/sh\n") || body.includes("\0") || body.includes("�")) {
56
+ return { error: "必須是以 #!/bin/sh 開頭的 UTF-8 文字 script。" };
57
+ }
58
+ if (body.split("\n").includes(ROOT_OP_END)) {
59
+ return { error: `不可包含單獨一行的 ${ROOT_OP_END}。` };
60
+ }
61
+ return { name, body };
62
+ }
63
+ /**
64
+ * The exact text of the DSM task. The body runs from a quoted here-document
65
+ * (no expansion) and its stdout replaces `<op>.out` in the root-owned run
66
+ * folder. The package version is part of the text, so every upgrade changes
67
+ * what the administrator approved and needs approval again.
68
+ */
69
+ export function renderRootTaskScript(manifest, op) {
70
+ const body = op.body.endsWith("\n") ? op.body : `${op.body}\n`;
71
+ return `${rootTaskHeader(manifest.package, op.name)}${manifest.spkVersion}.
72
+ # Approved by a DSM administrator; runs only when this task is run.
73
+ set -eu
74
+ umask 022
75
+ PATH=/usr/sbin:/usr/bin:/sbin:/bin
76
+ export PATH
77
+ dir=${rootOutputDir(manifest.package)}
78
+ ${op.protectedOutput ? `[ "$(id -u)" = 0 ]
79
+ [ ! -L /run ] && [ "$(stat -c %u /run)" = 0 ]
80
+ [ ! -L "$dir" ]
81
+ if [ ! -e "$dir" ]; then mkdir -m 0755 "$dir"; fi
82
+ [ -d "$dir" ] && [ "$(stat -c %u "$dir")" = 0 ]
83
+ output_mode=$(stat -c %a "$dir")
84
+ [ "$((0$output_mode & 0022))" = 0 ]
85
+ ` : `if [ -L "$dir" ] || [ ! -d "$dir" ]; then
86
+ rm -f "$dir"
87
+ mkdir -m 0755 "$dir"
88
+ fi
89
+ `}tmp=$(mktemp "$dir/.${op.name}.XXXXXX")
90
+ if /bin/sh -eu >"$tmp" <<'${ROOT_OP_END}'
91
+ ${body}${ROOT_OP_END}
92
+ then
93
+ chmod 0644 "$tmp"
94
+ mv -f "$tmp" "$dir/${op.name}.out"
95
+ else
96
+ status=$?
97
+ rm -f "$tmp"
98
+ exit "$status"
99
+ fi
100
+ `;
101
+ }
@@ -0,0 +1,35 @@
1
+ import { type RootOp } from "./root-ops.js";
2
+ /** What the window embeds per root operation: the task it may create, and how to recognize it. */
3
+ export interface RootTask {
4
+ op: string;
5
+ name: string;
6
+ header: string;
7
+ script: string;
8
+ }
9
+ export declare function rootTasks(manifest: {
10
+ package: string;
11
+ spkVersion: string;
12
+ rootOps?: RootOp[];
13
+ }): RootTask[];
14
+ /**
15
+ * `createRootAccess(options)` → `{ state, authorize, run, remove }`, plain
16
+ * ES5 for DSM's desktop. Every DSM call goes through `options.send` (the
17
+ * AppWindow's sendWebAPI) as the signed-in administrator:
18
+ *
19
+ * - authorize: DSM's own password confirmation, then one disabled root task
20
+ * with exactly the embedded script. The one-use token goes into that single
21
+ * create call and is never kept.
22
+ * - run: re-reads the task and runs it only if it is still exactly ours, then
23
+ * waits for its history entry and asks the backend for the output.
24
+ * - an older version of our task is "outdated" and never runs; authorizing
25
+ * again replaces it. A same-named task that is not ours is a "conflict" and
26
+ * is never run, replaced or deleted.
27
+ */
28
+ export declare const ROOT_RUNTIME = "function createRootAccess(options) {\n var API = \"SYNO.Core.TaskScheduler\";\n var POLL_MS = 500, POLLS = 60;\n var byOp = {};\n options.tasks.forEach(function (task) { byOp[task.op] = task; });\n\n function fail(code) {\n var error = new Error(code);\n error.code = code;\n return error;\n }\n function begin(op) {\n if (!Object.prototype.hasOwnProperty.call(byOp, op)) return Promise.reject(fail(\"root_unknown_op\"));\n if (options.user().admin !== true) return Promise.reject(fail(\"admin_required\"));\n return Promise.resolve(byOp[op]);\n }\n function isRootTask(summary) {\n return summary.owner === \"root\" && summary.real_owner === \"root\" && summary.enable === false && summary.type === \"script\";\n }\n function find(task) {\n return options.send(API, \"list\", 3, { start: 0, limit: 500 }).then(function (data) {\n if (!data || !Array.isArray(data.tasks)) throw fail(\"root_bad_response\");\n var named = data.tasks.filter(function (summary) { return summary && summary.name === task.name; });\n if (named.length === 0) return { state: \"missing\" };\n if (named.length > 1 || !isRootTask(named[0])) return { state: \"conflict\" };\n var ref = { id: named[0].id, real_owner: named[0].real_owner };\n return options.send(API, \"get\", 4, ref).then(function (detail) {\n var script = detail && detail.extra && detail.extra.script;\n if (!detail || String(detail.id) !== String(ref.id) || detail.name !== task.name\n || detail.owner !== \"root\" || detail.enable !== false || detail.type !== \"script\" || typeof script !== \"string\") {\n return { state: \"conflict\" };\n }\n if (script === task.script) return { state: \"ready\", ref: ref };\n if (script.indexOf(task.header) === 0) return { state: \"outdated\", ref: ref };\n return { state: \"conflict\" };\n });\n });\n }\n function definition(task, user) {\n var hours = [];\n for (var h = 1; h <= 23; h++) hours.push(h);\n return {\n name: task.name,\n real_owner: user,\n owner: \"root\",\n enable: false,\n type: \"script\",\n schedule: {\n date_type: 0, week_day: \"0,1,2,3,4,5,6\", repeat_date: 1001, monthly_week: [], hour: 0, minute: 0,\n repeat_hour: 0, repeat_min: 0, last_work_hour: 0,\n repeat_min_store_config: [1, 5, 10, 15, 20, 30], repeat_hour_store_config: hours\n },\n extra: { notify_enable: false, script: task.script, notify_mail: \"\", notify_if_error: false }\n };\n }\n function newest(history) {\n if (!Array.isArray(history)) throw fail(\"root_bad_response\");\n return history.reduce(function (latest, entry) {\n return entry && (!latest || Number(entry.timestamp) > Number(latest.timestamp)) ? entry : latest;\n }, null);\n }\n function history(ref) {\n return options.send(API, \"get_history_status_list\", 1, { id: ref.id }).then(newest);\n }\n function waitForRun(ref, before, polls) {\n return history(ref).then(function (latest) {\n var finished = latest && (!before || latest.timestamp !== before.timestamp)\n && latest.exit_type !== \"running\" && latest.stop_time;\n if (finished) {\n if (latest.exit_code !== 0) throw fail(\"root_task_failed\");\n return;\n }\n if (polls <= 1) throw fail(\"root_timeout\");\n return options.wait(POLL_MS).then(function () { return waitForRun(ref, before, polls - 1); });\n });\n }\n\n return {\n state: function (op) {\n return begin(op).then(find).then(function (found) { return found.state; });\n },\n authorize: function (op) {\n return begin(op).then(function (task) {\n return find(task).then(function (found) {\n if (found.state === \"ready\") return \"ready\";\n if (found.state === \"conflict\") throw fail(\"root_conflict\");\n return options.confirmPassword().then(function (token) {\n if (!token) return \"cancelled\";\n var params = definition(task, options.user().name);\n params.SynoConfirmPWToken = token;\n var replaced = found.state === \"outdated\"\n ? options.send(API, \"delete\", 2, { tasks: [found.ref] })\n : Promise.resolve();\n return replaced.then(function () {\n return options.send(API + \".Root\", \"create\", 4, params);\n }).then(function () {\n return find(task);\n }).then(function (after) {\n if (after.state !== \"ready\") throw fail(\"root_verify_failed\");\n return \"ready\";\n });\n });\n });\n });\n },\n run: function (op) {\n return begin(op).then(function (task) {\n return find(task).then(function (found) {\n if (found.state !== \"ready\") throw fail(\"root_not_ready\");\n return history(found.ref).then(function (before) {\n return options.send(API, \"run\", 2, { tasks: [found.ref] }).then(function () {\n return waitForRun(found.ref, before, POLLS);\n });\n });\n }).then(function () {\n return options.call(\"root.output\", { op: op });\n });\n });\n },\n remove: function (op) {\n return begin(op).then(function (task) {\n return find(task).then(function (found) {\n if (found.state === \"missing\") return \"missing\";\n if (found.state === \"conflict\") throw fail(\"root_conflict\");\n return options.send(API, \"delete\", 2, { tasks: [found.ref] }).then(function () {\n return find(task);\n }).then(function (after) {\n if (after.state !== \"missing\") throw fail(\"root_verify_failed\");\n return \"missing\";\n });\n });\n });\n },\n output: function (op) {\n return begin(op).then(function () {\n return options.call(\"root.output\", { op: op });\n });\n }\n };\n}";
29
+ /**
30
+ * The authorization panel the window shows above the app for each root
31
+ * operation, identical in every package. `rootPanelView(model)` decides what
32
+ * it shows; `mountRootPanel(host, access, i18n, tasks, admin)` draws it with
33
+ * DOM text nodes only (no HTML from data).
34
+ */
35
+ export declare const ROOT_PANEL = "function rootPanelShowsResult(state) {\n return state === \"ready\" || state === \"outdated\";\n}\n\nfunction rootPanelView(model) {\n if (!model.admin) return { status: null, buttons: [], message: { key: \"admin_only\" } };\n function buttons(actions) {\n return actions.map(function (action) { return { action: action, disabled: model.busy === true }; });\n }\n if (model.error) {\n return {\n status: \"state.\" + model.state, buttons: buttons([\"retry\"]),\n message: { key: \"error.\" + model.error, params: { name: model.name, code: model.error } }\n };\n }\n var message = model.notice ? { key: model.notice } : null;\n switch (model.state) {\n case \"missing\":\n return { status: \"state.missing\", buttons: buttons([\"authorize\"]), message: message };\n case \"outdated\":\n return { status: \"state.outdated\", buttons: buttons([\"authorize\", \"remove\"]), message: message };\n case \"ready\":\n return { status: \"state.ready\", buttons: buttons([\"run\", \"remove\"]), message: message };\n case \"conflict\":\n return { status: \"state.conflict\", buttons: buttons([\"retry\"]), message: { key: \"error.root_conflict\", params: { name: model.name } } };\n default:\n return { status: \"state.checking\", buttons: [], message: null };\n }\n}\n\nfunction mountRootPanel(host, access, i18n, tasks, admin) {\n var t = i18n.t;\n function el(tag, className, text) {\n var node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n }\n function say(message) {\n if (!message) return \"\";\n var text = t(message.key, message.params);\n return text === message.key && message.key.indexOf(\"error.\") === 0 ? t(\"error.default\", message.params) : text;\n }\n tasks.forEach(function (task) {\n var model = { admin: admin, name: task.name, state: \"checking\", busy: false, error: null, notice: null };\n var result = null;\n var section = el(\"section\", \"dsmapp-rootpanel\");\n host.appendChild(section);\n\n function render() {\n var view = rootPanelView(model);\n while (section.firstChild) section.removeChild(section.firstChild);\n section.appendChild(el(\"h2\", null, t(\"title\")));\n section.appendChild(el(\"p\", null, t(\"intro\", { op: task.op })));\n if (view.status) {\n var facts = el(\"dl\");\n facts.appendChild(el(\"dt\", null, t(\"status\")));\n facts.appendChild(el(\"dd\", \"dsmapp-rootpanel-state\", t(view.status, { name: task.name })));\n facts.appendChild(el(\"dt\", null, t(\"last_run\")));\n facts.appendChild(el(\"dd\", null, result && result.modified\n ? new Date(result.modified * 1000).toLocaleString(i18n.locale) : t(\"never\")));\n section.appendChild(facts);\n }\n if (view.message) section.appendChild(el(\"p\", \"dsmapp-rootpanel-message\", say(view.message)));\n if (result && typeof result.output === \"string\") {\n section.appendChild(el(\"div\", \"dsmapp-rootpanel-label\", t(\"output\")));\n section.appendChild(el(\"pre\", \"dsmapp-rootpanel-output\", result.output));\n }\n var actions = el(\"div\", \"dsmapp-rootpanel-actions\");\n view.buttons.forEach(function (button) {\n var node = el(\"button\", button.action === \"remove\" ? null : \"dsmapp-rootpanel-primary\", t(button.action));\n node.type = \"button\";\n node.disabled = button.disabled;\n node.onclick = function () { act(button.action); };\n actions.appendChild(node);\n });\n section.appendChild(actions);\n if (admin && rootPanelShowsResult(model.state)) section.appendChild(el(\"p\", \"dsmapp-rootpanel-hint\", t(\"uninstall_hint\")));\n }\n function settle(promise, notice) {\n model.busy = true;\n model.error = null;\n model.notice = null;\n render();\n return promise.then(function (value) {\n model.notice = typeof notice === \"function\" ? notice(value) : notice;\n }, function (error) {\n model.error = (error && error.code) || \"unknown\";\n }).then(function () {\n model.busy = false;\n render();\n });\n }\n function refresh(notice) {\n return settle(access.state(task.op).then(function (state) {\n model.state = state;\n if (!rootPanelShowsResult(state)) return;\n return access.output(task.op).then(function (out) { result = out; });\n }), notice);\n }\n function refreshAfterChange() {\n return model.error ? null : refresh(model.notice);\n }\n function act(action) {\n if (action === \"retry\") return refresh(null);\n if (action === \"authorize\") {\n return settle(access.authorize(task.op), function (outcome) {\n return outcome === \"cancelled\" ? \"cancelled\" : null;\n }).then(refreshAfterChange);\n }\n if (action === \"run\") {\n return settle(access.run(task.op).then(function (out) { result = out; }), \"completed\");\n }\n if (action === \"remove\") {\n return settle(access.remove(task.op).then(function () { result = null; }), null).then(refreshAfterChange);\n }\n }\n render();\n if (admin) refresh(null);\n });\n}";
@@ -0,0 +1,290 @@
1
+ import { renderRootTaskScript, rootTaskHeader, rootTaskName } from "./root-ops.js";
2
+ export function rootTasks(manifest) {
3
+ return (manifest.rootOps ?? []).map((op) => ({
4
+ op: op.name,
5
+ name: rootTaskName(manifest.package, op.name),
6
+ header: rootTaskHeader(manifest.package, op.name),
7
+ script: renderRootTaskScript(manifest, op),
8
+ }));
9
+ }
10
+ /**
11
+ * `createRootAccess(options)` → `{ state, authorize, run, remove }`, plain
12
+ * ES5 for DSM's desktop. Every DSM call goes through `options.send` (the
13
+ * AppWindow's sendWebAPI) as the signed-in administrator:
14
+ *
15
+ * - authorize: DSM's own password confirmation, then one disabled root task
16
+ * with exactly the embedded script. The one-use token goes into that single
17
+ * create call and is never kept.
18
+ * - run: re-reads the task and runs it only if it is still exactly ours, then
19
+ * waits for its history entry and asks the backend for the output.
20
+ * - an older version of our task is "outdated" and never runs; authorizing
21
+ * again replaces it. A same-named task that is not ours is a "conflict" and
22
+ * is never run, replaced or deleted.
23
+ */
24
+ export const ROOT_RUNTIME = `function createRootAccess(options) {
25
+ var API = "SYNO.Core.TaskScheduler";
26
+ var POLL_MS = 500, POLLS = 60;
27
+ var byOp = {};
28
+ options.tasks.forEach(function (task) { byOp[task.op] = task; });
29
+
30
+ function fail(code) {
31
+ var error = new Error(code);
32
+ error.code = code;
33
+ return error;
34
+ }
35
+ function begin(op) {
36
+ if (!Object.prototype.hasOwnProperty.call(byOp, op)) return Promise.reject(fail("root_unknown_op"));
37
+ if (options.user().admin !== true) return Promise.reject(fail("admin_required"));
38
+ return Promise.resolve(byOp[op]);
39
+ }
40
+ function isRootTask(summary) {
41
+ return summary.owner === "root" && summary.real_owner === "root" && summary.enable === false && summary.type === "script";
42
+ }
43
+ function find(task) {
44
+ return options.send(API, "list", 3, { start: 0, limit: 500 }).then(function (data) {
45
+ if (!data || !Array.isArray(data.tasks)) throw fail("root_bad_response");
46
+ var named = data.tasks.filter(function (summary) { return summary && summary.name === task.name; });
47
+ if (named.length === 0) return { state: "missing" };
48
+ if (named.length > 1 || !isRootTask(named[0])) return { state: "conflict" };
49
+ var ref = { id: named[0].id, real_owner: named[0].real_owner };
50
+ return options.send(API, "get", 4, ref).then(function (detail) {
51
+ var script = detail && detail.extra && detail.extra.script;
52
+ if (!detail || String(detail.id) !== String(ref.id) || detail.name !== task.name
53
+ || detail.owner !== "root" || detail.enable !== false || detail.type !== "script" || typeof script !== "string") {
54
+ return { state: "conflict" };
55
+ }
56
+ if (script === task.script) return { state: "ready", ref: ref };
57
+ if (script.indexOf(task.header) === 0) return { state: "outdated", ref: ref };
58
+ return { state: "conflict" };
59
+ });
60
+ });
61
+ }
62
+ function definition(task, user) {
63
+ var hours = [];
64
+ for (var h = 1; h <= 23; h++) hours.push(h);
65
+ return {
66
+ name: task.name,
67
+ real_owner: user,
68
+ owner: "root",
69
+ enable: false,
70
+ type: "script",
71
+ schedule: {
72
+ date_type: 0, week_day: "0,1,2,3,4,5,6", repeat_date: 1001, monthly_week: [], hour: 0, minute: 0,
73
+ repeat_hour: 0, repeat_min: 0, last_work_hour: 0,
74
+ repeat_min_store_config: [1, 5, 10, 15, 20, 30], repeat_hour_store_config: hours
75
+ },
76
+ extra: { notify_enable: false, script: task.script, notify_mail: "", notify_if_error: false }
77
+ };
78
+ }
79
+ function newest(history) {
80
+ if (!Array.isArray(history)) throw fail("root_bad_response");
81
+ return history.reduce(function (latest, entry) {
82
+ return entry && (!latest || Number(entry.timestamp) > Number(latest.timestamp)) ? entry : latest;
83
+ }, null);
84
+ }
85
+ function history(ref) {
86
+ return options.send(API, "get_history_status_list", 1, { id: ref.id }).then(newest);
87
+ }
88
+ function waitForRun(ref, before, polls) {
89
+ return history(ref).then(function (latest) {
90
+ var finished = latest && (!before || latest.timestamp !== before.timestamp)
91
+ && latest.exit_type !== "running" && latest.stop_time;
92
+ if (finished) {
93
+ if (latest.exit_code !== 0) throw fail("root_task_failed");
94
+ return;
95
+ }
96
+ if (polls <= 1) throw fail("root_timeout");
97
+ return options.wait(POLL_MS).then(function () { return waitForRun(ref, before, polls - 1); });
98
+ });
99
+ }
100
+
101
+ return {
102
+ state: function (op) {
103
+ return begin(op).then(find).then(function (found) { return found.state; });
104
+ },
105
+ authorize: function (op) {
106
+ return begin(op).then(function (task) {
107
+ return find(task).then(function (found) {
108
+ if (found.state === "ready") return "ready";
109
+ if (found.state === "conflict") throw fail("root_conflict");
110
+ return options.confirmPassword().then(function (token) {
111
+ if (!token) return "cancelled";
112
+ var params = definition(task, options.user().name);
113
+ params.SynoConfirmPWToken = token;
114
+ var replaced = found.state === "outdated"
115
+ ? options.send(API, "delete", 2, { tasks: [found.ref] })
116
+ : Promise.resolve();
117
+ return replaced.then(function () {
118
+ return options.send(API + ".Root", "create", 4, params);
119
+ }).then(function () {
120
+ return find(task);
121
+ }).then(function (after) {
122
+ if (after.state !== "ready") throw fail("root_verify_failed");
123
+ return "ready";
124
+ });
125
+ });
126
+ });
127
+ });
128
+ },
129
+ run: function (op) {
130
+ return begin(op).then(function (task) {
131
+ return find(task).then(function (found) {
132
+ if (found.state !== "ready") throw fail("root_not_ready");
133
+ return history(found.ref).then(function (before) {
134
+ return options.send(API, "run", 2, { tasks: [found.ref] }).then(function () {
135
+ return waitForRun(found.ref, before, POLLS);
136
+ });
137
+ });
138
+ }).then(function () {
139
+ return options.call("root.output", { op: op });
140
+ });
141
+ });
142
+ },
143
+ remove: function (op) {
144
+ return begin(op).then(function (task) {
145
+ return find(task).then(function (found) {
146
+ if (found.state === "missing") return "missing";
147
+ if (found.state === "conflict") throw fail("root_conflict");
148
+ return options.send(API, "delete", 2, { tasks: [found.ref] }).then(function () {
149
+ return find(task);
150
+ }).then(function (after) {
151
+ if (after.state !== "missing") throw fail("root_verify_failed");
152
+ return "missing";
153
+ });
154
+ });
155
+ });
156
+ },
157
+ output: function (op) {
158
+ return begin(op).then(function () {
159
+ return options.call("root.output", { op: op });
160
+ });
161
+ }
162
+ };
163
+ }`;
164
+ /**
165
+ * The authorization panel the window shows above the app for each root
166
+ * operation, identical in every package. `rootPanelView(model)` decides what
167
+ * it shows; `mountRootPanel(host, access, i18n, tasks, admin)` draws it with
168
+ * DOM text nodes only (no HTML from data).
169
+ */
170
+ export const ROOT_PANEL = `function rootPanelShowsResult(state) {
171
+ return state === "ready" || state === "outdated";
172
+ }
173
+
174
+ function rootPanelView(model) {
175
+ if (!model.admin) return { status: null, buttons: [], message: { key: "admin_only" } };
176
+ function buttons(actions) {
177
+ return actions.map(function (action) { return { action: action, disabled: model.busy === true }; });
178
+ }
179
+ if (model.error) {
180
+ return {
181
+ status: "state." + model.state, buttons: buttons(["retry"]),
182
+ message: { key: "error." + model.error, params: { name: model.name, code: model.error } }
183
+ };
184
+ }
185
+ var message = model.notice ? { key: model.notice } : null;
186
+ switch (model.state) {
187
+ case "missing":
188
+ return { status: "state.missing", buttons: buttons(["authorize"]), message: message };
189
+ case "outdated":
190
+ return { status: "state.outdated", buttons: buttons(["authorize", "remove"]), message: message };
191
+ case "ready":
192
+ return { status: "state.ready", buttons: buttons(["run", "remove"]), message: message };
193
+ case "conflict":
194
+ return { status: "state.conflict", buttons: buttons(["retry"]), message: { key: "error.root_conflict", params: { name: model.name } } };
195
+ default:
196
+ return { status: "state.checking", buttons: [], message: null };
197
+ }
198
+ }
199
+
200
+ function mountRootPanel(host, access, i18n, tasks, admin) {
201
+ var t = i18n.t;
202
+ function el(tag, className, text) {
203
+ var node = document.createElement(tag);
204
+ if (className) node.className = className;
205
+ if (text !== undefined) node.textContent = text;
206
+ return node;
207
+ }
208
+ function say(message) {
209
+ if (!message) return "";
210
+ var text = t(message.key, message.params);
211
+ return text === message.key && message.key.indexOf("error.") === 0 ? t("error.default", message.params) : text;
212
+ }
213
+ tasks.forEach(function (task) {
214
+ var model = { admin: admin, name: task.name, state: "checking", busy: false, error: null, notice: null };
215
+ var result = null;
216
+ var section = el("section", "dsmapp-rootpanel");
217
+ host.appendChild(section);
218
+
219
+ function render() {
220
+ var view = rootPanelView(model);
221
+ while (section.firstChild) section.removeChild(section.firstChild);
222
+ section.appendChild(el("h2", null, t("title")));
223
+ section.appendChild(el("p", null, t("intro", { op: task.op })));
224
+ if (view.status) {
225
+ var facts = el("dl");
226
+ facts.appendChild(el("dt", null, t("status")));
227
+ facts.appendChild(el("dd", "dsmapp-rootpanel-state", t(view.status, { name: task.name })));
228
+ facts.appendChild(el("dt", null, t("last_run")));
229
+ facts.appendChild(el("dd", null, result && result.modified
230
+ ? new Date(result.modified * 1000).toLocaleString(i18n.locale) : t("never")));
231
+ section.appendChild(facts);
232
+ }
233
+ if (view.message) section.appendChild(el("p", "dsmapp-rootpanel-message", say(view.message)));
234
+ if (result && typeof result.output === "string") {
235
+ section.appendChild(el("div", "dsmapp-rootpanel-label", t("output")));
236
+ section.appendChild(el("pre", "dsmapp-rootpanel-output", result.output));
237
+ }
238
+ var actions = el("div", "dsmapp-rootpanel-actions");
239
+ view.buttons.forEach(function (button) {
240
+ var node = el("button", button.action === "remove" ? null : "dsmapp-rootpanel-primary", t(button.action));
241
+ node.type = "button";
242
+ node.disabled = button.disabled;
243
+ node.onclick = function () { act(button.action); };
244
+ actions.appendChild(node);
245
+ });
246
+ section.appendChild(actions);
247
+ if (admin && rootPanelShowsResult(model.state)) section.appendChild(el("p", "dsmapp-rootpanel-hint", t("uninstall_hint")));
248
+ }
249
+ function settle(promise, notice) {
250
+ model.busy = true;
251
+ model.error = null;
252
+ model.notice = null;
253
+ render();
254
+ return promise.then(function (value) {
255
+ model.notice = typeof notice === "function" ? notice(value) : notice;
256
+ }, function (error) {
257
+ model.error = (error && error.code) || "unknown";
258
+ }).then(function () {
259
+ model.busy = false;
260
+ render();
261
+ });
262
+ }
263
+ function refresh(notice) {
264
+ return settle(access.state(task.op).then(function (state) {
265
+ model.state = state;
266
+ if (!rootPanelShowsResult(state)) return;
267
+ return access.output(task.op).then(function (out) { result = out; });
268
+ }), notice);
269
+ }
270
+ function refreshAfterChange() {
271
+ return model.error ? null : refresh(model.notice);
272
+ }
273
+ function act(action) {
274
+ if (action === "retry") return refresh(null);
275
+ if (action === "authorize") {
276
+ return settle(access.authorize(task.op), function (outcome) {
277
+ return outcome === "cancelled" ? "cancelled" : null;
278
+ }).then(refreshAfterChange);
279
+ }
280
+ if (action === "run") {
281
+ return settle(access.run(task.op).then(function (out) { result = out; }), "completed");
282
+ }
283
+ if (action === "remove") {
284
+ return settle(access.remove(task.op).then(function () { result = null; }), null).then(refreshAfterChange);
285
+ }
286
+ }
287
+ render();
288
+ if (admin) refresh(null);
289
+ });
290
+ }`;
@@ -0,0 +1,22 @@
1
+ import { type NativeArchFamily } from "../build-spk/dsm-arch.js";
2
+ import type { StandaloneManifest } from "./manifest.js";
3
+ import type { HelperArtifact } from "./helper.js";
4
+ export declare const LIFECYCLE_SCRIPTS: readonly ["preinst", "postinst", "preuninst", "postuninst", "preupgrade", "postupgrade", "start-stop-status"];
5
+ export declare class StandaloneBuildError extends Error {
6
+ readonly code: string;
7
+ constructor(code: string, message: string);
8
+ }
9
+ export interface AssembleOptions {
10
+ manifest: StandaloneManifest;
11
+ arch: NativeArchFamily;
12
+ /** The compiled or prebuilt CGI executable, already checked. */
13
+ cgiPath: string;
14
+ outputPath: string;
15
+ helper?: HelperArtifact;
16
+ }
17
+ /**
18
+ * Packs an unsigned Standalone Package SPK. Nothing in it runs as root: the
19
+ * package account owns the payload, lifecycle scripts do nothing, and the
20
+ * only entry point is the CGI DSM runs behind its own login.
21
+ */
22
+ export declare function assembleStandaloneSpk(options: AssembleOptions): Promise<void>;