social-autoposter 1.6.52 → 1.6.53
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 +28 -16
- package/package.json +1 -1
- package/scripts/a16z_pearx_calendar_reminders.py +99 -0
- package/scripts/linkedin_killswitch.py +19 -1
package/bin/cli.js
CHANGED
|
@@ -520,31 +520,43 @@ function installBrowserHarness() {
|
|
|
520
520
|
}
|
|
521
521
|
|
|
522
522
|
// Step 2 + 3: clone + `uv tool install -e .` browser-harness.
|
|
523
|
+
//
|
|
524
|
+
// PINNED to a known-good upstream commit instead of tracking origin/HEAD.
|
|
525
|
+
// The installer used to fetch+reset --hard to HEAD on every run, so any
|
|
526
|
+
// upstream change shipped to users untested (this is how the two-blank-tab
|
|
527
|
+
// regression in upstream daemon.py attach behavior could reach users). Our
|
|
528
|
+
// launch-at-real-URL fix in server.py/twitter-backend.sh neutralizes that
|
|
529
|
+
// class of bug regardless, but pinning stops surprise upstream drift. Bump
|
|
530
|
+
// BROWSER_HARNESS_PIN deliberately after validating a newer upstream against
|
|
531
|
+
// the shipped server.py contract.
|
|
532
|
+
const BROWSER_HARNESS_PIN = '6d20866664ea3d9691b27bbf64f42ae097437dc3';
|
|
523
533
|
const harnessDir = path.join(HOME, 'Developer', 'browser-harness');
|
|
534
|
+
const pinHarness = () => {
|
|
535
|
+
// Fetch the exact pinned commit (GitHub serves arbitrary SHAs) and hard-
|
|
536
|
+
// reset onto it. Works for a fresh clone and an existing checkout alike.
|
|
537
|
+
const fetch = spawnSync('git', ['-C', harnessDir, 'fetch', '--depth', '1', 'origin', BROWSER_HARNESS_PIN], { stdio: 'inherit' });
|
|
538
|
+
if (fetch.status !== 0) {
|
|
539
|
+
console.warn(` WARNING: could not fetch pinned browser-harness commit ${BROWSER_HARNESS_PIN.slice(0, 9)}; using existing checkout.`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const reset = spawnSync('git', ['-C', harnessDir, 'reset', '--hard', 'FETCH_HEAD'], { stdio: 'inherit' });
|
|
543
|
+
if (reset.status !== 0) {
|
|
544
|
+
console.warn(' WARNING: could not reset browser-harness clone to pinned commit; using existing checkout.');
|
|
545
|
+
}
|
|
546
|
+
};
|
|
524
547
|
if (!fs.existsSync(harnessDir)) {
|
|
525
548
|
fs.mkdirSync(path.dirname(harnessDir), { recursive: true });
|
|
526
549
|
console.log(' cloning browser-harness from GitHub...');
|
|
527
550
|
const clone = spawnSync('git', ['clone', '--depth', '1', 'https://github.com/browser-use/browser-harness', harnessDir], { stdio: 'inherit' });
|
|
528
551
|
if (clone.status !== 0) {
|
|
529
552
|
console.warn(' WARNING: git clone failed; twitter-harness will not work until you clone manually.');
|
|
530
|
-
}
|
|
531
|
-
} else {
|
|
532
|
-
// Refresh the existing clone instead of silently reusing it. server.py
|
|
533
|
-
// invokes `browser-harness -c <script>`; a stale checkout that predates the
|
|
534
|
-
// `-c` interface (or otherwise drifted from upstream) makes every bh_run
|
|
535
|
-
// return the CLI usage string while looking "installed". fetch+reset --hard
|
|
536
|
-
// to current upstream so the installed CLI always matches the shipped
|
|
537
|
-
// server.py contract.
|
|
538
|
-
console.log(` browser-harness clone exists -> ${harnessDir}; updating to latest...`);
|
|
539
|
-
const fetch = spawnSync('git', ['-C', harnessDir, 'fetch', '--depth', '1', 'origin', 'HEAD'], { stdio: 'inherit' });
|
|
540
|
-
if (fetch.status === 0) {
|
|
541
|
-
const reset = spawnSync('git', ['-C', harnessDir, 'reset', '--hard', 'FETCH_HEAD'], { stdio: 'inherit' });
|
|
542
|
-
if (reset.status !== 0) {
|
|
543
|
-
console.warn(' WARNING: could not reset browser-harness clone to latest; using existing checkout.');
|
|
544
|
-
}
|
|
545
553
|
} else {
|
|
546
|
-
console.
|
|
554
|
+
console.log(` pinning browser-harness to ${BROWSER_HARNESS_PIN.slice(0, 9)}...`);
|
|
555
|
+
pinHarness();
|
|
547
556
|
}
|
|
557
|
+
} else {
|
|
558
|
+
console.log(` browser-harness clone exists -> ${harnessDir}; pinning to ${BROWSER_HARNESS_PIN.slice(0, 9)}...`);
|
|
559
|
+
pinHarness();
|
|
548
560
|
}
|
|
549
561
|
|
|
550
562
|
if (uvBin && fs.existsSync(harnessDir)) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""One-shot: create recurring accelerator-application reminders in
|
|
3
|
+
matt@mediar.ai's Google Calendar via DWD (same SA as the gmail keepalive),
|
|
4
|
+
requesting a calendar scope. If the scope isn't authorized in the mediar.ai
|
|
5
|
+
Workspace DWD config, this fails with unauthorized_client and we fall back.
|
|
6
|
+
"""
|
|
7
|
+
import json, time, urllib.parse, urllib.request, urllib.error
|
|
8
|
+
import google.auth
|
|
9
|
+
from google.auth.transport.requests import Request
|
|
10
|
+
from googleapiclient.discovery import build
|
|
11
|
+
|
|
12
|
+
SA_EMAIL = "gmail-dwd-impersonator@gmail-api-integration-486018.iam.gserviceaccount.com"
|
|
13
|
+
TARGET_USER = "matt@mediar.ai"
|
|
14
|
+
SCOPE = "https://www.googleapis.com/auth/calendar"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def mint_access_token():
|
|
18
|
+
creds, _ = google.auth.default(
|
|
19
|
+
scopes=["https://www.googleapis.com/auth/cloud-platform"]
|
|
20
|
+
)
|
|
21
|
+
creds.refresh(Request())
|
|
22
|
+
iat = int(time.time()); exp = iat + 3600
|
|
23
|
+
claim = {"iss": SA_EMAIL, "sub": TARGET_USER, "scope": SCOPE,
|
|
24
|
+
"aud": "https://oauth2.googleapis.com/token", "iat": iat, "exp": exp}
|
|
25
|
+
iam = build("iamcredentials", "v1", credentials=creds, cache_discovery=False)
|
|
26
|
+
signed = iam.projects().serviceAccounts().signJwt(
|
|
27
|
+
name=f"projects/-/serviceAccounts/{SA_EMAIL}",
|
|
28
|
+
body={"payload": json.dumps(claim)}).execute()
|
|
29
|
+
body = urllib.parse.urlencode({
|
|
30
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
31
|
+
"assertion": signed["signedJwt"]}).encode()
|
|
32
|
+
req = urllib.request.Request("https://oauth2.googleapis.com/token", data=body,
|
|
33
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"})
|
|
34
|
+
try:
|
|
35
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
36
|
+
return json.loads(resp.read())["access_token"]
|
|
37
|
+
except urllib.error.HTTPError as e:
|
|
38
|
+
raise RuntimeError(f"token exchange {e.code}: {e.read().decode()}") from None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
from google.oauth2.credentials import Credentials
|
|
43
|
+
token = mint_access_token()
|
|
44
|
+
cal = build("calendar", "v3", credentials=Credentials(token=token),
|
|
45
|
+
cache_discovery=False)
|
|
46
|
+
|
|
47
|
+
events = [
|
|
48
|
+
{
|
|
49
|
+
"summary": "Apply to a16z Speedrun (next cohort) - S4L",
|
|
50
|
+
"start": "2026-09-15",
|
|
51
|
+
"rrule": "RRULE:FREQ=MONTHLY;INTERVAL=4",
|
|
52
|
+
"description": (
|
|
53
|
+
"Reapply S4L to a16z Speedrun. Speedrun runs ~3 cohorts/year; "
|
|
54
|
+
"this fires every 4 months so you catch the next deadline.\n\n"
|
|
55
|
+
"Apply: https://speedrun.a16z.com/apply (start with email i@m13v.com)\n"
|
|
56
|
+
"Status / update existing app: https://speedrun.a16z.com/application-login\n\n"
|
|
57
|
+
"Reuse the saved answer set (pitch, traction, funding, founder bio). "
|
|
58
|
+
"Last filled 2026-06-03; still need citizenship, university, years of "
|
|
59
|
+
"experience, last-round date, and the 3 investor emails."
|
|
60
|
+
),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"summary": "Apply to PearX (next batch) - S4L",
|
|
64
|
+
"start": "2026-10-01",
|
|
65
|
+
"rrule": "RRULE:FREQ=MONTHLY;INTERVAL=6",
|
|
66
|
+
"description": (
|
|
67
|
+
"Reapply S4L to PearX. Pear runs 2 batches/year (summer + winter); "
|
|
68
|
+
"this fires every 6 months for the next window.\n\n"
|
|
69
|
+
"Apply: https://pear.vc/pearx-application/ (Airtable form; long-text "
|
|
70
|
+
"fields are contenteditable divs)\n\n"
|
|
71
|
+
"Reuse the saved answer set. PearX S26 app was filled 2026-06-03 "
|
|
72
|
+
"(left for review, not submitted)."
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
created = []
|
|
78
|
+
for ev in events:
|
|
79
|
+
body = {
|
|
80
|
+
"summary": ev["summary"],
|
|
81
|
+
"description": ev["description"],
|
|
82
|
+
"start": {"date": ev["start"]},
|
|
83
|
+
"end": {"date": ev["start"]},
|
|
84
|
+
"recurrence": [ev["rrule"]],
|
|
85
|
+
"reminders": {"useDefault": False, "overrides": [
|
|
86
|
+
{"method": "popup", "minutes": 24 * 60},
|
|
87
|
+
{"method": "email", "minutes": 24 * 60},
|
|
88
|
+
]},
|
|
89
|
+
"transparency": "transparent",
|
|
90
|
+
}
|
|
91
|
+
out = cal.events().insert(calendarId="primary", body=body).execute()
|
|
92
|
+
created.append((ev["summary"], out.get("htmlLink")))
|
|
93
|
+
|
|
94
|
+
for s, link in created:
|
|
95
|
+
print(f"CREATED: {s}\n {link}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
@@ -152,6 +152,24 @@ def _now_iso():
|
|
|
152
152
|
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
def _fmt_local(iso_or_dt):
|
|
156
|
+
"""Render a UTC ISO string (or datetime) in the machine's local timezone for
|
|
157
|
+
human-readable emails, e.g. '2026-06-03 16:35 PDT'. Falls back to the raw
|
|
158
|
+
input on any parse failure so the email still goes out."""
|
|
159
|
+
try:
|
|
160
|
+
dt = iso_or_dt
|
|
161
|
+
if isinstance(dt, str):
|
|
162
|
+
dt = _parse_ts(dt)
|
|
163
|
+
if dt is None:
|
|
164
|
+
return str(iso_or_dt)
|
|
165
|
+
if dt.tzinfo is None:
|
|
166
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
167
|
+
local = dt.astimezone()
|
|
168
|
+
return local.strftime("%Y-%m-%d %I:%M %p %Z").replace(" 0", " ")
|
|
169
|
+
except Exception:
|
|
170
|
+
return str(iso_or_dt)
|
|
171
|
+
|
|
172
|
+
|
|
155
173
|
def _ensure_dir():
|
|
156
174
|
os.makedirs(STATE_DIR, exist_ok=True)
|
|
157
175
|
|
|
@@ -1125,7 +1143,7 @@ def _cmd_recover_record(args):
|
|
|
1125
1143
|
"",
|
|
1126
1144
|
"We are NOT giving up: pipelines stay paused and we will make",
|
|
1127
1145
|
"one more login attempt after the restriction lifts, at",
|
|
1128
|
-
" " + str(retry_at) + "
|
|
1146
|
+
" " + _fmt_local(retry_at) + " (" + str(retry_at) + " UTC)",
|
|
1129
1147
|
"This is attempt " + str(attempts) + " of "
|
|
1130
1148
|
+ str(RECOVERY_RESTRICTED_MAX_ATTEMPTS) + ".",
|
|
1131
1149
|
"",
|