newsline-cli 0.1.2 → 0.1.4
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/newsline +2 -2
- package/package.json +1 -1
- package/refresh.sh +31 -0
- package/resolve.py +27 -16
- package/statusline.sh +7 -0
package/newsline
CHANGED
|
@@ -250,7 +250,7 @@ PY
|
|
|
250
250
|
|
|
251
251
|
# apply the new language/topic on the next render, not after the old cache expires
|
|
252
252
|
local cache="${NEWSLINE_CACHE:-$HOME/.cache/newsline}"
|
|
253
|
-
rm -f "$cache/line" "$cache/feeds.resolved.json" "$cache/feeds.resolved.ok" 2>/dev/null
|
|
253
|
+
rm -f "$cache/line" "$cache/feeds.resolved.json" "$cache/feeds.resolved.ok" "$cache/notice" 2>/dev/null
|
|
254
254
|
|
|
255
255
|
echo "✔ newsline installed (lang=$lang, topic=$topic)."
|
|
256
256
|
[ -n "$base" ] && echo " composing with your existing status line — it stays visible above the news."
|
|
@@ -292,7 +292,7 @@ else:
|
|
|
292
292
|
print("status line not managed by newsline; left as-is")
|
|
293
293
|
PY
|
|
294
294
|
local cache="${NEWSLINE_CACHE:-$HOME/.cache/newsline}"
|
|
295
|
-
rm -f "$CONFIG" "$cache/line" "$cache/feeds.resolved.json" "$cache/feeds.resolved.ok" 2>/dev/null
|
|
295
|
+
rm -f "$CONFIG" "$cache/line" "$cache/feeds.resolved.json" "$cache/feeds.resolved.ok" "$cache/notice" 2>/dev/null
|
|
296
296
|
echo "✔ newsline uninstalled."
|
|
297
297
|
}
|
|
298
298
|
|
package/package.json
CHANGED
package/refresh.sh
CHANGED
|
@@ -16,6 +16,9 @@ FEEDS="${NEWSLINE_FEEDS:-$HERE/feeds.json}"
|
|
|
16
16
|
# through it so monetization can be switched on later WITHOUT re-shipping.
|
|
17
17
|
ENDPOINT="${NEWSLINE_ENDPOINT:-https://newsline.thesockerrr.workers.dev/r}"
|
|
18
18
|
COUNT="${NEWSLINE_COUNT:-15}" # how many headlines to cache for rotation
|
|
19
|
+
# Client version, sent to /feed as ?v= for the server-side update nudge.
|
|
20
|
+
# Bump together with package.json when tagging a release.
|
|
21
|
+
export NEWSLINE_VERSION="${NEWSLINE_VERSION:-0.1.4}"
|
|
19
22
|
LOCK="$CACHE_DIR/refresh.lock"
|
|
20
23
|
mkdir -p "$CACHE_DIR" 2>/dev/null
|
|
21
24
|
|
|
@@ -82,6 +85,34 @@ if [ "$need_resolve" = "1" ]; then
|
|
|
82
85
|
fi
|
|
83
86
|
fi
|
|
84
87
|
|
|
88
|
+
# --- server-driven update nudge: one extra line below the news --------------
|
|
89
|
+
# If the server flagged this client as too old (RESOLVED has "_update"),
|
|
90
|
+
# write the notice line statusline.sh appends; otherwise clear it. Skipped
|
|
91
|
+
# (cleared) when this client already meets minVersion, so the nudge stops
|
|
92
|
+
# right after an update even while the old RESOLVED is still cached.
|
|
93
|
+
NOTICE="$CACHE_DIR/notice"
|
|
94
|
+
msg=$(python3 -c '
|
|
95
|
+
import json, os, sys
|
|
96
|
+
try:
|
|
97
|
+
u = json.load(open(sys.argv[1])).get("_update") or {}
|
|
98
|
+
except Exception:
|
|
99
|
+
u = {}
|
|
100
|
+
def pv(s): return [int(x) if x.isdigit() else 0 for x in str(s).split(".")]
|
|
101
|
+
mv, cur = u.get("minVersion", ""), os.environ.get("NEWSLINE_VERSION", "")
|
|
102
|
+
print("" if (mv and cur and pv(cur) >= pv(mv)) else u.get("message", ""))
|
|
103
|
+
' "$RESOLVED" 2>/dev/null | tr -d '\000-\037')
|
|
104
|
+
if [ -n "$msg" ]; then
|
|
105
|
+
case "$HERE" in # install channel by where we live
|
|
106
|
+
*"/node_modules/"*) ucmd="npm i -g newsline-cli" ;;
|
|
107
|
+
*[Cc]ellar*|*linuxbrew*) ucmd="brew upgrade newsline" ;;
|
|
108
|
+
*) ucmd="curl -fsSL https://raw.githubusercontent.com/itdar/cc-plugin/master/install.sh | sh" ;;
|
|
109
|
+
esac
|
|
110
|
+
# yellow line, command in bold bright yellow — mirrors Claude Code's own update nudge
|
|
111
|
+
printf '\033[33m[%s] Run: \033[1;93m%s\033[0m\n' "$msg" "$ucmd" > "$NOTICE.tmp" && mv -f "$NOTICE.tmp" "$NOTICE"
|
|
112
|
+
else
|
|
113
|
+
rm -f "$NOTICE" 2>/dev/null
|
|
114
|
+
fi
|
|
115
|
+
|
|
85
116
|
# --- fetch + parse + build the line; write atomically, keep old cache on failure ---
|
|
86
117
|
if python3 "$HERE/fetch.py" "$lang" "$RESOLVED" "$ENDPOINT" "$COUNT" > "$CACHE_DIR/line.tmp" 2>/dev/null \
|
|
87
118
|
&& [ -s "$CACHE_DIR/line.tmp" ]; then
|
package/resolve.py
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
"""newsline · resolve which feeds to use — server-first, local fallback, topic-aware.
|
|
3
3
|
|
|
4
4
|
Tries the operator's curation API (NEWSLINE_API) with a short timeout, sending only
|
|
5
|
-
COARSE CONTEXT (lang, country, localtime, dow, tz, topic) — never personal data.
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
COARSE CONTEXT (lang, country, localtime, dow, tz, topic) — never personal data.
|
|
6
|
+
The server's list is used AS-IS (it handles topic: vertical partners first, then
|
|
7
|
+
Google's topic feed). Only when the server is unreachable does the local fallback
|
|
8
|
+
kick in: bundled feeds.json, with a keyless Google News topic feed prepended if a
|
|
9
|
+
topic preference is set (NEWSLINE_TOPIC) so the topic still leads offline.
|
|
8
10
|
|
|
9
11
|
Prints a feeds.json-shaped object fetch.py consumes: {"<lang>": [...], "default": [...]}
|
|
10
12
|
|
|
@@ -62,6 +64,7 @@ def local_feeds(lang, path):
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def server_feeds(lang, api_base):
|
|
67
|
+
"""Return (feeds_or_None, update_or_None) from the curation API."""
|
|
65
68
|
params = {
|
|
66
69
|
"lang": lang,
|
|
67
70
|
"country": os.environ.get("NEWSLINE_COUNTRY", ""),
|
|
@@ -69,18 +72,22 @@ def server_feeds(lang, api_base):
|
|
|
69
72
|
"dow": os.environ.get("NEWSLINE_DOW", ""),
|
|
70
73
|
"tz": os.environ.get("NEWSLINE_TZ", ""),
|
|
71
74
|
"topic": os.environ.get("NEWSLINE_TOPIC", ""),
|
|
75
|
+
"v": os.environ.get("NEWSLINE_VERSION", ""),
|
|
72
76
|
}
|
|
73
77
|
url = api_base.rstrip("/") + "/feed?" + urllib.parse.urlencode(params)
|
|
74
78
|
req = urllib.request.Request(url, headers={"User-Agent": "newsline/0.1"})
|
|
75
79
|
obj = json.loads(urllib.request.urlopen(req, timeout=TIMEOUT).read())
|
|
80
|
+
update = obj.get("update")
|
|
81
|
+
if not isinstance(update, dict):
|
|
82
|
+
update = None
|
|
76
83
|
feeds = obj.get("feeds")
|
|
77
84
|
if isinstance(feeds, list):
|
|
78
85
|
# fetch.py urlopens these — accept only http(s) URLs
|
|
79
86
|
feeds = [x for x in feeds
|
|
80
87
|
if isinstance(x, str) and x.startswith(("http://", "https://"))]
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return None
|
|
88
|
+
else:
|
|
89
|
+
feeds = None
|
|
90
|
+
return (feeds or None), update
|
|
84
91
|
|
|
85
92
|
|
|
86
93
|
def main():
|
|
@@ -91,22 +98,26 @@ def main():
|
|
|
91
98
|
|
|
92
99
|
local_urls, default = local_feeds(lang, local_path)
|
|
93
100
|
|
|
94
|
-
feeds = None
|
|
101
|
+
feeds = update = None
|
|
95
102
|
if api_base:
|
|
96
103
|
try:
|
|
97
|
-
feeds = server_feeds(lang, api_base) # server-first
|
|
104
|
+
feeds, update = server_feeds(lang, api_base) # server-first, used AS-IS
|
|
98
105
|
except Exception:
|
|
99
|
-
feeds = None
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
feeds, update = None, None # any issue -> local fallback
|
|
107
|
+
if not feeds:
|
|
108
|
+
# server unreachable: bundled feeds, topic preference prepended locally
|
|
109
|
+
feeds = local_urls or default
|
|
110
|
+
tfeed = topic_feed(lang, os.environ.get("NEWSLINE_COUNTRY", ""),
|
|
111
|
+
os.environ.get("NEWSLINE_TOPIC", ""))
|
|
112
|
+
if tfeed:
|
|
113
|
+
feeds = [tfeed] + [f for f in feeds if f != tfeed]
|
|
106
114
|
|
|
107
115
|
if not feeds:
|
|
108
116
|
return 1
|
|
109
|
-
|
|
117
|
+
out = {lang: feeds, "default": default or feeds}
|
|
118
|
+
if update:
|
|
119
|
+
out["_update"] = update # refresh.sh turns this into the notice line
|
|
120
|
+
sys.stdout.write(json.dumps(out, ensure_ascii=False))
|
|
110
121
|
return 0
|
|
111
122
|
|
|
112
123
|
|
package/statusline.sh
CHANGED
|
@@ -42,3 +42,10 @@ if [ -s "$CACHE_FILE" ]; then
|
|
|
42
42
|
else
|
|
43
43
|
printf '📰 …' # first run, before the first refresh lands
|
|
44
44
|
fi
|
|
45
|
+
|
|
46
|
+
# --- server-driven update nudge (written/cleared by refresh.sh), below the news ---
|
|
47
|
+
NOTICE_FILE="$CACHE_DIR/notice"
|
|
48
|
+
if [ -s "$NOTICE_FILE" ]; then
|
|
49
|
+
[ -s "$CACHE_FILE" ] || printf '\n' # placeholder path prints without a newline
|
|
50
|
+
head -1 "$NOTICE_FILE"
|
|
51
|
+
fi
|