@synopackageland/cli 0.13.1 → 0.15.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 (119) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +44 -1
  3. package/dist/build-spk/generator.d.ts +3 -2
  4. package/dist/build-spk/generator.js +6 -850
  5. package/dist/build-spk/resource-worker.d.ts +2 -14
  6. package/dist/build-spk/resource-worker.js +3 -166
  7. package/dist/cli.js +208 -56
  8. package/dist/compose/analyze.d.ts +3 -61
  9. package/dist/compose/analyze.js +35 -776
  10. package/dist/compose/env.d.ts +5 -3
  11. package/dist/compose/env.js +17 -25
  12. package/dist/compose/runner.js +23 -6
  13. package/dist/contract/app-launch.d.ts +11 -0
  14. package/dist/contract/app-launch.js +18 -0
  15. package/dist/contract/web-launch.d.ts +3 -13
  16. package/dist/contract/web-launch.js +14 -81
  17. package/dist/ctl/cli.d.ts +36 -0
  18. package/dist/ctl/cli.js +218 -0
  19. package/dist/ctl/descriptor.d.ts +81 -0
  20. package/dist/ctl/descriptor.js +93 -0
  21. package/dist/ctl/dsm-auth.d.ts +10 -0
  22. package/dist/ctl/dsm-auth.js +30 -0
  23. package/dist/ctl/errors.d.ts +17 -0
  24. package/dist/ctl/errors.js +32 -0
  25. package/dist/ctl/flows.d.ts +11 -0
  26. package/dist/ctl/flows.js +52 -0
  27. package/dist/ctl/input.d.ts +20 -0
  28. package/dist/ctl/input.js +128 -0
  29. package/dist/ctl/json.d.ts +2 -0
  30. package/dist/ctl/json.js +38 -0
  31. package/dist/ctl/redaction.d.ts +15 -0
  32. package/dist/ctl/redaction.js +198 -0
  33. package/dist/ctl/service.d.ts +38 -0
  34. package/dist/ctl/service.js +156 -0
  35. package/dist/ctl/store.d.ts +51 -0
  36. package/dist/ctl/store.js +93 -0
  37. package/dist/ctl/transfers.d.ts +14 -0
  38. package/dist/ctl/transfers.js +74 -0
  39. package/dist/ctl/transport.d.ts +11 -0
  40. package/dist/ctl/transport.js +79 -0
  41. package/dist/deploy/deploy.d.ts +4 -3
  42. package/dist/deploy/deploy.js +5 -210
  43. package/dist/deploy/plan.js +26 -7
  44. package/dist/dev.js +5 -4
  45. package/dist/error-registry.js +1020 -831
  46. package/dist/info.js +120 -28
  47. package/dist/init.js +188 -64
  48. package/dist/inspect.js +21 -6
  49. package/dist/runtime/executor-digests.json +1 -12
  50. package/dist/skill.js +51 -9
  51. package/dist/standalone/api-description-inspect.d.ts +4 -0
  52. package/dist/standalone/api-description-inspect.js +12 -0
  53. package/dist/standalone/api-description.d.ts +7 -0
  54. package/dist/standalone/api-description.js +73 -0
  55. package/dist/standalone/build.d.ts +3 -0
  56. package/dist/standalone/build.js +140 -33
  57. package/dist/standalone/compose-errors.d.ts +3 -0
  58. package/dist/standalone/compose-errors.js +243 -0
  59. package/dist/standalone/compose.d.ts +97 -10
  60. package/dist/standalone/compose.js +306 -75
  61. package/dist/standalone/daemon.js +111 -29
  62. package/dist/standalone/dsm-window.js +55 -9
  63. package/dist/standalone/identity-gateway.d.ts +9 -2
  64. package/dist/standalone/identity-gateway.js +282 -66
  65. package/dist/standalone/inspect-spk.d.ts +15 -0
  66. package/dist/standalone/inspect-spk.js +714 -128
  67. package/dist/standalone/lifecycle.js +124 -42
  68. package/dist/standalone/manifest.d.ts +10 -1
  69. package/dist/standalone/manifest.js +308 -87
  70. package/dist/standalone/resource.js +6 -2
  71. package/dist/standalone/spk.d.ts +4 -1
  72. package/dist/standalone/spk.js +139 -39
  73. package/dist/template-paths.d.ts +0 -2
  74. package/dist/template-paths.js +0 -5
  75. package/dist/test-command.d.ts +1 -1
  76. package/dist/test-command.js +1 -1
  77. package/dist/validate.d.ts +3 -3
  78. package/dist/validate.js +32 -140
  79. package/package.json +5 -3
  80. package/schema/synology-app.schema.json +1 -1
  81. package/schema/synopkgland-api-v1.schema.json +423 -0
  82. package/skills/SKILL.md +34 -1
  83. package/skills/reference/compose.md +349 -0
  84. package/skills/reference/create.md +26 -4
  85. package/skills/reference/ctl.md +41 -0
  86. package/skills/reference/deploy.md +1 -1
  87. package/skills/reference/files.md +4 -0
  88. package/skills/reference/identity-gateway.md +8 -138
  89. package/skills/reference/portal.md +31 -0
  90. package/skills/reference/ui-vue.md +31 -0
  91. package/skills/reference/wrap.md +10 -30
  92. package/assets/go/daemon/fixture_linux_test.go +0 -15
  93. package/assets/go/daemon/fixture_other_test.go +0 -7
  94. package/assets/go/daemon/go.mod +0 -5
  95. package/assets/go/daemon/main.go +0 -636
  96. package/assets/go/daemon/main_test.go +0 -315
  97. package/assets/go/daemon/network.go +0 -107
  98. package/assets/go/daemon/network_test.go +0 -86
  99. package/assets/go/daemon/process_linux.go +0 -55
  100. package/assets/go/daemon/process_linux_test.go +0 -161
  101. package/assets/go/daemon/process_other.go +0 -14
  102. package/assets/go/daemon/testdata/child.go +0 -44
  103. package/assets/go/gateway/auth.go +0 -101
  104. package/assets/go/gateway/auth_test.go +0 -216
  105. package/assets/go/gateway/cgi.go +0 -133
  106. package/assets/go/gateway/cgi_test.go +0 -156
  107. package/assets/go/gateway/csrf.go +0 -73
  108. package/assets/go/gateway/csrf_test.go +0 -155
  109. package/assets/go/gateway/directory.go +0 -108
  110. package/assets/go/gateway/directory_test.go +0 -202
  111. package/assets/go/gateway/gateway.go +0 -412
  112. package/assets/go/gateway/gateway_test.go +0 -676
  113. package/assets/go/gateway/go.mod +0 -5
  114. package/assets/go/gateway/groups.go +0 -66
  115. package/assets/go/gateway/main.go +0 -45
  116. package/dist/build-spk/gateway.d.ts +0 -31
  117. package/dist/build-spk/gateway.js +0 -60
  118. package/dist/compose/privilege.d.ts +0 -14
  119. package/dist/compose/privilege.js +0 -102
@@ -1,8 +1,8 @@
1
1
  import { composeDaemon, renderComposeInstall } from "./compose.js";
2
- import { renderBackgroundControl, renderBackgroundWorkerStart, renderBackgroundWorkerStop, renderUserManagerRecovery, backgroundUnitName } from "./background.js";
2
+ import { renderBackgroundControl, renderBackgroundWorkerStart, renderBackgroundWorkerStop, renderUserManagerRecovery, backgroundUnitName, } from "./background.js";
3
3
  import { renderPackagePostuninst } from "../build-spk/native-lifecycle.js";
4
4
  import { renderSharedFolderScript } from "./shared-folder.js";
5
- import { renderDelegateInstallDraft } from '../build-spk/delegate-scope.js';
5
+ import { renderDelegateInstallDraft } from "../build-spk/delegate-scope.js";
6
6
  import { renderPortalLifecycle } from "../portal/package.js";
7
7
  import { nativePortal } from "./portal.js";
8
8
  import { renderUninstallHook } from "./uninstall-hook.js";
@@ -11,46 +11,110 @@ const UNINSTALL_ONLY = '[ "${SYNOPKG_PKG_STATUS:-}" = UPGRADE ] || synopkgland_u
11
11
  /** Package-account lifecycle hooks for CGI, daemon and background packages. */
12
12
  export function renderStandaloneScript(name, manifest) {
13
13
  if (manifest.compose) {
14
- const native = { ...manifest, compose: undefined, daemon: composeDaemon(manifest) };
15
- const body = name === "preinst" ? renderComposeInstall(manifest.package) : renderStandaloneScript(name, { ...native, appScripts: undefined });
16
- return manifest.appScripts?.some(app => app === name) ? composeAppScript(name, body) : body;
14
+ const native = {
15
+ ...manifest,
16
+ compose: undefined,
17
+ daemon: manifest.identityGateway ? composeDaemon(manifest) : undefined,
18
+ };
19
+ const body = name === "preinst"
20
+ ? renderComposeInstall(manifest.package)
21
+ : renderStandaloneScript(name, { ...native, appScripts: undefined });
22
+ const control = `"/var/packages/${manifest.package}/target/daemon/control"`;
23
+ const paths = `"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}"`;
24
+ const remember = name === "postinst" &&
25
+ manifest.compose.plan.ingress &&
26
+ !manifest.compose.plan.mounts_blocked
27
+ ? `"/var/packages/${manifest.package}/target/compose/control" remember-ingress --project "/var/packages/${manifest.package}/target/compose" --package '${manifest.package}' || exit $?\n`
28
+ : "";
29
+ const ledger = remember +
30
+ (manifest.identityGateway && !manifest.compose.plan.mounts_blocked
31
+ ? name === "start-stop-status"
32
+ ? `if [ "$1" = restart ]; then\n "$0" stop || exit $?\n exec "$0" start\nfi\nif [ "$1" = start ]; then\n ${control} acquire ${paths} || exit $?\nelif [ "$1" = stop ]; then\n ${control} release ${paths} || exit $?\nfi\n`
33
+ : ["preupgrade", "preuninst"].includes(name)
34
+ ? `${control} release ${paths} || exit $?\n`
35
+ : ""
36
+ : "");
37
+ const withLedger = ledger
38
+ ? body.replace("#!/bin/sh\n", "#!/bin/sh\n" + ledger)
39
+ : body;
40
+ return manifest.appScripts?.some((app) => app === name)
41
+ ? composeAppScript(name, withLedger)
42
+ : withLedger;
17
43
  }
18
44
  const portal = nativePortal(manifest);
19
45
  const script = renderCapabilityScript(name, manifest);
20
- const sdk = portal ? script.replace("#!/bin/sh\n", () => `#!/bin/sh\n${renderPortalLifecycle(name, portal)}`) : script;
21
- return manifest.appScripts?.some(app => app === name) ? composeAppScript(name, sdk) : sdk;
46
+ const sdk = portal
47
+ ? script.replace("#!/bin/sh\n", () => `#!/bin/sh\n${renderPortalLifecycle(name, portal)}`)
48
+ : script;
49
+ return manifest.appScripts?.some((app) => app === name)
50
+ ? composeAppScript(name, sdk)
51
+ : sdk;
22
52
  }
23
53
  function renderCapabilityScript(name, manifest) {
24
- if (name === 'preupgrade' && (manifest.capabilities.rootOps || manifest.capabilities.delegate || manifest.capabilities.webapi)) {
54
+ if (name === "preupgrade" &&
55
+ (manifest.capabilities.rootOps ||
56
+ manifest.capabilities.delegate ||
57
+ manifest.capabilities.webapi)) {
25
58
  const installed = `\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}`;
26
59
  // Incoming preupgrade sees the OLD payload. Only complete absence is first
27
60
  // introduction; a partial or symlinked executor must still fail closed.
28
61
  const legacy = manifest.daemon
29
62
  ? ` if [ -e "${installed}/daemon" ] || [ -L "${installed}/daemon" ]; then\n "${installed}/daemon/control" stop "${installed}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n fi\n`
30
- : '';
31
- return renderStandaloneScriptBody(name, manifest).replace('#!/bin/sh\n', `#!/bin/sh\nif [ ! -e "${installed}/executor" ] && [ ! -L "${installed}/executor" ]; then\n${legacy} exit 0\nfi\n`);
63
+ : "";
64
+ return renderStandaloneScriptBody(name, manifest).replace("#!/bin/sh\n", `#!/bin/sh\nif [ ! -e "${installed}/executor" ] && [ ! -L "${installed}/executor" ]; then\n${legacy} exit 0\nfi\n`);
32
65
  }
33
66
  return renderStandaloneScriptBody(name, manifest);
34
67
  }
35
68
  function renderStandaloneScriptBody(name, manifest) {
36
- if (manifest.capabilities?.delegate?.scope === 'install-selected' && ['preinst', 'postinst'].includes(name)) {
37
- const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, delegate: { ...manifest.capabilities.delegate, scope: 'home' } } });
38
- return normal.replace('#!/bin/sh\n', () => '#!/bin/sh\n' + renderDelegateInstallDraft(manifest.package, name === 'preinst'));
69
+ if (manifest.capabilities?.delegate?.scope === "install-selected" &&
70
+ ["preinst", "postinst"].includes(name)) {
71
+ const normal = renderCapabilityScript(name, {
72
+ ...manifest,
73
+ capabilities: {
74
+ ...manifest.capabilities,
75
+ delegate: { ...manifest.capabilities.delegate, scope: "home" },
76
+ },
77
+ });
78
+ return normal.replace("#!/bin/sh\n", () => "#!/bin/sh\n" +
79
+ renderDelegateInstallDraft(manifest.package, name === "preinst"));
39
80
  }
40
- if (manifest.daemon && manifest.capabilities?.jobs && (manifest.capabilities.delegate || manifest.capabilities.rootOps || manifest.capabilities.webapi)
41
- && ['start-stop-status', 'preupgrade', 'preuninst'].includes(name))
81
+ if (manifest.daemon &&
82
+ manifest.capabilities?.jobs &&
83
+ (manifest.capabilities.delegate ||
84
+ manifest.capabilities.rootOps ||
85
+ manifest.capabilities.webapi) &&
86
+ ["start-stop-status", "preupgrade", "preuninst"].includes(name))
42
87
  return renderDelegatedDaemonScript(name, manifest);
43
- if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate || manifest.capabilities?.webapi) && ["preuninst", "preupgrade", "start-stop-status"].includes(name)) {
88
+ if ((manifest.capabilities?.rootOps ||
89
+ manifest.capabilities?.delegate ||
90
+ manifest.capabilities?.webapi) &&
91
+ ["preuninst", "preupgrade", "start-stop-status"].includes(name)) {
44
92
  const helper = `"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle"`;
45
93
  // Attempt worker stop first, but let executor shutdown drain requests and
46
94
  // close ingress even when it fails; a still-running worker fails the hook.
47
- const workerStop = manifest.capabilities.jobs ? renderBackgroundWorkerStop(manifest.package) : "";
48
- const stopWorker = manifest.capabilities.jobs ? "worker_result=0\nstop_background_worker || worker_result=1\n" : "";
49
- const workerResult = manifest.capabilities.jobs ? 'exit "$worker_result"\n' : "exit 0\n";
95
+ const workerStop = manifest.capabilities.jobs
96
+ ? renderBackgroundWorkerStop(manifest.package)
97
+ : "";
98
+ const stopWorker = manifest.capabilities.jobs
99
+ ? "worker_result=0\nstop_background_worker || worker_result=1\n"
100
+ : "";
101
+ const workerResult = manifest.capabilities.jobs
102
+ ? 'exit "$worker_result"\n'
103
+ : "exit 0\n";
50
104
  const request = (action) => `${helper} ${action} --package ${manifest.package} || exit $?\n`;
51
105
  if (name === "start-stop-status") {
52
- const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined, webapi: undefined } });
53
- const stopDaemon = manifest.daemon ? `"/var/packages/${manifest.package}/target/daemon/control" stop "\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n` : "";
106
+ const normal = renderCapabilityScript(name, {
107
+ ...manifest,
108
+ capabilities: {
109
+ ...manifest.capabilities,
110
+ rootOps: undefined,
111
+ delegate: undefined,
112
+ webapi: undefined,
113
+ },
114
+ });
115
+ const stopDaemon = manifest.daemon
116
+ ? `"/var/packages/${manifest.package}/target/daemon/control" stop "\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n`
117
+ : "";
54
118
  return normal.replace("#!/bin/sh\n", `#!/bin/sh\n${workerStop}if [ "$1" = start ]; then\n${request("start")}fi\nif [ "$1" = stop ]; then\n${stopWorker}${stopDaemon}${request("stop")}${workerResult}fi\n`);
55
119
  }
56
120
  if (name === "preuninst")
@@ -60,11 +124,24 @@ function renderStandaloneScriptBody(name, manifest) {
60
124
  if (name === "postuninst")
61
125
  return renderPackagePostuninst(manifest.package);
62
126
  if (name === "preuninst" && manifest.uninstall) {
63
- const body = manifest.daemon ? renderDaemonPreuninst(manifest) : `${UNINSTALL_ONLY}exit 0\n`;
127
+ const body = manifest.daemon
128
+ ? renderDaemonPreuninst(manifest)
129
+ : `${UNINSTALL_ONLY}exit 0\n`;
64
130
  return `#!/bin/sh\n${renderUninstallHook(manifest)}${body}`;
65
131
  }
66
- if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate || manifest.capabilities?.webapi) && ["postinst", "postupgrade"].includes(name)) {
67
- const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined, webapi: undefined } });
132
+ if ((manifest.capabilities?.rootOps ||
133
+ manifest.capabilities?.delegate ||
134
+ manifest.capabilities?.webapi) &&
135
+ ["postinst", "postupgrade"].includes(name)) {
136
+ const normal = renderCapabilityScript(name, {
137
+ ...manifest,
138
+ capabilities: {
139
+ ...manifest.capabilities,
140
+ rootOps: undefined,
141
+ delegate: undefined,
142
+ webapi: undefined,
143
+ },
144
+ });
68
145
  return normal.replace("#!/bin/sh\n", `#!/bin/sh\n"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle" start --package ${manifest.package} || exit $?\n`);
69
146
  }
70
147
  if (manifest.sharedFolder && (name === "postinst" || name === "postupgrade"))
@@ -95,40 +172,39 @@ function renderDelegatedDaemonScript(name, manifest) {
95
172
  // because a producer could not stop. A partial teardown remains a failure.
96
173
  const stop = (daemonAction, executorAction, executorStop = executor(executorAction)) => `result=0\nstop_background_worker || result=1\n${daemon} ${daemonAction} ${paths} || result=1\n${executorStop} || result=1\nexit "$result"\n`;
97
174
  const workerStop = renderBackgroundWorkerStop(pkg);
98
- if (name !== 'start-stop-status') {
99
- if (name === 'preupgrade')
100
- return `#!/bin/sh\n${renderExecutorUpgradeStop(pkg)}${workerStop}${stop('stop', 'stop', UPGRADE_STOP)}`;
175
+ if (name !== "start-stop-status") {
176
+ if (name === "preupgrade")
177
+ return `#!/bin/sh\n${renderExecutorUpgradeStop(pkg)}${workerStop}${stop("stop", "stop", UPGRADE_STOP)}`;
101
178
  const purge = manifest.uninstall
102
- ? stop(manifest.daemon?.uninstall_args ? 'uninstall' : 'stop', 'purge $synopkgland_purge_args')
103
- .replace(executor('purge $synopkgland_purge_args'), () => `synopkgland_uninstall_hook\n${executor('purge $synopkgland_purge_args')}`)
104
- : stop(manifest.daemon?.uninstall_args ? 'uninstall' : 'stop', 'purge');
105
- return `#!/bin/sh\n${workerStop}${renderUninstallHook(manifest)}if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n${stop('stop', 'stop')}fi\n${purge}`;
179
+ ? stop(manifest.daemon?.uninstall_args ? "uninstall" : "stop", "purge $synopkgland_purge_args").replace(executor("purge $synopkgland_purge_args"), () => `synopkgland_uninstall_hook\n${executor("purge $synopkgland_purge_args")}`)
180
+ : stop(manifest.daemon?.uninstall_args ? "uninstall" : "stop", "purge");
181
+ return `#!/bin/sh\n${workerStop}${renderUninstallHook(manifest)}if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n${stop("stop", "stop")}fi\n${purge}`;
106
182
  }
107
183
  return `#!/bin/sh
108
184
  ${workerStop}${renderUserManagerRecovery()}${renderBackgroundWorkerStart(pkg)}case "$1" in
109
185
  prestart) recover_user_manager ;;
110
186
  prestop) exit 0 ;;
111
187
  start)
112
- ${executor('start')} || exit $?
188
+ ${executor("start")} || exit $?
113
189
  if ! ${daemon} start ${paths}; then
114
190
  ${daemon} stop ${paths}
115
- ${executor('stop')}
191
+ ${executor("stop")}
116
192
  exit 1
117
193
  fi
118
194
  if ! start_background_worker; then
119
- ${worker('stop')}
195
+ ${worker("stop")}
120
196
  ${daemon} stop ${paths}
121
- ${executor('stop')}
197
+ ${executor("stop")}
122
198
  exit 1
123
199
  fi
124
200
  exit 0 ;;
125
201
  status)
126
202
  ${daemon} status ${paths} || exit $?
127
- worker_state="$(${worker('get-active-status')})" || exit 3
203
+ worker_state="$(${worker("get-active-status")})" || exit 3
128
204
  [ "$worker_state" = active ] && exit 0
129
205
  exit 3 ;;
130
206
  stop)
131
- ${stop('stop', 'stop')} ;;
207
+ ${stop("stop", "stop")} ;;
132
208
  restart)
133
209
  "$0" stop || exit $?
134
210
  exec "$0" start ;;
@@ -163,7 +239,9 @@ function renderDaemonScript(name, manifest) {
163
239
  // DSM also runs the outgoing preuninst hook during package upgrades.
164
240
  if (["preupgrade", "preuninst"].includes(name)) {
165
241
  const uninstall = name === "preuninst" && manifest.daemon?.uninstall_args;
166
- const upgrade = uninstall ? `if [ "\${SYNOPKG_PKG_STATUS:-}" = "UPGRADE" ]; then\n exec ${control} stop ${paths}\nfi\n` : "";
242
+ const upgrade = uninstall
243
+ ? `if [ "\${SYNOPKG_PKG_STATUS:-}" = "UPGRADE" ]; then\n exec ${control} stop ${paths}\nfi\n`
244
+ : "";
167
245
  return `#!/bin/sh\n${upgrade}exec ${control} ${uninstall ? "uninstall" : "stop"} ${paths}\n`;
168
246
  }
169
247
  if (name !== "start-stop-status")
@@ -187,13 +265,17 @@ esac
187
265
  function renderExecutorPreuninst(manifest, workerStop, stopWorker, workerResult, request) {
188
266
  const pkg = manifest.package;
189
267
  const choose = 'action=purge\n[ "${SYNOPKG_PKG_STATUS:-}" != UPGRADE ] || action=stop\n';
190
- const hook = manifest.uninstall ? '[ "$action" = stop ] || synopkgland_uninstall_hook\n' : "";
268
+ const hook = manifest.uninstall
269
+ ? '[ "$action" = stop ] || synopkgland_uninstall_hook\n'
270
+ : "";
191
271
  const purge = request(manifest.uninstall ? '"$action" $synopkgland_purge_args' : '"$action"');
192
272
  if (!manifest.daemon)
193
273
  return `#!/bin/sh\n${workerStop}${renderUninstallHook(manifest)}${stopWorker}${choose}${hook}${purge}${workerResult}`;
194
274
  const control = `"/var/packages/${pkg}/target/daemon/control"`;
195
275
  const paths = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${pkg}/var}"`;
196
- const daemonAction = manifest.daemon.uninstall_args ? 'daemon_action=stop\n[ "$action" = stop ] || daemon_action=uninstall\n' : "daemon_action=stop\n";
276
+ const daemonAction = manifest.daemon.uninstall_args
277
+ ? 'daemon_action=stop\n[ "$action" = stop ] || daemon_action=uninstall\n'
278
+ : "daemon_action=stop\n";
197
279
  return `#!/bin/sh\n${workerStop}${renderUninstallHook(manifest)}${stopWorker}${choose}${daemonAction}daemon_result=0\n${control} "$daemon_action" ${paths} || daemon_result=$?\n${hook}${purge}exit "$daemon_result"\n`;
198
280
  }
199
281
  /** The daemon stops (or uninstalls) first; the App hook follows on a real uninstall. */
@@ -201,6 +283,6 @@ function renderDaemonPreuninst(manifest) {
201
283
  const pkg = manifest.package;
202
284
  const control = `"/var/packages/${pkg}/target/daemon/control"`;
203
285
  const paths = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${pkg}/var}"`;
204
- return `if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n exec ${control} stop ${paths}\nfi\n`
205
- + `${control} ${manifest.daemon?.uninstall_args ? "uninstall" : "stop"} ${paths}\ndaemon_result=$?\nsynopkgland_uninstall_hook\nexit "$daemon_result"\n`;
286
+ return (`if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n exec ${control} stop ${paths}\nfi\n` +
287
+ `${control} ${manifest.daemon?.uninstall_args ? "uninstall" : "stop"} ${paths}\ndaemon_result=$?\nsynopkgland_uninstall_hook\nexit "$daemon_result"\n`);
206
288
  }
@@ -1,4 +1,5 @@
1
- import { type WebAPIProfiles } from '../runtime/webapi-profile.js';
1
+ import { type WebLaunchMode } from "../contract/web-launch.js";
2
+ import { type WebAPIProfiles } from "../runtime/webapi-profile.js";
2
3
  import { type PackageMetadata } from "../package-metadata.js";
3
4
  import { type DelegateCapability } from "../runtime/executor-policy.js";
4
5
  import type { ExecutorPins, ExecutorTask } from "../runtime/executor-tasks.js";
@@ -41,6 +42,12 @@ export interface StandaloneI18n {
41
42
  info: InfoTranslations;
42
43
  }
43
44
  export interface StandaloneManifest {
45
+ ctl?: {
46
+ enabled: boolean;
47
+ aliases?: string[];
48
+ developedBy?: string;
49
+ commands: Record<string, import("../ctl/descriptor.js").Operation>;
50
+ };
44
51
  metadata?: PackageMetadata;
45
52
  root: string;
46
53
  package: string;
@@ -61,6 +68,7 @@ export interface StandaloneManifest {
61
68
  /** `ui.window`: the DSM window's size over the defaults; absent keeps 960×640, at least 700×420. */
62
69
  ui: {
63
70
  dir: string;
71
+ launch?: WebLaunchMode;
64
72
  build?: {
65
73
  cwd: string;
66
74
  command: string[];
@@ -102,6 +110,7 @@ export interface StandaloneManifest {
102
110
  */
103
111
  export type EntryMode = "admin-only" | "app-privilege";
104
112
  export interface Capabilities {
113
+ composeMounts?: true;
105
114
  webapi?: WebAPIProfiles;
106
115
  files?: "session";
107
116
  identity?: "standalone";