diff --git a/bin/moshcode.mjs b/bin/moshcode.mjs index 819411a7..1855a903 100755 --- a/bin/moshcode.mjs +++ b/bin/moshcode.mjs @@ -680,7 +680,8 @@ async function main() { const { execFile } = await import("node:child_process"); process.exitCode = (await selfUpdateCommand(rest, console.log, { upgrade: async () => { - const results = await runUpgrade(rest.filter((a) => !a.startsWith("--"))); + const targets = rest.filter((a) => !a.startsWith("--")); + const results = await runUpgrade(targets.length ? targets : ["self"]); return results.filter((r) => !r.ok).length ? 1 : 0; }, write: (path, body) => fsp.writeFile(path, body), diff --git a/install.sh b/install.sh index 35860cb8..dbbd5fe8 100644 --- a/install.sh +++ b/install.sh @@ -23,6 +23,7 @@ # MOSHCODE_BIN=/path/dir wrapper bin dir (default: $HOME/.local/bin) # MOSHCODE_REF=vX.Y.Z pin a tag/branch (default: latest release, else main) # MOSHCODE_ALLOW_ROOT=1 install as root anyway (see below) +# MOSHCODE_NO_AUTOUPDATE=1 skip the per-user hourly update check # MOSHCODE_NO_PROXY=1 skip the pinned-TLS proxy (https:// on a Moshpit # name will not verify without it) # @@ -278,6 +279,30 @@ install_proxy() { unset _proxy_sh } +schedule_auto_update() { + if [ "${MOSHCODE_NO_AUTOUPDATE:-}" = "1" ]; then + info "MOSHCODE_NO_AUTOUPDATE=1 — skipping automatic updates" + return 0 + fi + if ! (cd "$MOSHCODE_HOME" && MOSHCODE_BIN="$MOSHCODE_BIN" node --input-type=module -e ' + import("@profullstack/cli/auto-update").then(async ({installAutoUpdate}) => { + const result = await installAutoUpdate({name:"moshcode",command:process.env.MOSHCODE_BIN+"/moshcode",args:["update","--if-newer"]}); + if (!result.scheduled) { console.error(`moshcode: automatic updates not scheduled: ${result.reason}`); process.exitCode=1; } + else console.log(`moshcode: automatic updates scheduled with ${result.scheduler}`); + }).catch(error => { console.error(`moshcode: could not schedule automatic updates: ${error.message}`); process.exitCode=1; }); + '); then + warn "automatic update check could not be scheduled" + fi +} + +unschedule_auto_update() { + [ -d "$MOSHCODE_HOME/node_modules/@profullstack/cli" ] || return 0 + (cd "$MOSHCODE_HOME" && node --input-type=module -e ' + import("@profullstack/cli/auto-update").then(({removeAutoUpdate}) => removeAutoUpdate("moshcode")) + .catch(error => { console.error(`moshcode: could not remove automatic update check: ${error.message}`); process.exitCode=1; }); + ') || true +} + # ---- commands ------------------------------------------------------------- run_install() { printf '\n%smoshcode installer%s %s— code hard, mosh harder 🤘%s\n\n' "$BOLD" "$RESET" "$ASH" "$RESET" @@ -288,6 +313,7 @@ run_install() { fetch_and_unpack "$_ref" write_wrapper ensure_path + schedule_auto_update install_proxy printf '\n%sdone.%s run:\n' "$ACID" "$RESET" printf ' moshcode # open the TUI shell\n' @@ -300,6 +326,7 @@ run_install() { run_remove() { info "removing moshcode" + unschedule_auto_update rm -f "$WRAPPER" 2>/dev/null || true rm -f "$SCRIPT_WRAPPER" 2>/dev/null || true rm -rf "$MOSHCODE_HOME" 2>/dev/null || true diff --git a/package.json b/package.json index 781cf23d..33d2e95b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moshcode", - "version": "0.120.0", + "version": "0.120.1", "type": "module", "description": "moshcode — a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript", "repository": { @@ -37,6 +37,7 @@ "@moshcoder/moshpit-dns": "^0.5.0" }, "dependencies": { + "@profullstack/cli": "^0.2.0", "@profullstack/hqtui": "^0.7.0", "@profullstack/synconfig": "^0.1.3", "@profullstack/webhooks": "^0.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7413185..93a52702 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@profullstack/cli': + specifier: ^0.2.0 + version: 0.2.0 '@profullstack/hqtui': specifier: ^0.7.0 version: 0.7.0 @@ -29,6 +32,10 @@ packages: engines: {node: '>=20'} hasBin: true + '@profullstack/cli@0.2.0': + resolution: {integrity: sha512-98Ei8yt1hBhoBFGfqDUM6QBKmzP7mad90uybp9BUw2zvilHOIRv5bTn8byR7+MjiiNN0FQ/DvOlGT9o3KcNCyg==} + engines: {node: '>=18'} + '@profullstack/hqtui@0.7.0': resolution: {integrity: sha512-2sE5RK+7jhIEAVOH8KdasUs8NOK8HkDen0vmijvFlEzZLwSl3Z6sQUihUmuqUUEKcu+qVi7eo4950chosbtgcA==} engines: {bun: '>=1.1', node: '>=22.6'} @@ -46,6 +53,8 @@ snapshots: '@moshcoder/moshpit-dns@0.5.0': {} + '@profullstack/cli@0.2.0': {} + '@profullstack/hqtui@0.7.0': {} '@profullstack/synconfig@0.1.3': {} diff --git a/src/upgrade.mjs b/src/upgrade.mjs index 65b36bc9..12ac5b00 100644 --- a/src/upgrade.mjs +++ b/src/upgrade.mjs @@ -7,6 +7,7 @@ import fs from "node:fs"; import { ENGINES, engineStatus, exitReason, ranOk, resolveEngine, upgradeSpec, runCmd } from "./engines.mjs"; import { TOOLS, resolveTool, toolStatus, toolUpgradeSpec } from "./tools.mjs"; import { needsRootHere, primeEscalation } from "./escalate.mjs"; +import { updatePlan } from "./selfupdate.mjs"; // Self-upgrade re-runs the moshcode installer's `update` path. Defaults to the // GitHub-hosted install.sh (always live); override with MOSHCODE_INSTALL_URL. @@ -203,10 +204,15 @@ export async function runUpgrade(targets = [], io = {}) { log(`\n· moshcode runs from a git checkout (${MOSHCODE_HOME}) — \`git pull\` there to update it (skipping self-reinstall).`); } else { const before = selfVersion(); - const ok = await run("moshcode", selfSpec(), "(self)"); - const after = selfVersion(); - if (ok && before && after) { - log(before === after ? `· moshcode already at ${after}` : `· moshcode ${before} → ${after} — restart moshcode to load it.`); + const decision = await updatePlan({ installed: before }); + if (!decision.act) { + log(`\n· moshcode ${before || "version unknown"}: ${decision.why}; skipping self-reinstall.`); + } else { + const ok = await run("moshcode", selfSpec(), "(self)"); + const after = selfVersion(); + if (ok && before && after) { + log(before === after ? `· moshcode already at ${after}` : `· moshcode ${before} → ${after} — restart moshcode to load it.`); + } } } }