@m13v/s4l 1.7.1-rc.8 → 1.7.1-rc.9
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/bin/cli.js +23 -0
- package/mcp/dist/runtime.js +23 -0
- package/mcp/dist/version.json +2 -2
- package/mcp/install.mjs +0 -15
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/s4l_menubar.py +6 -8
- package/mcp/package.json +1 -1
- package/mcp/shared/doctor.cjs +8 -0
- package/package.json +1 -1
- package/scripts/get-latest-staging-mcpb.sh +28 -45
- package/scripts/release-mcpb.sh +48 -0
- package/scripts/set-channel.sh +0 -29
package/bin/cli.js
CHANGED
|
@@ -823,6 +823,29 @@ function installMcp() {
|
|
|
823
823
|
} catch (e) {
|
|
824
824
|
console.warn(' WARNING: could not stamp MCP version:', e && e.message);
|
|
825
825
|
}
|
|
826
|
+
// Auto-opt a fresh install into the staging channel when the version being
|
|
827
|
+
// installed is itself a pre-release (-rc.N) — e.g. `npx social-autoposter@
|
|
828
|
+
// X.Y.Z-rc.N init`. Without this, channel.json stays absent, which
|
|
829
|
+
// scripts/s4l_channel.py's fail-safe default reads as "stable", so the box
|
|
830
|
+
// would install this one rc and then silently stop tracking staging (never
|
|
831
|
+
// pick up the next rc). Mirrors the same one-time write in
|
|
832
|
+
// mcp/src/runtime.ts's provision() for the .mcpb (Desktop) install path.
|
|
833
|
+
// Only writes when no channel marker exists yet — never overrides an
|
|
834
|
+
// existing preference (e.g. a user who already opted back out).
|
|
835
|
+
try {
|
|
836
|
+
const pkgVersion = require('../package.json').version;
|
|
837
|
+
if (pkgVersion.includes('-rc.')) {
|
|
838
|
+
const stateDir = process.env.S4L_STATE_DIR || path.join(os.homedir(), '.social-autoposter-mcp');
|
|
839
|
+
const channelPath = path.join(stateDir, 'channel.json');
|
|
840
|
+
if (!fs.existsSync(channelPath)) {
|
|
841
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
842
|
+
fs.writeFileSync(channelPath, JSON.stringify({ channel: 'staging' }, null, 2) + '\n');
|
|
843
|
+
console.log(' pre-release install detected -> opted into the staging channel');
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
} catch (e) {
|
|
847
|
+
console.warn(' WARNING: could not set staging channel:', e && e.message);
|
|
848
|
+
}
|
|
826
849
|
console.log(' installing MCP runtime deps (npm install --omit=dev in mcp/)');
|
|
827
850
|
const npmRes = spawnSync('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], {
|
|
828
851
|
cwd: mcpDest,
|
package/mcp/dist/runtime.js
CHANGED
|
@@ -676,6 +676,29 @@ async function provision(progress) {
|
|
|
676
676
|
/* best effort; S4L_REPO_DIR + the run-*.sh fallback also resolve the repo */
|
|
677
677
|
}
|
|
678
678
|
setStep("repo", "done", `unpacked to ${resolvedRepo}`);
|
|
679
|
+
// Auto-opt a bare .mcpb FIRST install into the staging channel when the
|
|
680
|
+
// shipped build is itself a pre-release (-rc.N) — e.g. downloaded from
|
|
681
|
+
// the staging-latest alias link (scripts/release-mcpb.sh step 7b).
|
|
682
|
+
// Without this, channel.json stays absent, which releaseChannel()'s
|
|
683
|
+
// fail-safe default reads as "stable" (mcp/src/version.ts), so the box
|
|
684
|
+
// would install this one rc and then silently stop tracking staging
|
|
685
|
+
// (never pick up the next rc). Mirrors the same one-time write in
|
|
686
|
+
// bin/cli.js's installMcp() for the npx install path. Gated to this
|
|
687
|
+
// `else` branch (genuine bare-.mcpb unpack, not envClone) so a
|
|
688
|
+
// developer's own npm/git clone is never auto-enrolled. Only writes when
|
|
689
|
+
// no channel marker exists yet — never overrides an existing preference.
|
|
690
|
+
try {
|
|
691
|
+
const bv = bundledVersion();
|
|
692
|
+
const channelPath = path.join(STATE_DIR, "channel.json");
|
|
693
|
+
if (bv && bv.includes("-rc.") && !fs.existsSync(channelPath)) {
|
|
694
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
695
|
+
fs.writeFileSync(channelPath, JSON.stringify({ channel: "staging" }, null, 2) + "\n", "utf-8");
|
|
696
|
+
console.error(`[runtime] first install of a staging build (${bv}) — opted into the staging channel`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
/* best effort; worst case the box just tracks stable */
|
|
701
|
+
}
|
|
679
702
|
}
|
|
680
703
|
// --- Step 1: uv -----------------------------------------------------------
|
|
681
704
|
setStep("uv", "running");
|
package/mcp/dist/version.json
CHANGED
package/mcp/install.mjs
CHANGED
|
@@ -14,8 +14,6 @@ import path from "node:path";
|
|
|
14
14
|
|
|
15
15
|
const SERVER_KEY = "social-autoposter";
|
|
16
16
|
const UNINSTALL = process.argv.includes("--uninstall");
|
|
17
|
-
const CHANNEL_STAGING = process.argv.includes("--channel-staging");
|
|
18
|
-
const CHANNEL_STABLE = process.argv.includes("--channel-stable");
|
|
19
17
|
|
|
20
18
|
// ---- resolve the absolute paths we want pinned into the spawn env ----------
|
|
21
19
|
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
@@ -137,19 +135,6 @@ for (const t of targets) {
|
|
|
137
135
|
}
|
|
138
136
|
}
|
|
139
137
|
|
|
140
|
-
// Set release channel if requested
|
|
141
|
-
if (!UNINSTALL && (CHANNEL_STAGING || CHANNEL_STABLE)) {
|
|
142
|
-
const stateDir = path.join(home, ".social-autoposter-mcp");
|
|
143
|
-
const channel = CHANNEL_STAGING ? "staging" : "stable";
|
|
144
|
-
try {
|
|
145
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
146
|
-
writeJson(path.join(stateDir, "channel.json"), { channel });
|
|
147
|
-
console.log(`✓ Release channel set to: ${channel}`);
|
|
148
|
-
} catch (err) {
|
|
149
|
-
console.error(`✗ Failed to set channel: ${err.message}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
138
|
console.log("");
|
|
154
139
|
if (ok === targets.length) {
|
|
155
140
|
if (UNINSTALL) {
|
package/mcp/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"dxt_version": "0.1",
|
|
3
3
|
"name": "social-autoposter",
|
|
4
4
|
"display_name": "S4L",
|
|
5
|
-
"version": "1.7.1-rc.
|
|
5
|
+
"version": "1.7.1-rc.9",
|
|
6
6
|
"description": "Draft, review, approve, and autopilot X/Twitter posts.",
|
|
7
7
|
"long_description": "## **⚠️ The disclaimer above is generic Claude boilerplate.** Anthropic shows the same warning on every plugin regardless of what it does; any plugin has the same level of access as any app you download from the internet.\n\nS4L is an open source product developed by Mediar.ai Incorporated, a VC-backed San Francisco-based startup.\n\nTo get started:\n\n1\\. Copy this prompt: **Set me up on S4L plugin end to end**\n\n2\\. Quit with CMD+Q, reopen Claude, paste into a new chat.\n\nWhat happens next:\n\n* About every 5 minutes S4L scans X for posts that match your topics and drafts replies in your voice.\n* Drafts show up as review cards, usually the first within a few minutes. Nothing is posted automatically; you approve each one.\n* Posting autopilot stays off until you explicitly turn it on.",
|
|
8
8
|
"author": {
|
|
@@ -1801,15 +1801,13 @@ class S4LMenuBar(rumps.App):
|
|
|
1801
1801
|
return
|
|
1802
1802
|
_activate_front()
|
|
1803
1803
|
choice = _show_alert(
|
|
1804
|
-
title="Tidy
|
|
1804
|
+
title="Tidy S4L background tasks?",
|
|
1805
1805
|
message=(
|
|
1806
|
-
"
|
|
1807
|
-
"
|
|
1808
|
-
"
|
|
1809
|
-
"
|
|
1810
|
-
"
|
|
1811
|
-
"close and reopen in a moment. Your X login, drafts, and config "
|
|
1812
|
-
"all stay."
|
|
1806
|
+
"Move the background worker to a dedicated folder so its "
|
|
1807
|
+
"once-a-minute runs stop cluttering your `claude --resume` "
|
|
1808
|
+
"history.\n\n"
|
|
1809
|
+
"Claude Desktop will restart once to apply. Your X login, "
|
|
1810
|
+
"drafts, and config all stay."
|
|
1813
1811
|
),
|
|
1814
1812
|
ok="Tidy & restart Claude", cancel="Later",
|
|
1815
1813
|
)
|
package/mcp/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m13v/s4l-mcp",
|
|
3
|
-
"version": "1.7.1-rc.
|
|
3
|
+
"version": "1.7.1-rc.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Desktop MCP client for social-autoposter (X/Twitter rail): manual draft/review/approve loop, autopilot control, and stats. Thin wrapper over the existing pipeline scripts.",
|
|
6
6
|
"license": "MIT",
|
package/mcp/shared/doctor.cjs
CHANGED
|
@@ -149,6 +149,14 @@ function runDoctorSync(options = {}) {
|
|
|
149
149
|
"deferred until X connection to avoid an unexpected keychain prompt"
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
|
+
// Once the durable cookie mirror is populated, the X session no longer
|
|
153
|
+
// depends on this keychain item (see twitter_cookie_mirror.py — it exists
|
|
154
|
+
// precisely so a locked/missing Safe Storage key isn't fatal). Skip the
|
|
155
|
+
// probe instead of re-triggering + re-failing it on every doctor run;
|
|
156
|
+
// same mirrorCount() gate keychain_autolock already uses below.
|
|
157
|
+
if (mirrorCount() > 0) {
|
|
158
|
+
return result("pass", "skipped — durable cookie mirror already covers this");
|
|
159
|
+
}
|
|
152
160
|
const r = spawnSync(
|
|
153
161
|
"security",
|
|
154
162
|
[
|
package/package.json
CHANGED
|
@@ -1,67 +1,50 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Print (or download) the permanent download link for the latest S4L staging
|
|
3
|
+
# release (.mcpb).
|
|
4
|
+
#
|
|
3
5
|
# Usage:
|
|
4
|
-
# bash scripts/get-latest-staging-mcpb.sh [--download] [--channel]
|
|
6
|
+
# bash scripts/get-latest-staging-mcpb.sh [--download] [--channel]
|
|
5
7
|
#
|
|
6
8
|
# Options:
|
|
7
|
-
# --download Download the .mcpb to current directory (instead of
|
|
8
|
-
#
|
|
9
|
-
# --
|
|
9
|
+
# --download Download the .mcpb to the current directory (instead of
|
|
10
|
+
# just printing the URL)
|
|
11
|
+
# --channel After downloading, opt this machine into the staging
|
|
12
|
+
# channel (delegates to scripts/s4l_channel.py, the single
|
|
13
|
+
# source of truth for the channel marker)
|
|
14
|
+
#
|
|
15
|
+
# The link below is a FIXED URL, not a live GitHub API lookup: every
|
|
16
|
+
# `bash scripts/release-mcpb.sh --staging` run mirrors its .mcpb onto a
|
|
17
|
+
# separate, non-version-tagged release ("staging-latest") so this exact URL
|
|
18
|
+
# always serves whatever's newest — see scripts/release-mcpb.sh step 7b. That
|
|
19
|
+
# means this script needs zero network calls to resolve anything; it only
|
|
20
|
+
# hits the network when actually downloading.
|
|
10
21
|
|
|
11
22
|
set -euo pipefail
|
|
12
23
|
|
|
24
|
+
STAGING_MCPB_URL="https://github.com/m13v/s4l/releases/download/staging-latest/social-autoposter.mcpb"
|
|
25
|
+
|
|
13
26
|
DO_DOWNLOAD=0
|
|
14
27
|
DO_CHANNEL=0
|
|
15
|
-
DO_PATH=0
|
|
16
28
|
|
|
17
29
|
while [[ $# -gt 0 ]]; do
|
|
18
30
|
case "$1" in
|
|
19
31
|
--download) DO_DOWNLOAD=1; shift ;;
|
|
20
32
|
--channel) DO_CHANNEL=1; shift ;;
|
|
21
|
-
|
|
22
|
-
*) echo "Unknown option: $1"; exit 1 ;;
|
|
33
|
+
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
23
34
|
esac
|
|
24
35
|
done
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
RELEASE_DATA=$(curl -fsS "https://api.github.com/repos/m13v/s4l/releases?per_page=1&pre_release=true" 2>/dev/null | python3 -c "import sys, json; data=json.load(sys.stdin); r=data[0] if data else {}; print(json.dumps(r))")
|
|
29
|
-
|
|
30
|
-
# Extract version and download URL using python for reliable JSON parsing
|
|
31
|
-
LATEST_TAG=$(echo "$RELEASE_DATA" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('tag_name', ''))" 2>/dev/null)
|
|
32
|
-
MCPB_URL=$(echo "$RELEASE_DATA" | python3 -c "
|
|
33
|
-
import sys, json
|
|
34
|
-
data = json.load(sys.stdin)
|
|
35
|
-
for asset in data.get('assets', []):
|
|
36
|
-
if 'social-autoposter.mcpb' in asset.get('name', ''):
|
|
37
|
-
print(asset.get('browser_download_url', ''))
|
|
38
|
-
break
|
|
39
|
-
" 2>/dev/null)
|
|
40
|
-
|
|
41
|
-
if [ -z "$LATEST_TAG" ] || [ -z "$MCPB_URL" ]; then
|
|
42
|
-
echo "Error: Could not find latest staging release" >&2
|
|
43
|
-
exit 1
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
if [ "$DO_PATH" -eq 1 ]; then
|
|
47
|
-
# Return the tag only (for scripting)
|
|
48
|
-
echo "$LATEST_TAG"
|
|
37
|
+
if [ "$DO_DOWNLOAD" -eq 0 ]; then
|
|
38
|
+
echo "$STAGING_MCPB_URL"
|
|
49
39
|
exit 0
|
|
50
40
|
fi
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
echo "$FILENAME"
|
|
42
|
+
FILENAME="social-autoposter-staging.mcpb"
|
|
43
|
+
echo "Downloading $FILENAME from $STAGING_MCPB_URL" >&2
|
|
44
|
+
curl -fL "$STAGING_MCPB_URL" -o "$FILENAME"
|
|
45
|
+
echo "$FILENAME"
|
|
57
46
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
echo '{"channel":"staging"}' > "$STATE_DIR/channel.json"
|
|
62
|
-
echo "✓ Channel set to staging" >&2
|
|
63
|
-
fi
|
|
64
|
-
else
|
|
65
|
-
# Just print the URL
|
|
66
|
-
echo "$MCPB_URL"
|
|
47
|
+
if [ "$DO_CHANNEL" -eq 1 ]; then
|
|
48
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
49
|
+
python3 "$REPO_ROOT/scripts/s4l_channel.py" set staging >&2
|
|
67
50
|
fi
|
package/scripts/release-mcpb.sh
CHANGED
|
@@ -45,6 +45,16 @@
|
|
|
45
45
|
# the EXACT tested artifact ships to everyone. Nothing can drift between test and
|
|
46
46
|
# ship because there is no repack.
|
|
47
47
|
#
|
|
48
|
+
# FIXED STAGING DOWNLOAD LINK (2026-07-07, step 7b): stable has a permanent
|
|
49
|
+
# "latest" URL (releases/latest/download/social-autoposter.mcpb); staging did
|
|
50
|
+
# not, because GitHub's releases/latest hard-excludes prereleases with no
|
|
51
|
+
# override. Every --staging run now also mirrors the .mcpb onto a separate,
|
|
52
|
+
# non-version-tagged release ("staging-latest") so this URL never changes:
|
|
53
|
+
# https://github.com/m13v/s4l/releases/download/staging-latest/social-autoposter.mcpb
|
|
54
|
+
# The tag deliberately isn't semver-shaped, so version.ts/snapshot.py's real
|
|
55
|
+
# "newest rc" resolution (which requires a ^\d+\.\d+\.\d+ tag) ignores it —
|
|
56
|
+
# it is a convenience download link only, never a candidate update source.
|
|
57
|
+
#
|
|
48
58
|
# Usage:
|
|
49
59
|
# bash scripts/release-mcpb.sh # patch bump, npm + .mcpb + GitHub (STABLE)
|
|
50
60
|
# bash scripts/release-mcpb.sh --bump minor
|
|
@@ -68,6 +78,12 @@ MCP_DIR="$REPO_ROOT/mcp"
|
|
|
68
78
|
BUNDLE="$MCP_DIR/social-autoposter.mcpb"
|
|
69
79
|
GH_REPO="m13v/s4l"
|
|
70
80
|
SIZE_CAP_MB=180
|
|
81
|
+
# Floating alias release for a fixed "always the newest staging build" download
|
|
82
|
+
# link, mirroring what releases/latest/download/<asset> already gives stable.
|
|
83
|
+
# GitHub's own releases/latest excludes prereleases with no override, so there is
|
|
84
|
+
# no built-in equivalent for the rc channel — see step 7b below for why a
|
|
85
|
+
# non-semver tag here is safe (never confuses the real update-detection code).
|
|
86
|
+
STAGING_ALIAS_TAG="staging-latest"
|
|
71
87
|
|
|
72
88
|
TAG_OVERRIDE=""
|
|
73
89
|
VERSION_OVERRIDE=""
|
|
@@ -496,6 +512,38 @@ say "Released $TAG"
|
|
|
496
512
|
echo " asset: social-autoposter.mcpb (${MB}MB)"
|
|
497
513
|
[[ -n "$URL" ]] && echo " $URL"
|
|
498
514
|
|
|
515
|
+
# ---- 7b. Fixed "always latest staging" download link ------------------------
|
|
516
|
+
# Mirrors the asset just uploaded above onto a SEPARATE release tagged
|
|
517
|
+
# "$STAGING_ALIAS_TAG" (not a version), so the download URL never changes:
|
|
518
|
+
# https://github.com/$GH_REPO/releases/download/$STAGING_ALIAS_TAG/social-autoposter.mcpb
|
|
519
|
+
# Safe by construction: latestFromGithubListStaging() (mcp/src/version.ts) and
|
|
520
|
+
# _latest_from_github_list_staging() (scripts/snapshot.py) both require
|
|
521
|
+
# tag_name to match ^\d+\.\d+\.\d+ before treating a release as a candidate
|
|
522
|
+
# "newest" build; a tag literally named "staging-latest" fails that check and
|
|
523
|
+
# is silently skipped by both, so this alias can never get mistaken for a real
|
|
524
|
+
# rc and can never confuse which build a box installs. It exists ONLY to give
|
|
525
|
+
# humans (and scripts) one fixed bookmark — actual boxes still resolve "newest"
|
|
526
|
+
# from the real per-release rc tags via the releases LIST endpoint, unchanged.
|
|
527
|
+
if [[ "$DO_STAGING" == "1" ]]; then
|
|
528
|
+
say "Mirroring asset onto the fixed staging-latest alias"
|
|
529
|
+
ALIAS_NOTES="Always mirrors the newest staging pre-release's .mcpb asset (currently $TAG).
|
|
530
|
+
|
|
531
|
+
Fixed download link (never changes): https://github.com/$GH_REPO/releases/download/$STAGING_ALIAS_TAG/social-autoposter.mcpb
|
|
532
|
+
|
|
533
|
+
Not a real release — do not install from the tag page; use the asset link above. Actual per-build releases are tagged by version (e.g. $TAG)."
|
|
534
|
+
if gh release view "$STAGING_ALIAS_TAG" -R "$GH_REPO" >/dev/null 2>&1; then
|
|
535
|
+
gh release upload "$STAGING_ALIAS_TAG" "$BUNDLE" -R "$GH_REPO" --clobber
|
|
536
|
+
gh release edit "$STAGING_ALIAS_TAG" -R "$GH_REPO" --notes "$ALIAS_NOTES" >/dev/null
|
|
537
|
+
else
|
|
538
|
+
gh release create "$STAGING_ALIAS_TAG" "$BUNDLE" \
|
|
539
|
+
-R "$GH_REPO" \
|
|
540
|
+
--title "social-autoposter (staging — always latest)" \
|
|
541
|
+
--notes "$ALIAS_NOTES" \
|
|
542
|
+
--prerelease
|
|
543
|
+
fi
|
|
544
|
+
echo " https://github.com/$GH_REPO/releases/download/$STAGING_ALIAS_TAG/social-autoposter.mcpb"
|
|
545
|
+
fi
|
|
546
|
+
|
|
499
547
|
# ---- 8. Verify the update banner will fire ---------------------------------
|
|
500
548
|
# The menu-bar "⬆ Update available" banner (mcp/src/version.ts::versionStatus)
|
|
501
549
|
# resolves "latest" from GitHub releases/latest, which is what .mcpb boxes (no
|
package/scripts/set-channel.sh
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Set the S4L release channel (stable or staging).
|
|
3
|
-
# Usage:
|
|
4
|
-
# bash scripts/set-channel.sh stable # track stable releases only (default)
|
|
5
|
-
# bash scripts/set-channel.sh staging # track staging releases (pre-releases)
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
if [ $# -lt 1 ]; then
|
|
10
|
-
echo "Usage: bash scripts/set-channel.sh <stable|staging>" >&2
|
|
11
|
-
exit 1
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
CHANNEL="$1"
|
|
15
|
-
if [ "$CHANNEL" != "stable" ] && [ "$CHANNEL" != "staging" ]; then
|
|
16
|
-
echo "Error: channel must be 'stable' or 'staging'" >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
STATE_DIR="${S4L_STATE_DIR:-$HOME/.social-autoposter-mcp}"
|
|
21
|
-
mkdir -p "$STATE_DIR"
|
|
22
|
-
echo "{\"channel\":\"$CHANNEL\"}" > "$STATE_DIR/channel.json"
|
|
23
|
-
|
|
24
|
-
echo "✓ Release channel set to: $CHANNEL"
|
|
25
|
-
if [ "$CHANNEL" = "staging" ]; then
|
|
26
|
-
echo " Will now track pre-releases (rc.N builds). Next update check pulls the latest staging release."
|
|
27
|
-
else
|
|
28
|
-
echo " Will now track stable releases only. Next update check pulls the latest stable release."
|
|
29
|
-
fi
|