ninepm 0.49.0 → 0.51.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.
- package/dist/index.d.ts +8 -1
- package/dist/index.js +80 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ export interface CreatedServiceToken {
|
|
|
114
114
|
clientSecret: string;
|
|
115
115
|
}
|
|
116
116
|
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
117
|
-
export declare const CLI_VERSION = "0.
|
|
117
|
+
export declare const CLI_VERSION = "0.51.0";
|
|
118
118
|
type CredentialBackend = "macos-keychain" | "windows-dpapi" | "linux-secret-service" | "file-plaintext" | "none";
|
|
119
119
|
export interface StoredCredentialEntry {
|
|
120
120
|
token: string;
|
|
@@ -284,6 +284,13 @@ export declare function checkCliVersion(options: {
|
|
|
284
284
|
fetchFn?: FetchLike;
|
|
285
285
|
timeoutMs?: number;
|
|
286
286
|
}): Promise<CliVersionCheck>;
|
|
287
|
+
export interface UpdateNudgeOptions {
|
|
288
|
+
fetchFn?: FetchLike;
|
|
289
|
+
now?: () => Date;
|
|
290
|
+
cachePath?: string;
|
|
291
|
+
env?: NodeJS.ProcessEnv;
|
|
292
|
+
}
|
|
293
|
+
export declare function deployUpdateNudge(options?: UpdateNudgeOptions): Promise<string | null>;
|
|
287
294
|
export interface ListSitesResult {
|
|
288
295
|
projects: Project[];
|
|
289
296
|
}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { MAX_DEPLOY_DESCRIPTION_LENGTH, normalizeDeployDescription } from "./dep
|
|
|
14
14
|
import { MAX_STATIC_ASSET_FILE_BYTES, MAX_WORKER_ENTRY_COMPRESSED_BYTES, MAX_WORKER_SCRIPT_COMPRESSED_BYTES, WORKER_ENTRY_COMPRESSED_WARN_BYTES, assetFileTooLargeMessage, formatMebibytes, formatMegabytes, oversizedAssets, workerBundleNearLimitMessage, workerBundleTooLargeMessage, workerEntryNearLimitMessage, workerEntryTooLargeMessage, } from "./deploy-limits.js";
|
|
15
15
|
import { buildFileManifest, readManifestFile } from "./manifest.js";
|
|
16
16
|
import { API_KEY_HELP, API_URL_HELP, CLI_CLIENT_ID, DEFAULT_API_URL, reportCliError } from "./cli-config.js";
|
|
17
|
-
export const CLI_VERSION = "0.
|
|
17
|
+
export const CLI_VERSION = "0.51.0";
|
|
18
18
|
const PUBLIC_DASHBOARD_URL = "https://9pm.ai/dashboard";
|
|
19
19
|
const TOKEN_SERVICE = "9pm";
|
|
20
20
|
const TOKEN_ACCOUNT = "default";
|
|
@@ -1021,6 +1021,72 @@ export async function checkCliVersion(options) {
|
|
|
1021
1021
|
const latest = await fetchLatestCliVersion(options);
|
|
1022
1022
|
return { current: CLI_VERSION, latest, comparison: compareCliVersions(CLI_VERSION, latest.version) };
|
|
1023
1023
|
}
|
|
1024
|
+
// Passive update nudge after a successful deploy (#996). Deliberately quiet:
|
|
1025
|
+
// at most one registry probe per interval (answer cached in the credential
|
|
1026
|
+
// config dir), a short timeout so a slow registry never holds up a deploy that
|
|
1027
|
+
// already finished, and every failure — network, malformed cache, unwritable
|
|
1028
|
+
// config dir — resolves to null, so the nudge can only ever add a line.
|
|
1029
|
+
const UPDATE_NUDGE_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
1030
|
+
const UPDATE_NUDGE_TIMEOUT_MS = 1500;
|
|
1031
|
+
const UPDATE_NUDGE_CACHE_FILE = "update-check.json";
|
|
1032
|
+
const NO_UPDATE_CHECK_ENV = "NINEPM_NO_UPDATE_CHECK";
|
|
1033
|
+
export async function deployUpdateNudge(options = {}) {
|
|
1034
|
+
const env = options.env ?? process.env;
|
|
1035
|
+
if (isTruthyEnv(env[NO_UPDATE_CHECK_ENV])) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
const now = options.now?.() ?? new Date();
|
|
1039
|
+
const cachePath = options.cachePath ?? path.join(credentialConfigDir(), UPDATE_NUDGE_CACHE_FILE);
|
|
1040
|
+
const cache = readUpdateCheckCache(cachePath);
|
|
1041
|
+
// A checkedAt in the future (clock rollback) reads as fresh on purpose —
|
|
1042
|
+
// expiring it would probe the registry on every deploy until the clock
|
|
1043
|
+
// catches back up.
|
|
1044
|
+
const fresh = cache.checkedAtMs !== null && now.getTime() - cache.checkedAtMs < UPDATE_NUDGE_INTERVAL_MS;
|
|
1045
|
+
let latest = cache.latest;
|
|
1046
|
+
if (!fresh) {
|
|
1047
|
+
try {
|
|
1048
|
+
const fetched = await fetchLatestCliVersion({
|
|
1049
|
+
apiUrl: "", // unused: the updater sources the public npm registry (see fetchLatestCliVersion)
|
|
1050
|
+
timeoutMs: UPDATE_NUDGE_TIMEOUT_MS,
|
|
1051
|
+
...(options.fetchFn ? { fetchFn: options.fetchFn } : {}),
|
|
1052
|
+
});
|
|
1053
|
+
latest = fetched.version;
|
|
1054
|
+
}
|
|
1055
|
+
catch {
|
|
1056
|
+
// Keep the last-known answer (if any) for the comparison below. The
|
|
1057
|
+
// attempt is still stamped: an unreachable registry must not re-add its
|
|
1058
|
+
// timeout to every deploy for the rest of the interval.
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
1062
|
+
writeFileSync(cachePath, JSON.stringify({ checkedAt: now.toISOString(), ...(latest ? { latest } : {}) }));
|
|
1063
|
+
}
|
|
1064
|
+
catch {
|
|
1065
|
+
// The cache only spaces out probes; a read-only config dir just means the
|
|
1066
|
+
// next deploy probes again.
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (!latest || compareCliVersions(CLI_VERSION, latest) !== "behind") {
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
return `Update available: 9pm CLI ${CLI_VERSION} -> ${latest}. Run: npm i -g ninepm@latest`;
|
|
1073
|
+
}
|
|
1074
|
+
// Best-effort read of the update-check cache; the caller judges freshness.
|
|
1075
|
+
// checkedAtMs is null when the file is missing or malformed (so the next probe
|
|
1076
|
+
// runs now), and latest is null when the last attempt failed to get an answer
|
|
1077
|
+
// — a failed attempt is cached too, so the outage's timeout is paid at most
|
|
1078
|
+
// once per interval rather than on every deploy.
|
|
1079
|
+
function readUpdateCheckCache(cachePath) {
|
|
1080
|
+
try {
|
|
1081
|
+
const parsed = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
1082
|
+
const checkedAtMs = typeof parsed.checkedAt === "string" ? Date.parse(parsed.checkedAt) : Number.NaN;
|
|
1083
|
+
const latest = typeof parsed.latest === "string" && parsed.latest.trim() ? parsed.latest.trim() : null;
|
|
1084
|
+
return { checkedAtMs: Number.isFinite(checkedAtMs) ? checkedAtMs : null, latest };
|
|
1085
|
+
}
|
|
1086
|
+
catch {
|
|
1087
|
+
return { checkedAtMs: null, latest: null };
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1024
1090
|
export async function listSites(options) {
|
|
1025
1091
|
const payload = await apiRequest(options, "/v1/sites", { method: "GET" });
|
|
1026
1092
|
return { projects: payload.projects };
|
|
@@ -3686,6 +3752,14 @@ async function main() {
|
|
|
3686
3752
|
console.log(`Status: ${result.deployment.status}. Run 9pm apps to check when it becomes active.`);
|
|
3687
3753
|
}
|
|
3688
3754
|
console.log(`Manage: ${PUBLIC_DASHBOARD_URL}`);
|
|
3755
|
+
// Gated the same way as the reachability probe above: a programmatic
|
|
3756
|
+
// caller injecting fetchFn opted out of the CLI's own network side trips.
|
|
3757
|
+
if (!command.options.fetchFn) {
|
|
3758
|
+
const nudge = await deployUpdateNudge();
|
|
3759
|
+
if (nudge) {
|
|
3760
|
+
console.log(nudge);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3689
3763
|
return;
|
|
3690
3764
|
}
|
|
3691
3765
|
if (command.kind === "login") {
|
|
@@ -4161,7 +4235,11 @@ Container deploys:
|
|
|
4161
4235
|
--cpus <n> and --memory <size> (512m, 2048, 4g); the platform picks a placement that fits.
|
|
4162
4236
|
All of these may require platform enablement.
|
|
4163
4237
|
A redeploy KEEPS the app's existing disk automatically: omit --volume and the current disk is
|
|
4164
|
-
carried forward.
|
|
4238
|
+
carried forward. A LARGER --volume than the current disk grows it in place (same disk, data
|
|
4239
|
+
preserved). Sizes never shrink: a smaller --volume is refused — to recreate the disk smaller,
|
|
4240
|
+
drop it first (--no-volume --allow-data-wipe, which destroys its data), then deploy again
|
|
4241
|
+
with the smaller --volume (fresh empty disk).
|
|
4242
|
+
To remove the disk on purpose pass --no-volume (and --allow-data-wipe, since it
|
|
4165
4243
|
destroys the data). If a disk was previously detached by an old CLI, redeploy with --volume <same
|
|
4166
4244
|
gb> --region <same region> to reattach the existing disk (it is reused, not recreated empty).
|
|
4167
4245
|
--allow-data-wipe acknowledges that dropping a disk, or switching to a placement that can't keep
|