@nocobase/app-cli-distribution 0.0.0-stage → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +129 -0
- package/README.md +107 -2
- package/dist/artifacts.d.ts +72 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +239 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/download-tokens.d.ts +57 -0
- package/dist/download-tokens.d.ts.map +1 -0
- package/dist/download-tokens.js +66 -0
- package/dist/download-tokens.js.map +1 -0
- package/dist/http.d.ts +3 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +19 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +41 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +318 -0
- package/dist/install.js.map +1 -0
- package/dist/public-install.d.ts +17 -0
- package/dist/public-install.d.ts.map +1 -0
- package/dist/public-install.js +164 -0
- package/dist/public-install.js.map +1 -0
- package/dist/routes.d.ts +29 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +199 -0
- package/dist/routes.js.map +1 -0
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +25 -0
- package/dist/schemas.js.map +1 -0
- package/package.json +54 -3
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { DIST_PRODUCT_PATTERN } from '@nocobase/agent-protocol';
|
|
2
|
+
import { npmLauncherScript } from '@nocobase/app-cli-client/install';
|
|
3
|
+
import { apiErrorResponse, cliRoute, describeRoute, } from '@nocobase/app-server/router';
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
import { requestedServer } from './install.js';
|
|
6
|
+
const SAFE_SERVER = /^https?:\/\/[A-Za-z0-9.:[\]_-]+(?:\/[A-Za-z0-9._~/-]*)?$/u;
|
|
7
|
+
/** A separate CLI-only installer. It neither changes nor extends the protected installation protocol. */
|
|
8
|
+
export function publicInstallScript(options) {
|
|
9
|
+
if (!DIST_PRODUCT_PATTERN.test(options.cli) ||
|
|
10
|
+
options.cli === 'nocobase-runner')
|
|
11
|
+
throw new Error(`Not a CLI command name: ${options.cli}`);
|
|
12
|
+
const server = options.server ?? '';
|
|
13
|
+
return PUBLIC_INSTALL_SCRIPT.replaceAll('@CLI@', options.cli).replace('@DEFAULT_SERVER@', SAFE_SERVER.test(server) ? server.replace(/\/+$/u, '') : '');
|
|
14
|
+
}
|
|
15
|
+
/** Mount explicitly alongside a host's independently configured public artifact admission. */
|
|
16
|
+
export function createPublicInstallRoutes(options) {
|
|
17
|
+
return new Hono().get('/installScript', describeRoute(options.openapi ?? {
|
|
18
|
+
tags: ['CliDistribution'],
|
|
19
|
+
summary: 'Get the public CLI install script',
|
|
20
|
+
operationId: 'cliDistributionPublicInstallScript',
|
|
21
|
+
...cliRoute(false),
|
|
22
|
+
security: [],
|
|
23
|
+
responses: {
|
|
24
|
+
200: {
|
|
25
|
+
description: 'A public CLI-only POSIX shell script.',
|
|
26
|
+
content: { 'text/x-shellscript': { schema: { type: 'string' } } },
|
|
27
|
+
},
|
|
28
|
+
500: apiErrorResponse(500),
|
|
29
|
+
},
|
|
30
|
+
}), (context) => context.body(publicInstallScript({
|
|
31
|
+
cli: options.cli(),
|
|
32
|
+
server: requestedServer(context, options),
|
|
33
|
+
}), 200, {
|
|
34
|
+
'content-type': 'text/x-shellscript; charset=utf-8',
|
|
35
|
+
'cache-control': 'no-cache',
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
// Kept independent of the protected script and its extension points, intentionally allowing some duplication.
|
|
39
|
+
const PUBLIC_INSTALL_SCRIPT = `#!/bin/sh
|
|
40
|
+
# Installs the publicly downloadable @CLI@ CLI. Business access requires a separate login.
|
|
41
|
+
set -eu
|
|
42
|
+
usage() {
|
|
43
|
+
cat <<'USAGE'
|
|
44
|
+
Usage: install.sh [--server <url>] [--prefix <dir>] [--bin-dir <dir>] [--dry-run]
|
|
45
|
+
|
|
46
|
+
Requires Node.js 24+, curl, tar and a SHA-256 tool. Supports macOS/Linux and Windows through WSL.
|
|
47
|
+
USAGE
|
|
48
|
+
}
|
|
49
|
+
cli=@CLI@
|
|
50
|
+
server="@DEFAULT_SERVER@"
|
|
51
|
+
prefix="\${NOCOBASE_CLI_INSTALL_DIR:-}"
|
|
52
|
+
bin_dir="\${NOCOBASE_CLI_BIN_DIR:-}"
|
|
53
|
+
dry_run=0
|
|
54
|
+
while [ $# -gt 0 ]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--server|--prefix|--bin-dir)
|
|
57
|
+
[ $# -ge 2 ] && [ -n "$2" ] || { usage >&2; exit 2; }
|
|
58
|
+
case "$1" in
|
|
59
|
+
--server) server="$2" ;;
|
|
60
|
+
--prefix) prefix="$2" ;;
|
|
61
|
+
--bin-dir) bin_dir="$2" ;;
|
|
62
|
+
esac
|
|
63
|
+
shift 2 ;;
|
|
64
|
+
--dry-run) dry_run=1; shift ;;
|
|
65
|
+
-h|--help) usage; exit 0 ;;
|
|
66
|
+
*) echo 'Unknown installation option.' >&2; usage >&2; exit 2 ;;
|
|
67
|
+
esac
|
|
68
|
+
done
|
|
69
|
+
[ -n "$server" ] || { usage >&2; exit 2; }
|
|
70
|
+
say() { printf '%s\\n' "$*"; }
|
|
71
|
+
fail() { printf '%s install: %s\\n' "$cli" "$*" >&2; exit 1; }
|
|
72
|
+
run() { if [ "$dry_run" = 1 ]; then say "+ $*"; else "$@"; fi; }
|
|
73
|
+
case "$(uname -s)" in
|
|
74
|
+
Darwin) os=darwin ;;
|
|
75
|
+
Linux) os=linux ;;
|
|
76
|
+
*) fail 'Use macOS, Linux or WSL.' ;;
|
|
77
|
+
esac
|
|
78
|
+
case "$(uname -m)" in
|
|
79
|
+
arm64|aarch64) arch=arm64 ;;
|
|
80
|
+
x86_64|amd64) arch=x64 ;;
|
|
81
|
+
*) fail 'Unsupported processor.' ;;
|
|
82
|
+
esac
|
|
83
|
+
target="\${NOCOBASE_CLI_TARGET:-$os-$arch}"
|
|
84
|
+
node_bin="$(command -v node 2>/dev/null || true)"
|
|
85
|
+
[ -n "$node_bin" ] || fail 'Node.js 24 or newer is required.'
|
|
86
|
+
node_version="$("$node_bin" -p 'process.versions.node' 2>/dev/null || true)"
|
|
87
|
+
case "$node_version" in ''|*[!0-9.]*) fail 'Node.js 24 or newer is required.' ;; esac
|
|
88
|
+
[ "\${node_version%%.*}" -ge 24 ] || fail 'Node.js 24 or newer is required.'
|
|
89
|
+
# Validate without printing an invalid address, which could accidentally contain a credential.
|
|
90
|
+
server="$("$node_bin" -e 'try { const u = new URL(process.argv[1]); if (!["http:", "https:"].includes(u.protocol) || u.username || u.password || u.search || u.hash) throw Error(); console.log(u.href.replace(/\\/+$/, "")); } catch { process.exit(2); }' "$server")" || { echo 'Use an HTTP(S) application URL without credentials, query or fragment.' >&2; exit 2; }
|
|
91
|
+
prefix="\${prefix:-$HOME/.local/share/$cli}"
|
|
92
|
+
bin_dir="\${bin_dir:-$HOME/.local/bin}"
|
|
93
|
+
prefix="$("$node_bin" -e 'console.log(require("node:path").resolve(process.argv[1]))' "$prefix")"
|
|
94
|
+
bin_dir="$("$node_bin" -e 'console.log(require("node:path").resolve(process.argv[1]))' "$bin_dir")"
|
|
95
|
+
command -v curl >/dev/null 2>&1 || fail 'curl is required.'
|
|
96
|
+
command -v tar >/dev/null 2>&1 || fail 'tar is required.'
|
|
97
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
98
|
+
sha256() { sha256sum "$1" | cut -d ' ' -f 1; }
|
|
99
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
100
|
+
sha256() { shasum -a 256 "$1" | cut -d ' ' -f 1; }
|
|
101
|
+
else
|
|
102
|
+
fail 'sha256sum or shasum is required.'
|
|
103
|
+
fi
|
|
104
|
+
tmp="$(mktemp -d)"
|
|
105
|
+
trap 'rm -rf "$tmp"' EXIT INT TERM
|
|
106
|
+
field() { sed -n "s/^$1=//p" "$tmp/resolve" | head -n 1; }
|
|
107
|
+
status="$(curl -sS -o "$tmp/resolve" -w '%{http_code}' "$server/api/agents/dist/products/$cli/targets/$target?format=env&accept=npm")" || fail 'Could not reach the application.'
|
|
108
|
+
[ "$status" = 200 ] || fail "The application answered $status while resolving the CLI."
|
|
109
|
+
version="$(field version)"
|
|
110
|
+
case "$version" in ''|.|..|*[!0-9A-Za-z.+-]*) fail 'The application named an unexpected version.' ;; esac
|
|
111
|
+
kind="$(field kind)"
|
|
112
|
+
dest="$prefix/versions/$version"
|
|
113
|
+
if [ "$kind" = npm ]; then
|
|
114
|
+
package="$(field package)"
|
|
115
|
+
case "$package" in ''|*[!0-9a-z@/._~-]*) fail 'The application named an unexpected npm package.' ;; esac
|
|
116
|
+
npm_bin="\${NOCOBASE_NPM:-$(command -v npm 2>/dev/null || true)}"
|
|
117
|
+
[ -n "$npm_bin" ] && [ -x "$npm_bin" ] || fail 'npm is required for this package.'
|
|
118
|
+
else
|
|
119
|
+
url="$(field url)"
|
|
120
|
+
checksum="$(field sha256)"
|
|
121
|
+
case "$url" in /*) ;; *) fail 'The application named an unexpected download.' ;; esac
|
|
122
|
+
case "$checksum" in *[!0-9a-f]*|'') fail 'The application named an unexpected checksum.' ;; esac
|
|
123
|
+
fi
|
|
124
|
+
if [ ! -x "$dest/bin/$cli" ]; then
|
|
125
|
+
if [ "$kind" = npm ]; then
|
|
126
|
+
run rm -rf "$dest.partial"
|
|
127
|
+
run mkdir -p "$dest.partial"
|
|
128
|
+
run "$npm_bin" install --prefix "$dest.partial" --no-save --no-audit --no-fund --omit=optional "$package@$version"
|
|
129
|
+
if [ "$dry_run" = 0 ]; then
|
|
130
|
+
[ -e "$dest.partial/node_modules/.bin/$cli" ] || fail 'The package provides no CLI command.'
|
|
131
|
+
mkdir -p "$dest.partial/bin"
|
|
132
|
+
cat <<'LAUNCHER' >"$dest.partial/bin/$cli"
|
|
133
|
+
${npmLauncherScript()}LAUNCHER
|
|
134
|
+
chmod 755 "$dest.partial/bin/$cli"
|
|
135
|
+
fi
|
|
136
|
+
run mv "$dest.partial" "$dest"
|
|
137
|
+
else
|
|
138
|
+
run curl -fSL --progress-bar -o "$tmp/cli.tar.gz" "$server$url" || fail 'CLI download failed.'
|
|
139
|
+
if [ "$dry_run" = 0 ]; then
|
|
140
|
+
actual="$(sha256 "$tmp/cli.tar.gz")"
|
|
141
|
+
[ "$actual" = "$checksum" ] || fail 'The download does not match its SHA-256.'
|
|
142
|
+
say 'Checksum verified.'
|
|
143
|
+
fi
|
|
144
|
+
run rm -rf "$dest.partial"
|
|
145
|
+
run mkdir -p "$dest.partial"
|
|
146
|
+
run tar -xzf "$tmp/cli.tar.gz" -C "$dest.partial" --strip-components=1 || fail 'Could not unpack the CLI archive.'
|
|
147
|
+
if [ "$dry_run" = 0 ]; then
|
|
148
|
+
[ -x "$dest.partial/bin/$cli" ] || fail 'The archive provides no CLI command.'
|
|
149
|
+
fi
|
|
150
|
+
run mv "$dest.partial" "$dest"
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
run ln -sfn "versions/$version" "$prefix/current"
|
|
154
|
+
run ln -sfn "$node_bin" "$prefix/node"
|
|
155
|
+
run mkdir -p "$bin_dir"
|
|
156
|
+
run ln -sfn "$prefix/current/bin/$cli" "$bin_dir/$cli"
|
|
157
|
+
if [ "$dry_run" = 0 ]; then
|
|
158
|
+
"$node_bin" -e 'console.log(JSON.stringify({prefix: process.argv[1], binLink: process.argv[2], mode: "cli"}, null, 2))' "$prefix" "$bin_dir/$cli" >"$prefix/install.json"
|
|
159
|
+
fi
|
|
160
|
+
if [ "$dry_run" = 1 ]; then say 'Dry run: no installation was changed.'; else say "$cli is installed: $bin_dir/$cli"; fi
|
|
161
|
+
case ":$PATH:" in *":$bin_dir:"*) ;; *) say "Add $bin_dir to your PATH." ;; esac
|
|
162
|
+
say "Next: $cli login --server $server"
|
|
163
|
+
`;
|
|
164
|
+
//# sourceMappingURL=public-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-install.js","sourceRoot":"","sources":["../src/public-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,aAAa,GAEd,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,GAAG,2DAA2D,CAAC;AAOhF,yGAAyG;AACzG,MAAM,UAAU,mBAAmB,CACjC,OAAmC;IAEnC,IACE,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;QACvC,OAAO,CAAC,GAAG,KAAK,iBAAiB;QAEjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACpC,OAAO,qBAAqB,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CACnE,kBAAkB,EAClB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC;AACJ,CAAC;AASD,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CACvC,OAAmC;IAEnC,OAAO,IAAI,IAAI,EAAE,CAAC,GAAG,CACnB,gBAAgB,EAChB,aAAa,CACX,OAAO,CAAC,OAAO,IAAI;QACjB,IAAI,EAAE,CAAC,iBAAiB,CAAC;QACzB,OAAO,EAAE,mCAAmC;QAC5C,WAAW,EAAE,oCAAoC;QACjD,GAAG,QAAQ,CAAC,KAAK,CAAC;QAClB,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE;YACT,GAAG,EAAE;gBACH,WAAW,EAAE,uCAAuC;gBACpD,OAAO,EAAE,EAAE,oBAAoB,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;aAClE;YACD,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC;SAC3B;KACF,CACF,EACD,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,CAAC,IAAI,CACV,mBAAmB,CAAC;QAClB,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC;KAC1C,CAAC,EACF,GAAG,EACH;QACE,cAAc,EAAE,mCAAmC;QACnD,eAAe,EAAE,UAAU;KAC5B,CACF,CACJ,CAAC;AACJ,CAAC;AAED,8GAA8G;AAC9G,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8F5B,iBAAiB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BpB,CAAC"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type DistArtifact, type DistNpmPackage, type DistResolution } from '@nocobase/agent-protocol';
|
|
2
|
+
import type { Context, Hono, MiddlewareHandler } from 'hono';
|
|
3
|
+
import type { DistService } from './artifacts.js';
|
|
4
|
+
import type { DownloadTokenService } from './download-tokens.js';
|
|
5
|
+
export type Admission = (context: Context) => Promise<boolean>;
|
|
6
|
+
export interface DistributionRoutesOptions {
|
|
7
|
+
readonly artifacts: DistService;
|
|
8
|
+
/** Explicit allowed products, or 'all' when the host intentionally serves every configured product. */
|
|
9
|
+
readonly products: readonly string[] | 'all';
|
|
10
|
+
readonly admissions?: readonly Admission[];
|
|
11
|
+
readonly fallback?: MiddlewareHandler;
|
|
12
|
+
readonly mint?: {
|
|
13
|
+
readonly guard: MiddlewareHandler;
|
|
14
|
+
readonly tokens: DownloadTokenService;
|
|
15
|
+
readonly createdBy: (context: Context) => string | null;
|
|
16
|
+
};
|
|
17
|
+
readonly openapi?: Partial<Record<'mint' | 'manifest' | 'resolve' | 'file', import('@nocobase/app-server/router').DescribeRouteOptions>>;
|
|
18
|
+
readonly unauthorizedMessage?: string;
|
|
19
|
+
}
|
|
20
|
+
/** `key=value` lines a POSIX shell reads without a JSON parser. Values never contain a newline. */
|
|
21
|
+
export declare function artifactEnv(artifact: DistArtifact): string;
|
|
22
|
+
/** The same lines for an npm answer, led by `kind=npm`; only a caller that sent `accept=npm` reads them. */
|
|
23
|
+
export declare function npmPackageEnv(npm: DistNpmPackage): string;
|
|
24
|
+
/** `artifactEnv` or `npmPackageEnv`, whichever the answer is. */
|
|
25
|
+
export declare function resolutionEnv(resolution: DistResolution): string;
|
|
26
|
+
/** A token authorizes before file lookup, then binds/counts the actual artifact's platform. */
|
|
27
|
+
export declare function downloadTokenAdmission(tokens: DownloadTokenService, artifacts: DistService): Admission;
|
|
28
|
+
export declare function createDistributionRoutes(options: DistributionRoutesOptions): Hono;
|
|
29
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAWL,KAAK,YAAY,EAEjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAUlC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAI7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AASjE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAC/D,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,uGAAuG;IACvG,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;QAClC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;QACtC,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;KACzD,CAAC;IACF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CACxB,MAAM,CACJ,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,EACxC,OAAO,6BAA6B,EAAE,oBAAoB,CAC3D,CACF,CAAC;IACF,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED,mGAAmG;AACnG,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAc1D;AAED,4GAA4G;AAC5G,wBAAgB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAUzD;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,MAAM,CAIhE;AAED,+FAA+F;AAC/F,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,WAAW,GACrB,SAAS,CA0BX;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,yBAAyB,GACjC,IAAI,CA2LN"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { DIST_ACCEPT_NPM, DIST_TARGET_PATTERN, distAccepts, DistArtifactSchema, DistManifestSchema, DistNpmPackageSchema, DistResolutionSchema, HEADERS, isDistNpmPackage, ProtocolError, UNIVERSAL_TARGET, } from '@nocobase/agent-protocol';
|
|
3
|
+
import { apiErrorResponse, apiErrorResponses, apiValidator, dataResponse, cliRoute, describeRoute, resolver, } from '@nocobase/app-server/router';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { distributionRouter } from './http.js';
|
|
6
|
+
import { DistFileParams, DistTargetParams, DistTargetQuery, DownloadTokenSchema, } from './schemas.js';
|
|
7
|
+
/** `key=value` lines a POSIX shell reads without a JSON parser. Values never contain a newline. */
|
|
8
|
+
export function artifactEnv(artifact) {
|
|
9
|
+
const parsed = DistArtifactSchema.parse(artifact);
|
|
10
|
+
return [
|
|
11
|
+
`product=${parsed.product}`,
|
|
12
|
+
`version=${parsed.version}`,
|
|
13
|
+
`target=${parsed.target}`,
|
|
14
|
+
`url=${parsed.url}`,
|
|
15
|
+
`sha256=${parsed.sha256}`,
|
|
16
|
+
`size=${parsed.size}`,
|
|
17
|
+
`channel=${parsed.channel}`,
|
|
18
|
+
// Only for the universal tarball, so the answer for a tarball built for the platform stays as it was.
|
|
19
|
+
...(parsed.universal === true ? ['universal=true'] : []),
|
|
20
|
+
'',
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
/** The same lines for an npm answer, led by `kind=npm`; only a caller that sent `accept=npm` reads them. */
|
|
24
|
+
export function npmPackageEnv(npm) {
|
|
25
|
+
const parsed = DistNpmPackageSchema.parse(npm);
|
|
26
|
+
return [
|
|
27
|
+
`kind=${parsed.kind}`,
|
|
28
|
+
`product=${parsed.product}`,
|
|
29
|
+
`version=${parsed.version}`,
|
|
30
|
+
`package=${parsed.package}`,
|
|
31
|
+
`channel=${parsed.channel}`,
|
|
32
|
+
'',
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
/** `artifactEnv` or `npmPackageEnv`, whichever the answer is. */
|
|
36
|
+
export function resolutionEnv(resolution) {
|
|
37
|
+
return isDistNpmPackage(resolution)
|
|
38
|
+
? npmPackageEnv(resolution)
|
|
39
|
+
: artifactEnv(resolution);
|
|
40
|
+
}
|
|
41
|
+
/** A token authorizes before file lookup, then binds/counts the actual artifact's platform. */
|
|
42
|
+
export function downloadTokenAdmission(tokens, artifacts) {
|
|
43
|
+
/** What a download token is asked for on this request: the manifest, a platform, or a tarball (which counts). */
|
|
44
|
+
return async (context) => {
|
|
45
|
+
const token = context.req.header(HEADERS.downloadToken);
|
|
46
|
+
if (!token)
|
|
47
|
+
return false;
|
|
48
|
+
const { product, target, version, file } = context.req.param();
|
|
49
|
+
if (version !== undefined && file !== undefined && product !== undefined) {
|
|
50
|
+
// Checked before the file is looked up, so an invalid token learns nothing about which files there are.
|
|
51
|
+
await tokens.admit(token, { product });
|
|
52
|
+
const found = await artifacts.file(product, version, file);
|
|
53
|
+
// The universal tarball serves every platform, so it downloads under whichever one the token is bound to.
|
|
54
|
+
await tokens.admit(token, {
|
|
55
|
+
product,
|
|
56
|
+
...(found.target === UNIVERSAL_TARGET ? {} : { target: found.target }),
|
|
57
|
+
download: true,
|
|
58
|
+
});
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
await tokens.admit(token, {
|
|
62
|
+
...(product === undefined ? {} : { product }),
|
|
63
|
+
...(target === undefined ? {} : { target }),
|
|
64
|
+
});
|
|
65
|
+
return true;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function createDistributionRoutes(options) {
|
|
69
|
+
const products = options.products;
|
|
70
|
+
if (products !== 'all' && !Array.isArray(products))
|
|
71
|
+
throw new Error("products must explicitly be an array or 'all'.");
|
|
72
|
+
const router = distributionRouter();
|
|
73
|
+
const artifacts = options.artifacts;
|
|
74
|
+
const allowed = async (context, next) => {
|
|
75
|
+
for (const admission of options.admissions ?? []) {
|
|
76
|
+
if (await admission(context))
|
|
77
|
+
return next();
|
|
78
|
+
}
|
|
79
|
+
if (options.fallback)
|
|
80
|
+
return options.fallback(context, next);
|
|
81
|
+
throw new ProtocolError('UNAUTHORIZED', options.unauthorizedMessage ??
|
|
82
|
+
'A download credential or sign-in is required.');
|
|
83
|
+
};
|
|
84
|
+
const productAllowed = async (context, next) => {
|
|
85
|
+
const product = context.req.param('product');
|
|
86
|
+
if (product !== undefined &&
|
|
87
|
+
products !== 'all' &&
|
|
88
|
+
!products.includes(product))
|
|
89
|
+
throw new ProtocolError('NOT_FOUND', `This application serves no ${product} on the ${artifacts.channel} channel. Build it with nocobase cli build.`);
|
|
90
|
+
return next();
|
|
91
|
+
};
|
|
92
|
+
const downloadErrors = {
|
|
93
|
+
401: apiErrorResponse(401, 'A required download credential or signed-in identity is missing or invalid.'),
|
|
94
|
+
500: apiErrorResponse(500),
|
|
95
|
+
};
|
|
96
|
+
if (options.mint !== undefined) {
|
|
97
|
+
const mint = options.mint;
|
|
98
|
+
router.post('/downloadTokens', mint.guard, describeRoute(options.openapi?.mint ?? {
|
|
99
|
+
tags: ['CliDistribution'],
|
|
100
|
+
summary: 'Create a CLI download token',
|
|
101
|
+
operationId: 'cliDistributionMint',
|
|
102
|
+
...cliRoute(false),
|
|
103
|
+
responses: {
|
|
104
|
+
201: dataResponse(DownloadTokenSchema),
|
|
105
|
+
...apiErrorResponses,
|
|
106
|
+
},
|
|
107
|
+
}), async (context) => {
|
|
108
|
+
return context.json({ data: await mint.tokens.create(mint.createdBy(context)) }, 201);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
router.get('/manifest', allowed, productAllowed, describeRoute(options.openapi?.manifest ?? {
|
|
112
|
+
tags: ['CliDistribution'],
|
|
113
|
+
summary: 'Get the distribution manifest',
|
|
114
|
+
operationId: 'cliDistributionManifest',
|
|
115
|
+
...cliRoute(false),
|
|
116
|
+
responses: { 200: dataResponse(DistManifestSchema), ...downloadErrors },
|
|
117
|
+
}), async (context) => {
|
|
118
|
+
const manifest = await artifacts.manifest();
|
|
119
|
+
return context.json({
|
|
120
|
+
data: products !== 'all'
|
|
121
|
+
? {
|
|
122
|
+
...manifest,
|
|
123
|
+
products: Object.fromEntries(Object.entries(manifest.products).filter(([product]) => products.includes(product))),
|
|
124
|
+
}
|
|
125
|
+
: manifest,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
router.get('/products/:product/targets/:target', allowed, productAllowed, describeRoute(options.openapi?.resolve ?? {
|
|
129
|
+
tags: ['CliDistribution'],
|
|
130
|
+
summary: 'Resolve a CLI artifact',
|
|
131
|
+
operationId: 'cliDistributionResolve',
|
|
132
|
+
...cliRoute(false),
|
|
133
|
+
responses: {
|
|
134
|
+
200: {
|
|
135
|
+
description: 'The resolved artifact or npm package.',
|
|
136
|
+
content: {
|
|
137
|
+
'application/json': {
|
|
138
|
+
schema: resolver(z.object({ data: DistResolutionSchema })),
|
|
139
|
+
},
|
|
140
|
+
'text/plain': { schema: { type: 'string' } },
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
404: apiErrorResponse(404),
|
|
144
|
+
...downloadErrors,
|
|
145
|
+
},
|
|
146
|
+
}), apiValidator('param', DistTargetParams), apiValidator('query', DistTargetQuery), async (context) => {
|
|
147
|
+
const { product, target } = context.req.valid('param');
|
|
148
|
+
const query = context.req.valid('query');
|
|
149
|
+
// An npm answer only to a caller that asked for it: everyone else is answered exactly as before it existed.
|
|
150
|
+
const npm = distAccepts(query.accept, DIST_ACCEPT_NPM) &&
|
|
151
|
+
DIST_TARGET_PATTERN.test(target)
|
|
152
|
+
? await artifacts.npmPackage(product)
|
|
153
|
+
: null;
|
|
154
|
+
const resolution = npm ?? (await artifacts.resolve(product, target));
|
|
155
|
+
if (query.format === 'env')
|
|
156
|
+
return context.text(resolutionEnv(resolution), 200, {
|
|
157
|
+
'cache-control': 'no-store',
|
|
158
|
+
});
|
|
159
|
+
return context.json({ data: resolution }, 200, {
|
|
160
|
+
'cache-control': 'no-store',
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
router.get('/products/:product/versions/:version/files/:file', allowed, productAllowed, describeRoute(options.openapi?.file ?? {
|
|
164
|
+
tags: ['CliDistribution'],
|
|
165
|
+
summary: 'Download a CLI artifact',
|
|
166
|
+
operationId: 'cliDistributionFile',
|
|
167
|
+
...cliRoute(false),
|
|
168
|
+
responses: {
|
|
169
|
+
200: {
|
|
170
|
+
description: 'The artifact file.',
|
|
171
|
+
content: {
|
|
172
|
+
'application/gzip': {
|
|
173
|
+
schema: { type: 'string', format: 'binary' },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
headers: {
|
|
177
|
+
'x-checksum-sha256': {
|
|
178
|
+
schema: { type: 'string' },
|
|
179
|
+
description: 'The SHA-256 checksum.',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
404: apiErrorResponse(404),
|
|
184
|
+
...downloadErrors,
|
|
185
|
+
},
|
|
186
|
+
}), apiValidator('param', DistFileParams), async (context) => {
|
|
187
|
+
const { product, version, file: name } = context.req.valid('param');
|
|
188
|
+
const file = await artifacts.file(product, version, name);
|
|
189
|
+
const stream = Readable.toWeb(file.open());
|
|
190
|
+
return context.body(stream, 200, {
|
|
191
|
+
'content-type': 'application/gzip',
|
|
192
|
+
'content-length': String(file.size),
|
|
193
|
+
'x-checksum-sha256': file.sha256,
|
|
194
|
+
'cache-control': 'private, max-age=31536000, immutable',
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
return router;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { DownloadToken } from './download-tokens.js';
|
|
3
|
+
export declare const DistTargetParams: z.ZodType<{
|
|
4
|
+
product: string;
|
|
5
|
+
target: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const DistFileParams: z.ZodType<{
|
|
8
|
+
product: string;
|
|
9
|
+
version: string;
|
|
10
|
+
file: string;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const DistTargetQuery: z.ZodType<{
|
|
13
|
+
format?: 'json' | 'env' | undefined;
|
|
14
|
+
accept?: string | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const DownloadTokenSchema: z.ZodType<DownloadToken>;
|
|
17
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACrC,CAAC;AACxC,eAAO,MAAM,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAIC,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAMC,CAAC;AAEH,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CASvD,CAAC"}
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const id = z.string().min(1).max(200);
|
|
3
|
+
const dateTime = z.string().meta({ format: 'date-time' });
|
|
4
|
+
export const DistTargetParams = z.object({ product: id, target: id });
|
|
5
|
+
export const DistFileParams = z.object({
|
|
6
|
+
product: id,
|
|
7
|
+
version: id.describe('The version to read or restore.'),
|
|
8
|
+
file: id.describe('The file to upload.'),
|
|
9
|
+
});
|
|
10
|
+
export const DistTargetQuery = z.object({
|
|
11
|
+
format: z.enum(['json', 'env']).optional(),
|
|
12
|
+
accept: z.string().max(200).optional().meta({
|
|
13
|
+
description: 'The other answers the caller understands, comma-separated. `npm`: when the application has no tarball of the product but names its npm package, answer that package and exact version (`kind: npm`) instead of 404. Unknown values are ignored.',
|
|
14
|
+
}),
|
|
15
|
+
});
|
|
16
|
+
export const DownloadTokenSchema = z.object({
|
|
17
|
+
token: z.string().meta({
|
|
18
|
+
description: 'The download token, shown only in this answer; the install script downloads the CLI with it.',
|
|
19
|
+
}),
|
|
20
|
+
expiresAt: dateTime,
|
|
21
|
+
maxDownloads: z.number().int().meta({
|
|
22
|
+
description: 'How many tarball downloads it allows, retries included.',
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACtC,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAItB,CAAC,CAAC,MAAM,CAAC;IACZ,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;IACvD,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;CACzC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAGvB,CAAC,CAAC,MAAM,CAAC;IACZ,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;QAC1C,WAAW,EACT,iPAAiP;KACpP,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAA6B,CAAC,CAAC,MAAM,CAAC;IACpE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC;QACrB,WAAW,EACT,8FAA8F;KACjG,CAAC;IACF,SAAS,EAAE,QAAQ;IACnB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;QAClC,WAAW,EAAE,yDAAyD;KACvE,CAAC;CACH,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/app-cli-distribution",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "Composable server-side CLI artifact distribution and installation.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"prettier": "@nocobase/dev-config/prettier",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=24.0.0"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./public-install": {
|
|
20
|
+
"types": "./dist/public-install.d.ts",
|
|
21
|
+
"import": "./dist/public-install.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"hono": "^4.13.2",
|
|
32
|
+
"zod": "^4.6.6",
|
|
33
|
+
"@nocobase/agent-protocol": "^0.1.0-beta.19",
|
|
34
|
+
"@nocobase/app-cli-client": "^0.1.0-beta.22"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@nocobase/app-server": "^5.0.0-beta.6"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.0.0",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"eslint": "^10.0.0",
|
|
43
|
+
"prettier": "^3.9.6",
|
|
44
|
+
"vitest": "^4.1.10",
|
|
45
|
+
"@nocobase/dev-config": "0.1.0-beta.25"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"lint": "eslint . --max-warnings 0",
|
|
52
|
+
"lint:fix": "eslint . --fix --max-warnings 0",
|
|
53
|
+
"format": "prettier . --write",
|
|
54
|
+
"format:check": "prettier . --check",
|
|
55
|
+
"fix": "pnpm lint:fix && pnpm format"
|
|
56
|
+
}
|
|
6
57
|
}
|