@synopackageland/cli 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +215 -1696
- package/README.md +21 -2
- package/assets/login-style/mask.png +0 -0
- package/assets/login-style/wallpaper-thumbnail.jpg +0 -0
- package/assets/login-style/wallpaper.jpg +0 -0
- package/dist/build-spk/generator.js +10 -1
- package/dist/build-spk/login-style.d.ts +55 -0
- package/dist/build-spk/login-style.js +115 -0
- package/dist/cli.js +22 -1
- package/dist/error-registry.js +37 -1
- package/dist/host/dsm-client.js +6 -9
- package/dist/init.js +9 -5
- package/dist/runtime/delegate-contract.d.ts +2 -92
- package/dist/runtime/delegate-contract.js +2 -24
- package/dist/runtime/executor-digests.json +11 -0
- package/dist/runtime/executor-policy-check.d.ts +1 -1
- package/dist/runtime/executor-policy-check.js +5 -7
- package/dist/runtime/executor-policy.d.ts +2 -0
- package/dist/runtime/executor-policy.js +5 -1
- package/dist/runtime/root-programs.d.ts +2 -0
- package/dist/runtime/sdk-source.d.ts +4 -0
- package/dist/runtime/sdk-source.js +7 -0
- package/dist/runtime/sdk-timeouts.d.ts +23 -0
- package/dist/runtime/sdk-timeouts.js +44 -0
- package/dist/skill.js +1 -0
- package/dist/standalone/app-scripts.d.ts +26 -0
- package/dist/standalone/app-scripts.js +141 -0
- package/dist/standalone/bridge-app.d.ts +2 -0
- package/dist/standalone/bridge-app.js +9 -1
- package/dist/standalone/bridge-backend.d.ts +1 -1
- package/dist/standalone/build.js +5 -2
- package/dist/standalone/dsm-window.d.ts +14 -3
- package/dist/standalone/dsm-window.js +24 -11
- package/dist/standalone/host-adapter.d.ts +1 -1
- package/dist/standalone/host-adapter.js +1 -1
- package/dist/standalone/host-file-navigation.d.ts +6 -2
- package/dist/standalone/host-file-navigation.js +8 -2
- package/dist/standalone/inspect-spk.d.ts +9 -0
- package/dist/standalone/inspect-spk.js +60 -6
- package/dist/standalone/lifecycle.js +3 -1
- package/dist/standalone/manifest.d.ts +6 -0
- package/dist/standalone/manifest.js +51 -3
- package/dist/standalone/portal-window.d.ts +6 -6
- package/dist/standalone/portal-window.js +12 -32
- package/dist/standalone/request-timeout.d.ts +6 -3
- package/dist/standalone/request-timeout.js +10 -4
- package/dist/standalone/root-ops.d.ts +3 -2
- package/dist/standalone/root-ops.js +9 -3
- package/dist/standalone/root-window.d.ts +2 -2
- package/dist/standalone/root-window.js +173 -49
- package/dist/standalone/sdk-check.js +4 -1
- package/dist/standalone/spk.d.ts +3 -0
- package/dist/standalone/spk.js +10 -3
- package/dist/validate.js +2 -0
- package/package.json +2 -3
- package/skills/SKILL.md +3 -1
- package/skills/reference/create.md +5 -1
- package/skills/reference/identity.md +1 -1
- package/skills/reference/lifecycle.md +110 -0
- package/skills/reference/portal.md +12 -0
- package/dist/authorization-sdk.d.ts +0 -6
- package/dist/authorization-sdk.js +0 -6
- package/dist/standalone/native-authorization-window.d.ts +0 -2
- package/dist/standalone/native-authorization-window.js +0 -36
- package/dist/standalone/native-root-window.d.ts +0 -29
- package/dist/standalone/native-root-window.js +0 -307
package/README.md
CHANGED
|
@@ -52,7 +52,8 @@ The CLI packages, deploys and checks. The app-side code is the SDK
|
|
|
52
52
|
`github.com/synopackageland/syno-pkg-land-sdk` and the npm package `@synopackageland/sdk` (Vue components at
|
|
53
53
|
`@synopackageland/sdk/vue`). The `require github.com/synopackageland/syno-pkg-land-sdk` line in
|
|
54
54
|
`backend/go.mod` is the app's only SDK version; `build` compiles the executor of that version
|
|
55
|
-
and checks it against the digests shipped in this CLI. CLI
|
|
55
|
+
and checks it against the digests shipped in this CLI. A CLI builds the SDK releases of its own minor version
|
|
56
|
+
(CLI X.Y.z builds SDK X.Y.*) that its digest table records; `EXECUTOR_DIGEST_MISMATCH` names the ones it knows.
|
|
56
57
|
|
|
57
58
|
The SDK repositories are private for now, so Go cannot fetch them from proxy.golang.org. Set
|
|
58
59
|
`go env -w GOPRIVATE=github.com/synopackageland/*` and give git a credential that can read
|
|
@@ -77,11 +78,29 @@ Add them with `init --with …` or later with `synopkgland add <capability…>`.
|
|
|
77
78
|
| `login` | Sign users in with their DSM account, without the DSM desktop | Nobody; requires HTTPS for password sign-in |
|
|
78
79
|
| `delegate` | Move files into a user's chosen folder while the user is offline | An administrator, once |
|
|
79
80
|
| `root-ops` | Run fixed `root/*.sh` scripts for administrators | An administrator, once |
|
|
80
|
-
| `portal` | Open the app from its own Application Portal alias | An administrator sets the alias; needs `root-ops` or `delegate` and DSM 7.1-42661+ (below 7.2-64570 the executor registers the entry) |
|
|
81
|
+
| `portal` | Open the app from its own Application Portal alias, with a login page style (`login_style` changes the colour, wallpaper and title) | An administrator sets the alias; needs `root-ops` or `delegate` and DSM 7.1-42661+ (below 7.2-64570 the executor registers the entry) |
|
|
81
82
|
|
|
82
83
|
Delegate is implemented; its default operation is `files.moveFromStaging`.
|
|
83
84
|
Device lifecycle acceptance on real hardware is still pending for delegate and root operations.
|
|
84
85
|
|
|
86
|
+
## Lifecycle scripts
|
|
87
|
+
|
|
88
|
+
Every template has a `dsm-scripts/` folder with DSM's own script names: `preinst`, `postinst`,
|
|
89
|
+
`preupgrade`, `postupgrade`, `preuninst`, `postuninst` and `start-stop-status`. Each does nothing
|
|
90
|
+
until you edit it. `build` packs them and calls each one from the generated script of the same
|
|
91
|
+
name, after the SDK's steps (before them for `preupgrade`, `preuninst` and `stop`); they run as
|
|
92
|
+
the package account, with DSM's arguments, environment and exit codes. Projects without
|
|
93
|
+
`dsm-scripts/` build as before. See `synopkgland skill show lifecycle`.
|
|
94
|
+
|
|
95
|
+
Root work after an install or upgrade, such as removing what an older version left, is not a
|
|
96
|
+
lifecycle script: name a `root/` program as `root.start` in dsm-package.yaml. The executor runs
|
|
97
|
+
it as root once at the first start of each new version, while the administrator's approval is
|
|
98
|
+
live, for at most 60 seconds; a failure is logged and does not block the start, and the executor
|
|
99
|
+
`status` reports the result as `start_step`. While it runs, the executor answers only `status`
|
|
100
|
+
(`state: "starting"`) and refuses other requests with the retryable `EXECUTOR_STARTING`; the DSM
|
|
101
|
+
window's approval waits for it. The program cannot declare `run` or `job`, and
|
|
102
|
+
declaring it changes the policy digest, so an administrator approves the App again.
|
|
103
|
+
|
|
85
104
|
## Commands
|
|
86
105
|
|
|
87
106
|
| Command | Purpose |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -19,6 +19,7 @@ import { createReproducibleTar } from "./reproducible-archive.js";
|
|
|
19
19
|
import { copyProjectNotices } from "./notices.js";
|
|
20
20
|
import { renderDeleteAppDataBlock } from "./delete-app-data.js";
|
|
21
21
|
import { renderUninstallWizardFiles } from "./uninstall-wizard.js";
|
|
22
|
+
import { APP_SCRIPTS_DIR, APP_SCRIPTS_SPK_DIR, composeAppScript, readAppScripts } from "../standalone/app-scripts.js";
|
|
22
23
|
import { renderInstallWizardFiles } from "./install-wizard.js";
|
|
23
24
|
import { renderWizardFiles, writeWizardUiFiles } from "./wizard-i18n.js";
|
|
24
25
|
import { sharedFolderCombobox } from "./share-combobox.js";
|
|
@@ -121,11 +122,19 @@ export async function buildSpk(options) {
|
|
|
121
122
|
}
|
|
122
123
|
writeWizards(staging, contract, parsedCompose?.compose, "container");
|
|
123
124
|
mkdirSync(join(staging, "scripts"), { recursive: true });
|
|
125
|
+
// validateProject already refused a bad scripts/ entry.
|
|
126
|
+
const appScripts = readAppScripts(options.project.root, () => { }) ?? [];
|
|
124
127
|
for (const name of SCRIPT_NAMES) {
|
|
125
128
|
const scriptPath = join(staging, "scripts", name);
|
|
126
|
-
|
|
129
|
+
const script = renderManagedScript(name, contract, isResourceWorkerRoute, rwEligibility?.adminport, isDynamicRoute, rwEligibility?.headless === true);
|
|
130
|
+
writeFileSync(scriptPath, appScripts.includes(name) ? composeAppScript(name, script) : script, "utf8");
|
|
127
131
|
chmodSync(scriptPath, SCRIPT_MODE);
|
|
128
132
|
}
|
|
133
|
+
for (const name of appScripts) {
|
|
134
|
+
mkdirSync(join(staging, APP_SCRIPTS_SPK_DIR), { recursive: true });
|
|
135
|
+
copyFileSync(join(options.project.root, APP_SCRIPTS_DIR, name), join(staging, APP_SCRIPTS_SPK_DIR, name));
|
|
136
|
+
chmodSync(join(staging, APP_SCRIPTS_SPK_DIR, name), SCRIPT_MODE);
|
|
137
|
+
}
|
|
129
138
|
mkdirSync(join(staging, "package"), { recursive: true });
|
|
130
139
|
copyFileSync(options.project.composePath, join(staging, "package", "compose.yaml"));
|
|
131
140
|
copyFileSync(options.project.contractPath, join(staging, "package", "synology-app.yaml"));
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSM's Login Portal draws an Application Portal entry from the launcher's
|
|
3
|
+
* `loginStyle` in ui/config: the thumbnail in Control Panel > Login Portal
|
|
4
|
+
* and the alias's own login page. Without it the thumbnail stays blank.
|
|
5
|
+
* Paths are relative to ui/; DSM replaces `{0}` with 1x or 2x, and a path
|
|
6
|
+
* without it serves both.
|
|
7
|
+
*/
|
|
8
|
+
/** Ships next to dist/ and src/ alike: <package>/assets/login-style, drawn by scripts/render-login-style.py. */
|
|
9
|
+
export declare const DEFAULT_LOGIN_STYLE_DIR: string;
|
|
10
|
+
/** The login button colour; the default mask's button has the same colour. */
|
|
11
|
+
export declare const DEFAULT_LOGIN_COLOR = "#2F6BD8";
|
|
12
|
+
export declare const LOGIN_COLOR: RegExp;
|
|
13
|
+
/** `login_style` in dsm-package.yaml, its paths resolved inside the project. */
|
|
14
|
+
export interface LoginStyle {
|
|
15
|
+
color?: string;
|
|
16
|
+
wallpaper?: {
|
|
17
|
+
image: string;
|
|
18
|
+
thumbnail: string;
|
|
19
|
+
};
|
|
20
|
+
title?: string;
|
|
21
|
+
}
|
|
22
|
+
/** The project files login_style names, by their dsm-package.yaml field. */
|
|
23
|
+
export type LoginImageField = "wallpaper" | "wallpaper_thumbnail" | "title";
|
|
24
|
+
/** The frame size of a JPEG, from its first SOF segment. */
|
|
25
|
+
export declare function jpegSize(bytes: Buffer): {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
};
|
|
29
|
+
/** Why a login_style image cannot be used, or undefined. */
|
|
30
|
+
export declare function loginImageProblem(field: LoginImageField, path: string): string | undefined;
|
|
31
|
+
/** ui/config `loginStyle` fields and the ui/ files they name. */
|
|
32
|
+
export interface LoginStyleAssets {
|
|
33
|
+
fields: Record<string, string>;
|
|
34
|
+
files: Map<string, Buffer>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The App's login style over the CLI defaults. The file name carries the
|
|
38
|
+
* content, as the window icons do, so DSM and browsers load a changed image.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loginStyleAssets(style?: LoginStyle): LoginStyleAssets;
|
|
41
|
+
/** Whether a payload file is the CLI's default of that name. */
|
|
42
|
+
export declare function isDefaultLoginAsset(name: "wallpaper.jpg" | "wallpaper-thumbnail.jpg" | "mask.png", bytes: Buffer): boolean;
|
|
43
|
+
/** check's view of login_style: each override by its project path, or the default. */
|
|
44
|
+
export interface LoginStyleReport {
|
|
45
|
+
color: string;
|
|
46
|
+
wallpaper: "default" | string;
|
|
47
|
+
title: "app-icon" | string;
|
|
48
|
+
}
|
|
49
|
+
export declare function loginStyleReport(root: string, style?: LoginStyle): LoginStyleReport;
|
|
50
|
+
/** One line for check and inspect; inspect knows only "custom", check the project path. */
|
|
51
|
+
export declare function formatLoginStyle(style: {
|
|
52
|
+
color: string;
|
|
53
|
+
wallpaper: string;
|
|
54
|
+
title: string;
|
|
55
|
+
}): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { decodePng } from "./png.js";
|
|
6
|
+
/**
|
|
7
|
+
* DSM's Login Portal draws an Application Portal entry from the launcher's
|
|
8
|
+
* `loginStyle` in ui/config: the thumbnail in Control Panel > Login Portal
|
|
9
|
+
* and the alias's own login page. Without it the thumbnail stays blank.
|
|
10
|
+
* Paths are relative to ui/; DSM replaces `{0}` with 1x or 2x, and a path
|
|
11
|
+
* without it serves both.
|
|
12
|
+
*/
|
|
13
|
+
/** Ships next to dist/ and src/ alike: <package>/assets/login-style, drawn by scripts/render-login-style.py. */
|
|
14
|
+
export const DEFAULT_LOGIN_STYLE_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "login-style");
|
|
15
|
+
/** The login button colour; the default mask's button has the same colour. */
|
|
16
|
+
export const DEFAULT_LOGIN_COLOR = "#2F6BD8";
|
|
17
|
+
export const LOGIN_COLOR = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
|
|
18
|
+
const MAX_WALLPAPER_BYTES = 2 * 1024 * 1024;
|
|
19
|
+
/** The frame size of a JPEG, from its first SOF segment. */
|
|
20
|
+
export function jpegSize(bytes) {
|
|
21
|
+
if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8)
|
|
22
|
+
throw new Error("Not a JPEG file (no start-of-image marker).");
|
|
23
|
+
let at = 2;
|
|
24
|
+
while (at + 4 <= bytes.length) {
|
|
25
|
+
if (bytes[at] !== 0xff)
|
|
26
|
+
break;
|
|
27
|
+
const marker = bytes[at + 1];
|
|
28
|
+
if (marker === 0xff) {
|
|
29
|
+
at += 1;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (marker === 0xd9 || marker === 0xda)
|
|
33
|
+
break;
|
|
34
|
+
if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
35
|
+
at += 2;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const length = bytes.readUInt16BE(at + 2);
|
|
39
|
+
const frame = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
40
|
+
if (frame && at + 9 <= bytes.length)
|
|
41
|
+
return { height: bytes.readUInt16BE(at + 5), width: bytes.readUInt16BE(at + 7) };
|
|
42
|
+
at += 2 + length;
|
|
43
|
+
}
|
|
44
|
+
throw new Error("The JPEG has no frame header.");
|
|
45
|
+
}
|
|
46
|
+
function exactly(name, size, width, height) {
|
|
47
|
+
if (size.width !== width || size.height !== height) {
|
|
48
|
+
throw new Error(`${name} is ${size.width}x${size.height}; it should be ${width}x${height}.`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Format and size per field: the sizes DSM's own packages ship and its pages draw. */
|
|
52
|
+
const IMAGE_RULES = {
|
|
53
|
+
wallpaper: (name, bytes) => {
|
|
54
|
+
const size = jpegSize(bytes);
|
|
55
|
+
if (size.width < 1920 || size.height < 1080)
|
|
56
|
+
throw new Error(`${name} is ${size.width}x${size.height}; the login page needs at least 1920x1080.`);
|
|
57
|
+
if (bytes.length > MAX_WALLPAPER_BYTES)
|
|
58
|
+
throw new Error(`${name} is ${bytes.length} bytes, over 2 MiB; every login page loads it.`);
|
|
59
|
+
},
|
|
60
|
+
wallpaper_thumbnail: (name, bytes) => exactly(name, jpegSize(bytes), 320, 200),
|
|
61
|
+
title: (name, bytes) => {
|
|
62
|
+
const image = decodePng(bytes);
|
|
63
|
+
exactly(name, image, 680, 124);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
/** Why a login_style image cannot be used, or undefined. */
|
|
67
|
+
export function loginImageProblem(field, path) {
|
|
68
|
+
const name = basename(path);
|
|
69
|
+
if (!existsSync(path))
|
|
70
|
+
return `${name} was not found.`;
|
|
71
|
+
try {
|
|
72
|
+
IMAGE_RULES[field](name, readFileSync(path));
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return error.message;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The App's login style over the CLI defaults. The file name carries the
|
|
81
|
+
* content, as the window icons do, so DSM and browsers load a changed image.
|
|
82
|
+
*/
|
|
83
|
+
export function loginStyleAssets(style = {}) {
|
|
84
|
+
const fields = { mainColor: style.color ?? DEFAULT_LOGIN_COLOR };
|
|
85
|
+
const files = new Map();
|
|
86
|
+
const add = (field, name, bytes) => {
|
|
87
|
+
const path = `images/login_${createHash("sha256").update(bytes).digest("hex").slice(0, 12)}_${name}`;
|
|
88
|
+
fields[field] = path;
|
|
89
|
+
files.set(path, bytes);
|
|
90
|
+
};
|
|
91
|
+
const asset = (name) => readFileSync(join(DEFAULT_LOGIN_STYLE_DIR, name));
|
|
92
|
+
add("defaultLoginWallpaper", "wallpaper.jpg", style.wallpaper ? readFileSync(style.wallpaper.image) : asset("wallpaper.jpg"));
|
|
93
|
+
add("defaultLoginWallpaperThumbnail", "wallpaper-thumbnail.jpg", style.wallpaper ? readFileSync(style.wallpaper.thumbnail) : asset("wallpaper-thumbnail.jpg"));
|
|
94
|
+
add("loginPreviewThumbnailMaskPath", "mask.png", asset("mask.png"));
|
|
95
|
+
if (style.title)
|
|
96
|
+
add("titleImgPath", "title.png", readFileSync(style.title));
|
|
97
|
+
return { fields, files };
|
|
98
|
+
}
|
|
99
|
+
/** Whether a payload file is the CLI's default of that name. */
|
|
100
|
+
export function isDefaultLoginAsset(name, bytes) {
|
|
101
|
+
return bytes.equals(readFileSync(join(DEFAULT_LOGIN_STYLE_DIR, name)));
|
|
102
|
+
}
|
|
103
|
+
export function loginStyleReport(root, style = {}) {
|
|
104
|
+
return {
|
|
105
|
+
color: style.color ?? DEFAULT_LOGIN_COLOR,
|
|
106
|
+
wallpaper: style.wallpaper ? relative(root, style.wallpaper.image) : "default",
|
|
107
|
+
title: style.title ? relative(root, style.title) : "app-icon",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** One line for check and inspect; inspect knows only "custom", check the project path. */
|
|
111
|
+
export function formatLoginStyle(style) {
|
|
112
|
+
const wallpaper = style.wallpaper === "default" ? "CLI default wallpaper" : style.wallpaper === "custom" ? "custom wallpaper" : `wallpaper ${style.wallpaper}`;
|
|
113
|
+
const title = style.title === "app-icon" ? "App icon as title" : style.title === "none" ? "no title" : style.title === "custom" ? "custom title" : `title ${style.title}`;
|
|
114
|
+
return `Login style: colour ${style.color}, ${wallpaper}, ${title}`;
|
|
115
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import { buildSpk } from "./build-spk/generator.js";
|
|
|
20
20
|
import { CONTAINER_ARCH_FAMILIES, isContainerArchFamily, isNativeArchFamily, NATIVE_ARCH_FAMILIES, parseArchList, } from "./build-spk/dsm-arch.js";
|
|
21
21
|
import { DEFAULT_MIN_DSM, CONTAINER_MIN_DSM } from "./build-spk/dsm-version.js";
|
|
22
22
|
import { iconReport } from "./build-spk/dsm-icons.js";
|
|
23
|
+
import { formatLoginStyle, loginStyleReport } from "./build-spk/login-style.js";
|
|
23
24
|
import { deployProject } from "./deploy/deploy.js";
|
|
24
25
|
import { planDeployment, deployStandalone } from "./deploy/plan.js";
|
|
25
26
|
import { createNasHost } from "./host/dsm-client.js";
|
|
@@ -90,6 +91,16 @@ function formatSpkChecks(report) {
|
|
|
90
91
|
: []),
|
|
91
92
|
];
|
|
92
93
|
}
|
|
94
|
+
/** The root programs the executor runs at lifecycle events, without a session. */
|
|
95
|
+
function formatRootSteps(report) {
|
|
96
|
+
const policy = report.executor?.policy;
|
|
97
|
+
const steps = [
|
|
98
|
+
...(policy?.start ? [`start ${policy.start} (once after each install or upgrade)`] : []),
|
|
99
|
+
...(policy?.stop ? [`stop ${policy.stop} (at package stop and upgrade)`] : []),
|
|
100
|
+
...(policy?.purge ? [`purge ${policy.purge} (at uninstall)`] : []),
|
|
101
|
+
];
|
|
102
|
+
return steps.length ? [`Root steps: ${steps.join(", ")}`] : [];
|
|
103
|
+
}
|
|
93
104
|
function formatSpkReport(spk, report) {
|
|
94
105
|
return [
|
|
95
106
|
`SPK: ${spk}`,
|
|
@@ -100,6 +111,9 @@ function formatSpkReport(spk, report) {
|
|
|
100
111
|
...(report.icon ? [`Icon: ${report.icon.source === "default" ? "SDK default icon" : report.icon.source === "custom" ? `custom (PACKAGE_ICON_256 sha256 ${report.icon.sha256.slice(0, 12)}…)` : "no PACKAGE_ICON"}`] : []),
|
|
101
112
|
...(report.entry === "admin-only" ? ["Entry: entry: admin-only; only a DSM administrator can open it and call any route (including SDK routes)"] : []),
|
|
102
113
|
...(report.entry === "app-privilege" ? ["Entry: entry: app-privilege; DSM Control Panel > Application Privileges decides who can use it"] : []),
|
|
114
|
+
...formatRootSteps(report),
|
|
115
|
+
...(report.appScripts ? [`App scripts: ${report.appScripts.join(", ")} (scripts/app, run by the generated scripts of the same name as the package account)`] : []),
|
|
116
|
+
...(report.loginStyle ? [formatLoginStyle(report.loginStyle)] : []),
|
|
103
117
|
...(report.portal ? [`Application Portal: default alias /${report.portal.alias}/ (${report.portal.appId}), active after an administrator applies it`] : []),
|
|
104
118
|
...report.capabilities.map((cap) => `Capability ${cap.name}: ${cap.requiresAdminApproval ? "needs DSM administrator approval" : "ready after install"}`),
|
|
105
119
|
"payload:",
|
|
@@ -217,11 +231,18 @@ export async function runCli(argv) {
|
|
|
217
231
|
const iconText = formatIconReport(icon, "dsm-package.yaml");
|
|
218
232
|
if (iconText.warning)
|
|
219
233
|
console.error(iconText.warning);
|
|
234
|
+
// A portal build carries the login style; a native layout keeps its own ui/config.
|
|
235
|
+
const loginStyle = !isNativeLayout(standalone) && "portal" in loaded.manifest && loaded.manifest.portal
|
|
236
|
+
? loginStyleReport(standalone, "loginStyle" in loaded.manifest ? loaded.manifest.loginStyle : undefined) : undefined;
|
|
220
237
|
emit("check", {
|
|
221
238
|
package: loaded.manifest.package,
|
|
222
239
|
capabilities: "capabilities" in loaded.manifest ? loaded.manifest.capabilities : {},
|
|
223
240
|
icon,
|
|
224
|
-
|
|
241
|
+
...(loginStyle ? { loginStyle } : {}),
|
|
242
|
+
}, ["Validation passed.", ...iconText.lines, ...(loginStyle ? [
|
|
243
|
+
formatLoginStyle(loginStyle),
|
|
244
|
+
"info: set login_style: in dsm-package.yaml (color, wallpaper with wallpaper_thumbnail, title) to brand the Login Portal page.",
|
|
245
|
+
] : [])].join("\n"));
|
|
225
246
|
return;
|
|
226
247
|
}
|
|
227
248
|
const project = requireProject(rootOf(command));
|
package/dist/error-registry.js
CHANGED
|
@@ -389,6 +389,26 @@ export const ERROR_REGISTRY = {
|
|
|
389
389
|
"description": "dsm-package.yaml is not a valid YAML object.",
|
|
390
390
|
"suggestion": "Fix the YAML syntax in dsm-package.yaml."
|
|
391
391
|
},
|
|
392
|
+
"APP_SCRIPT_UNKNOWN": {
|
|
393
|
+
"code": "APP_SCRIPT_UNKNOWN",
|
|
394
|
+
"description": "A file in the project's dsm-scripts/ folder does not have a DSM package script name the SDK runs.",
|
|
395
|
+
"suggestion": "Name it preinst, postinst, preuninst, postuninst, preupgrade, postupgrade or start-stop-status, or move it out of dsm-scripts/. prereplace and postreplace are not supported."
|
|
396
|
+
},
|
|
397
|
+
"APP_SCRIPT_INVALID": {
|
|
398
|
+
"code": "APP_SCRIPT_INVALID",
|
|
399
|
+
"description": "A dsm-scripts/<DSM script name> entry is a symlink, directory or special file.",
|
|
400
|
+
"suggestion": "Make it a regular file in the project's dsm-scripts/ folder."
|
|
401
|
+
},
|
|
402
|
+
"APP_SCRIPT_NOT_EXECUTABLE": {
|
|
403
|
+
"code": "APP_SCRIPT_NOT_EXECUTABLE",
|
|
404
|
+
"description": "A dsm-scripts/<DSM script name> file is not executable; DSM runs it directly.",
|
|
405
|
+
"suggestion": "Run chmod +x on the file."
|
|
406
|
+
},
|
|
407
|
+
"APP_SCRIPT_NO_SHEBANG": {
|
|
408
|
+
"code": "APP_SCRIPT_NO_SHEBANG",
|
|
409
|
+
"description": "A dsm-scripts/<DSM script name> file does not start with #!.",
|
|
410
|
+
"suggestion": "Start the file with an interpreter line such as #!/bin/sh."
|
|
411
|
+
},
|
|
392
412
|
"STANDALONE_FIELD_UNKNOWN": {
|
|
393
413
|
"code": "STANDALONE_FIELD_UNKNOWN",
|
|
394
414
|
"description": "dsm-package.yaml contains an unsupported field.",
|
|
@@ -594,6 +614,11 @@ export const ERROR_REGISTRY = {
|
|
|
594
614
|
"description": "backend.timeout_seconds needs a Go main that reads the requestTimeout the build passes.",
|
|
595
615
|
"suggestion": "Declare var requestTimeout = \"\" in main.go and pass its seconds to api.WithRequestDeadline(handler, api.Timeout(...)), or remove timeout_seconds."
|
|
596
616
|
},
|
|
617
|
+
"STANDALONE_REQUEST_TIMEOUT_SHORT": {
|
|
618
|
+
"code": "STANDALONE_REQUEST_TIMEOUT_SHORT",
|
|
619
|
+
"description": "The request deadline ends before a root program that root.run may run would answer: the executor answers 10 seconds after run.timeout_s and its client waits 10 more.",
|
|
620
|
+
"suggestion": "Set backend.timeout_seconds to at least the program's run.timeout_s (20 by default) + 20, or lower run.timeout_s."
|
|
621
|
+
},
|
|
597
622
|
"STANDALONE_UI_WINDOW_INVALID": {
|
|
598
623
|
"code": "STANDALONE_UI_WINDOW_INVALID",
|
|
599
624
|
"description": "ui.window does not give a valid DSM window size.",
|
|
@@ -644,6 +669,16 @@ export const ERROR_REGISTRY = {
|
|
|
644
669
|
"description": "An App without an executor (capabilities.rootOps or delegate) declares portal.",
|
|
645
670
|
"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."
|
|
646
671
|
},
|
|
672
|
+
"STANDALONE_LOGIN_STYLE_INVALID": {
|
|
673
|
+
"code": "STANDALONE_LOGIN_STYLE_INVALID",
|
|
674
|
+
"description": "login_style in dsm-package.yaml is malformed, or set without portal.",
|
|
675
|
+
"suggestion": "Declare portal, and give login_style only color (#RRGGBB), wallpaper together with wallpaper_thumbnail, and title; omit login_style to use the CLI default style."
|
|
676
|
+
},
|
|
677
|
+
"STANDALONE_LOGIN_STYLE_IMAGE_INVALID": {
|
|
678
|
+
"code": "STANDALONE_LOGIN_STYLE_IMAGE_INVALID",
|
|
679
|
+
"description": "An image login_style names is missing, in the wrong format or the wrong size.",
|
|
680
|
+
"suggestion": "Use a JPEG wallpaper of at least 1920x1080 and at most 2 MiB, a 320x200 JPEG wallpaper_thumbnail and a 680x124 PNG title, inside the project."
|
|
681
|
+
},
|
|
647
682
|
"STANDALONE_I18N_INVALID": {
|
|
648
683
|
"code": "STANDALONE_I18N_INVALID",
|
|
649
684
|
"description": "The i18n configuration in dsm-package.yaml is invalid.",
|
|
@@ -757,7 +792,7 @@ for (const [code, description, suggestion] of [
|
|
|
757
792
|
["TEMPLATES_MISSING", "@synopackageland/templates was not found.", "Reinstall @synopackageland/cli, or point SYNOPKGLAND_TEMPLATES_DIR at a templates checkout."],
|
|
758
793
|
["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
794
|
["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_POLICY_REJECTED", "The executor of the App's SDK version refuses the policy the CLI generated, so the NAS would refuse it at approval.", "The reason names the refused part: policy root/stop/purge → root.programs (schema, run, job), root.stop, uninstall.purge; policy scope/operation/destination/move/lifetime → capabilities.delegate; policy portal → portal; policy entry → entry. Fix that part of dsm-package.yaml; if it already matches the manifest reference, use the SDK version this CLI release names."],
|
|
795
|
+
["EXECUTOR_POLICY_REJECTED", "The executor of the App's SDK version refuses the policy the CLI generated, so the NAS would refuse it at approval.", "The reason names the refused part: policy root/start/stop/purge → root.programs (schema, run, job), root.start, root.stop, uninstall.purge; policy scope/operation/destination/move/lifetime → capabilities.delegate; policy portal → portal; policy entry → entry. Fix that part of dsm-package.yaml; if it already matches the manifest reference, use the SDK version this CLI release names."],
|
|
761
796
|
["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."],
|
|
762
797
|
["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)."],
|
|
763
798
|
["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."],
|
|
@@ -781,6 +816,7 @@ for (const [code, description, suggestion] of [
|
|
|
781
816
|
["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."],
|
|
782
817
|
["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."],
|
|
783
818
|
["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."],
|
|
819
|
+
["STANDALONE_PYTHON_SDK_MISSING", "The App's SDK version has no Python SDK (python/synopkgland_app).", "Point SYNOPKGLAND_SDK_DIR at a syno-pkg-land-sdk checkout that has python/synopkgland_app, or require an SDK release that ships it."],
|
|
784
820
|
["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."],
|
|
785
821
|
])
|
|
786
822
|
ERROR_REGISTRY[code] = { code, description, suggestion };
|
package/dist/host/dsm-client.js
CHANGED
|
@@ -186,17 +186,14 @@ function createDsmNasHost(credentials, profileName) {
|
|
|
186
186
|
throw e;
|
|
187
187
|
return call("SYNO.Core.TaskScheduler", method, legacyVersion, legacyParams);
|
|
188
188
|
});
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
throw new Error("Invalid EventScheduler readback.");
|
|
189
|
+
// EventScheduler keys a task by its unique name; get answers no data for a missing one.
|
|
190
|
+
const bootName = `${packageIdentity} executor boot`;
|
|
192
191
|
const tasks = [];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const detail = await call("SYNO.Core.EventScheduler", "get", 1, { task_name: summary.task_name });
|
|
197
|
-
if (!detail || detail.task_name !== summary.task_name)
|
|
192
|
+
const boot = await call("SYNO.Core.EventScheduler", "get", 1, { task_name: bootName });
|
|
193
|
+
if (boot !== undefined && boot !== null && !(typeof boot === "object" && Object.keys(boot).length === 0)) {
|
|
194
|
+
if (boot.task_name !== bootName)
|
|
198
195
|
throw new Error("Invalid boot task readback.");
|
|
199
|
-
tasks.push({ ...
|
|
196
|
+
tasks.push({ ...boot, name: boot.task_name, kind: "boot" });
|
|
200
197
|
}
|
|
201
198
|
const listed = await taskRead("list", 3, { start: 0, limit: 500 }, 2, { offset: 0, limit: 500 });
|
|
202
199
|
if (!Array.isArray(listed?.tasks) || !Number.isInteger(listed.total) || listed.total !== listed.tasks.length || listed.total > 500)
|
package/dist/init.js
CHANGED
|
@@ -5,10 +5,11 @@ import { capabilityNames, capabilitySettings } from "./capabilities.js";
|
|
|
5
5
|
import { MOVE_FROM_STAGING } from './runtime/delegate-contract.js';
|
|
6
6
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
8
|
+
import { APP_SCRIPTS_DIR } from "./standalone/app-scripts.js";
|
|
8
9
|
import { SDK_MODULE, SDK_PACKAGE, sdkSource } from "./runtime/sdk-source.js";
|
|
9
10
|
import { templatesRoot } from "./template-paths.js";
|
|
10
11
|
import { BASE_BORROWED, BRIDGE_TEMPLATES, borrowedFiles, borrowedTarget, capabilityOverlays, VUE_OVERLAYS } from "./capability-overlays.js";
|
|
11
|
-
import {
|
|
12
|
+
import { copyNodeSdk, copyPythonSdk, NODE_SDK_PACKAGE } from "./standalone/bridge-app.js";
|
|
12
13
|
import { API_PERMISSIONS_FILE } from "./standalone/api-permissions.js";
|
|
13
14
|
import { resolveMinDsm, assertContainerMinDsm, DEFAULT_MIN_DSM, isDsmAtLeast } from "./build-spk/dsm-version.js";
|
|
14
15
|
import { readSharedFolder } from "./standalone/shared-folder.js";
|
|
@@ -135,7 +136,9 @@ export function initFromTemplate(template, targetDir, options = {}) {
|
|
|
135
136
|
// Shared legal resources also live in the CLI package; only the template
|
|
136
137
|
// terms are an unconditional part of a generated project.
|
|
137
138
|
const shared = join(templatesRoot(), "_shared");
|
|
138
|
-
|
|
139
|
+
// dsm-scripts/: the DSM lifecycle scripts every project starts with (doing nothing); older templates have none.
|
|
140
|
+
const sharedScripts = existsSync(join(shared, APP_SCRIPTS_DIR)) ? readdirSync(join(shared, APP_SCRIPTS_DIR)).sort().map(name => `${APP_SCRIPTS_DIR}/${name}`) : [];
|
|
141
|
+
const sharedFiles = ["LICENSES/TEMPLATE.txt", "AGENTS.md", "CLAUDE.md", ...sharedScripts];
|
|
139
142
|
const files = [...templateFiles(source), ...sharedFiles, ...overlays.flatMap(root => templateFiles(root)),
|
|
140
143
|
...borrowed.map(borrowedTarget), ...(ui === "vue" ? ["frontend/third-party-notices.mjs"] : [])];
|
|
141
144
|
const collisions = files.map(targetName).filter((file) => existsSync(join(targetDir, file)));
|
|
@@ -144,9 +147,10 @@ export function initFromTemplate(template, targetDir, options = {}) {
|
|
|
144
147
|
}
|
|
145
148
|
mkdirSync(targetDir, { recursive: true });
|
|
146
149
|
copyTemplateTree(source, targetDir);
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
for (const file of sharedFiles) {
|
|
151
|
+
mkdirSync(dirname(join(targetDir, file)), { recursive: true });
|
|
149
152
|
cpSync(join(shared, file), join(targetDir, file));
|
|
153
|
+
}
|
|
150
154
|
for (const overlay of overlays)
|
|
151
155
|
copyTemplateTree(overlay, targetDir);
|
|
152
156
|
for (const dir of (ui === "vue" ? ["frontend"] : [])) {
|
|
@@ -275,7 +279,7 @@ function vendorAppSdk(template, targetDir) {
|
|
|
275
279
|
const sdk = sdkSource(version).goDir;
|
|
276
280
|
const vendor = join(targetDir, "backend/vendor");
|
|
277
281
|
if (template === "native-python")
|
|
278
|
-
return
|
|
282
|
+
return copyPythonSdk(sdk, join(vendor, "synopkgland_app"));
|
|
279
283
|
const target = join(vendor, NODE_SDK_PACKAGE);
|
|
280
284
|
copyNodeSdk(join(sdk, "node"), target);
|
|
281
285
|
const { devDependencies: _dev, scripts: _scripts, ...pkg } = JSON.parse(readFileSync(join(target, "package.json"), "utf8"));
|
|
@@ -1,94 +1,4 @@
|
|
|
1
|
-
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities.
|
|
1
|
+
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities.
|
|
2
|
+
* The DTOs and the golden vector live in the SDK (ts/src/delegate/long-running.ts). */
|
|
2
3
|
export declare const MOVE_FROM_STAGING: "files.moveFromStaging";
|
|
3
4
|
export declare const DELEGATE_EXTENSION_OPERATIONS: readonly ["files.moveFromStaging", "files.operationStatus", "files.cancelOperation", "delegate.disable", "delegate.enable", "delegate.audit"];
|
|
4
|
-
export declare const DELEGATE_OPERATION_STATES: readonly ["queued", "copying", "committed", "canceled", "interrupted", "uncertain"];
|
|
5
|
-
export type DelegateOperationState = typeof DELEGATE_OPERATION_STATES[number];
|
|
6
|
-
export interface LongRunningDelegateCapability {
|
|
7
|
-
scope: string;
|
|
8
|
-
operations: [typeof MOVE_FROM_STAGING];
|
|
9
|
-
destinations: string[];
|
|
10
|
-
lifetime: 'until-revoked';
|
|
11
|
-
}
|
|
12
|
-
export interface SelectDestination {
|
|
13
|
-
key: string;
|
|
14
|
-
path: string;
|
|
15
|
-
}
|
|
16
|
-
/** Protected executor selection, not the package-owned installer draft. */
|
|
17
|
-
export interface ScopeSelection {
|
|
18
|
-
scope: string;
|
|
19
|
-
root: string;
|
|
20
|
-
generation: number;
|
|
21
|
-
}
|
|
22
|
-
export interface DestinationState {
|
|
23
|
-
key: string;
|
|
24
|
-
selection: ScopeSelection | null;
|
|
25
|
-
destination: DestinationGrant | null;
|
|
26
|
-
status: 'active' | 'missing' | 'reselection-required' | 'needs-scope-confirmation' | 'disabled';
|
|
27
|
-
}
|
|
28
|
-
export interface DestinationGrant {
|
|
29
|
-
id: string;
|
|
30
|
-
key: string;
|
|
31
|
-
path: string;
|
|
32
|
-
username: string;
|
|
33
|
-
lifetime: 'until-revoked';
|
|
34
|
-
}
|
|
35
|
-
/** Owner/UID/destination are resolved from the protected grant, never supplied here. */
|
|
36
|
-
export interface MoveFromStaging {
|
|
37
|
-
delegation_id: string;
|
|
38
|
-
job_id: string;
|
|
39
|
-
staging: string;
|
|
40
|
-
/**
|
|
41
|
-
* File name, or folders below the destination and a file name, for example
|
|
42
|
-
* `Album/CD1/01.flac`. Missing folders are created as the user, at most
|
|
43
|
-
* `DELEGATE_LIMITS.maxDestinationFolders` deep and `maxDestinationBytes` long.
|
|
44
|
-
*/
|
|
45
|
-
name: string;
|
|
46
|
-
size: number;
|
|
47
|
-
/**
|
|
48
|
-
* When the name is taken: `fail` (default) refuses with `destination-exists`,
|
|
49
|
-
* `overwrite` atomically replaces a regular file the user can write, `rename`
|
|
50
|
-
* publishes `name (n).ext` (n up to 99) and the receipt reports that path.
|
|
51
|
-
*/
|
|
52
|
-
conflict?: DelegateConflictPolicy;
|
|
53
|
-
}
|
|
54
|
-
export declare const DELEGATE_CONFLICT_POLICIES: readonly ["fail", "overwrite", "rename"];
|
|
55
|
-
export type DelegateConflictPolicy = typeof DELEGATE_CONFLICT_POLICIES[number];
|
|
56
|
-
export interface OperationRef {
|
|
57
|
-
delegation_id: string;
|
|
58
|
-
job_id: string;
|
|
59
|
-
}
|
|
60
|
-
export interface OperationResult {
|
|
61
|
-
job_id: string;
|
|
62
|
-
state: DelegateOperationState;
|
|
63
|
-
/** Only a committed executor receipt may supply a final destination path. */
|
|
64
|
-
receipt?: {
|
|
65
|
-
path: string;
|
|
66
|
-
size: number;
|
|
67
|
-
};
|
|
68
|
-
code?: string;
|
|
69
|
-
reason?: string;
|
|
70
|
-
}
|
|
71
|
-
export declare const DELEGATE_ROUTES: {
|
|
72
|
-
readonly 'delegate.example.export': "authenticated";
|
|
73
|
-
readonly 'delegate.destination': "authenticated";
|
|
74
|
-
readonly 'delegate.select_destination': "authenticated";
|
|
75
|
-
readonly 'delegate.move_from_staging': "authenticated";
|
|
76
|
-
readonly 'delegate.operations.get': "authenticated";
|
|
77
|
-
readonly 'delegate.operations.list': "authenticated";
|
|
78
|
-
readonly 'delegate.operations.cancel': "authenticated";
|
|
79
|
-
readonly 'delegate.disable': "administrators";
|
|
80
|
-
readonly 'delegate.enable': "administrators";
|
|
81
|
-
readonly 'delegate.audit': "authenticated";
|
|
82
|
-
};
|
|
83
|
-
/** Shared, explicit limits; legacy TTL/job limits remain separate. */
|
|
84
|
-
export declare const DELEGATE_LIMITS: {
|
|
85
|
-
readonly maxFileBytes: number;
|
|
86
|
-
readonly maxQueued: 16;
|
|
87
|
-
readonly maxActive: 1;
|
|
88
|
-
readonly maxCopySeconds: 86400;
|
|
89
|
-
readonly maxOperationBytes: number;
|
|
90
|
-
readonly maxAuditBytes: number;
|
|
91
|
-
readonly revalidateSeconds: 5;
|
|
92
|
-
readonly maxDestinationFolders: 16;
|
|
93
|
-
readonly maxDestinationBytes: 1024;
|
|
94
|
-
};
|
|
@@ -1,29 +1,7 @@
|
|
|
1
|
-
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities.
|
|
1
|
+
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities.
|
|
2
|
+
* The DTOs and the golden vector live in the SDK (ts/src/delegate/long-running.ts). */
|
|
2
3
|
export const MOVE_FROM_STAGING = 'files.moveFromStaging';
|
|
3
4
|
export const DELEGATE_EXTENSION_OPERATIONS = [
|
|
4
5
|
MOVE_FROM_STAGING, 'files.operationStatus', 'files.cancelOperation',
|
|
5
6
|
'delegate.disable', 'delegate.enable', 'delegate.audit',
|
|
6
7
|
];
|
|
7
|
-
export const DELEGATE_OPERATION_STATES = [
|
|
8
|
-
'queued', 'copying', 'committed', 'canceled', 'interrupted', 'uncertain',
|
|
9
|
-
];
|
|
10
|
-
export const DELEGATE_CONFLICT_POLICIES = ['fail', 'overwrite', 'rename'];
|
|
11
|
-
export const DELEGATE_ROUTES = {
|
|
12
|
-
'delegate.example.export': 'authenticated',
|
|
13
|
-
'delegate.destination': 'authenticated',
|
|
14
|
-
'delegate.select_destination': 'authenticated',
|
|
15
|
-
'delegate.move_from_staging': 'authenticated',
|
|
16
|
-
'delegate.operations.get': 'authenticated',
|
|
17
|
-
'delegate.operations.list': 'authenticated',
|
|
18
|
-
'delegate.operations.cancel': 'authenticated',
|
|
19
|
-
'delegate.disable': 'administrators',
|
|
20
|
-
'delegate.enable': 'administrators',
|
|
21
|
-
'delegate.audit': 'authenticated',
|
|
22
|
-
};
|
|
23
|
-
/** Shared, explicit limits; legacy TTL/job limits remain separate. */
|
|
24
|
-
export const DELEGATE_LIMITS = {
|
|
25
|
-
maxFileBytes: 16 * 1024 ** 3, maxQueued: 16, maxActive: 1,
|
|
26
|
-
maxCopySeconds: 86400, maxOperationBytes: 10 * 1024 ** 2,
|
|
27
|
-
maxAuditBytes: 50 * 1024 ** 2, revalidateSeconds: 5,
|
|
28
|
-
maxDestinationFolders: 16, maxDestinationBytes: 1024,
|
|
29
|
-
};
|
|
@@ -31,5 +31,16 @@
|
|
|
31
31
|
"armv7": "cafc97c89f5645c14a8cf31d6d9f258394ed6f2d086478a7aebd04d57c274b4e"
|
|
32
32
|
},
|
|
33
33
|
"goToolchain": "go1.26.9"
|
|
34
|
+
},
|
|
35
|
+
"0.10.3": {
|
|
36
|
+
"x86_64": "4ba7040761a719e74d6b047ef68b9415e658ebb8b373b69555b8c307524e26ba",
|
|
37
|
+
"aarch64": "ba80b389e3291419389b66be7b0a0e1085d3dfec55c9c651fecae427279bb3d5",
|
|
38
|
+
"armv7": "49474467b9c9a7f31b30b5053b70eb4f71a95f2c06fbf3566e8de8f16972a299",
|
|
39
|
+
"lifecycle": {
|
|
40
|
+
"x86_64": "d65500c4f04713a32b45d360059281391acfa5e00ee24895fdd2de7b82735372",
|
|
41
|
+
"aarch64": "94f6cd119186ff482b8aea56541dd1f9d53c147566bc1efdca6f5a1c86198331",
|
|
42
|
+
"armv7": "cafc97c89f5645c14a8cf31d6d9f258394ed6f2d086478a7aebd04d57c274b4e"
|
|
43
|
+
},
|
|
44
|
+
"goToolchain": "go1.26.9"
|
|
34
45
|
}
|
|
35
46
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type StandaloneManifest } from '../standalone/manifest.js';
|
|
2
2
|
/**
|
|
3
3
|
* Why the SDK executor would refuse the policy, such as `invalid policy: policy root`;
|
|
4
|
-
* undefined when it accepts it
|
|
4
|
+
* undefined when it accepts it. The executor is
|
|
5
5
|
* built for this host from executorDir, so its own rules decide, not a copy in the CLI.
|
|
6
6
|
*/
|
|
7
7
|
export declare function executorPolicyRefusal(executorDir: string, pkg: string, policyText: string, env?: NodeJS.ProcessEnv): string | undefined;
|