dirpush 0.1.7 → 0.1.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/checks.ts +30 -36
package/package.json CHANGED
@@ -37,7 +37,7 @@
37
37
  "url": "https://github.com/baronunread/dirpush.git"
38
38
  },
39
39
  "homepage": "https://dirpush.com",
40
- "version": "0.1.7",
40
+ "version": "0.1.8",
41
41
  "keywords": [
42
42
  "directories",
43
43
  "startup",
package/src/checks.ts CHANGED
@@ -42,11 +42,6 @@ async function ask(prompt: string) {
42
42
  try { return (await rl.question(prompt)).trim(); } finally { rl.close(); }
43
43
  }
44
44
 
45
- const WHAT: Record<string, string> = {
46
- captcha: "Tick the CAPTCHA, then do whatever the note above asks.",
47
- bot_protection: "Wait for the “checking your browser” page to pass, or tick its box if it shows one.",
48
- };
49
-
50
45
  export async function checks(db: Db, opts: { product?: string; types: string[] }) {
51
46
  if (!process.stdin.isTTY) throw new Error("checks is interactive: run `dirpush checks` yourself in a terminal");
52
47
  const blocked = (types: string[]) => db.select().from(jobs)
@@ -58,36 +53,35 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
58
53
  let signedIn = false;
59
54
 
60
55
  const summary = async () => {
61
- console.log(`\n${bold("Done for now.")}`);
62
- if (handedBack.length) console.log(`${green("✓")} Ready for the agents to finish: ${handedBack.join(", ")}`);
63
- if (byYou.length) console.log(`${green("✓")} Submitted by you: ${byYou.join(", ")}`);
64
- if (skipped.length) console.log(`${dim("·")} Skipped, still waiting: ${skipped.join(", ")}`);
56
+ const parts = [handedBack.length && `${handedBack.length} back to the agents`, byYou.length && `${byYou.length} done by you`, skipped.length && `${skipped.length} skipped`].filter(Boolean);
57
+ console.log(`\n${green("✓")} ${bold("Done.")} ${parts.join(" · ") || "Nothing changed."}`);
65
58
  if (handedBack.length) await startAgent(handedBack.length);
66
59
  };
67
60
 
68
61
  const googleSignIn = async () => {
69
62
  const busy = db.select().from(jobs).where(and(eq(jobs.status, "running"), gt(jobs.leaseExpiresAt, Date.now()))).all();
70
63
  if (busy.length) {
71
- console.log(yellow(`An agent is using the browser right now (${busy.map((j) => j.subject).join(", ")}). Signing in closes that window.`));
72
- if ((await key(`${cyan("[enter]")} close it and sign in ${cyan("[n]")} not now `, "\rn")) === "n") return false;
64
+ console.log(yellow(` An agent is using the browser (${busy.map((j) => j.subject).join(", ")}); signing in closes it.`));
65
+ if ((await key(` ${cyan("enter")} sign in anyway ${cyan("n")} not now `, "\rn")) === "n") return false;
73
66
  }
74
- console.log(`A normal Chrome window opens on Google's sign-in page (the agents' own profile, nothing automated).`);
75
- console.log(`Sign in, then ${bold("quit that window with ⌘Q")} to come back here.`);
67
+ console.log(dim(" Waiting for you to quit that Chrome window…"));
76
68
  script("google-login"); // returns when that Chrome quits
77
- const ok = (await key(`Signed in? ${cyan("[y]")} yes ${cyan("[n]")} no `, "yn")) === "y";
69
+ const ok = (await key(` Signed in? ${cyan("y")} yes ${cyan("n")} no `, "yn")) === "y";
78
70
  if (ok) signedIn = true;
79
- else console.log(dim("No problem: run `dirpush checks` again whenever you want to retry."));
80
71
  return ok;
81
72
  };
82
73
 
83
- console.log(`${bold("dirpush checks")} ${dim(`${google.length} need Google sign-in · ${queue.length} CAPTCHAs or bot checks`)}`);
84
- if (!google.length && !queue.length) return console.log("Nothing is waiting on you.");
85
- console.log(dim("Agents never solve these. You clear each one, then an agent finishes the submission (at the end I can start yours)."));
74
+ const total = queue.length + (google.length ? 1 : 0);
75
+ if (!total) return console.log("Nothing is waiting on you.");
76
+ console.log(`${bold("dirpush checks")} ${dim(`· ${total} to clear · an agent finishes each one after you`)}`);
77
+ const host = (u: string) => u.replace(/^https?:\/\/(www\.)?/, "").replace(/\/$/, "");
78
+ const item = (n: number, name: string, where: string, todo: string) =>
79
+ console.log(`\n${dim(`${n}/${total}`)} ${bold(name)} ${dim(where)}\n ${todo}`);
80
+ const keys = (pairs: [string, string][]) => ` ${pairs.map(([k, l]) => `${cyan(k)} ${l}`).join(" ")} `;
86
81
 
87
82
  if (google.length) {
88
- console.log(`\n${bold("1. Sign into Google")} ${dim(google.map((j) => j.subject).join(", "))}`);
89
- console.log("These directories use “Sign in with Google”. Sign in once and the agents can use it from then on.");
90
- const k = await key(`${cyan("[enter]")} sign in now ${cyan("[n]")} later ${cyan("[q]")} quit `, "\rnq");
83
+ item(1, "Google sign-in", google.length === 1 ? "1 directory" : `${google.length} directories`, "A Chrome window opens: sign in, then quit it with ⌘Q.");
84
+ const k = await key(keys([["enter", "sign in"], ["n", "later"], ["q", "quit"]]), "\rnq");
91
85
  if (k === "q") return await summary();
92
86
  if (k === "\r" && (await googleSignIn())) {
93
87
  for (const j of google) { J.unblock(db, j.id, "human signed into Google in the agents' Chrome profile"); handedBack.push(j.subject); }
@@ -97,20 +91,21 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
97
91
  for (const [i, job] of queue.entries()) {
98
92
  const dir = await loadDirectory(job.subject).then((d) => d.directory).catch(() => null);
99
93
  const url = job.blockMessage?.match(/https?:\/\/[^\s)'"”]+/)?.[0]?.replace(/[.,;:]$/, "") ?? dir?.submission_url ?? dir?.website;
94
+ // Sites that break under automation: the human does them in their own browser (the agent's note says what to do).
95
+ const yours = /your own browser/i.test(job.blockMessage ?? "");
100
96
  const open = () => {
101
- if (!url) return console.log(yellow("No page saved for this one; open the directory yourself."));
102
- if (script("ab", "open", url) === 0) script("show-browser");
103
- else console.log(yellow(`Couldn't open the agents' Chrome. Open it yourself: ${url}`));
97
+ if (!url) return;
98
+ if (yours) Bun.spawnSync(["open", url]);
99
+ else if (script("ab", "open", url) === 0) script("show-browser");
100
+ else console.log(yellow(` Couldn't open the agents' Chrome; open ${url} yourself.`));
104
101
  };
105
- console.log(`\n${bold(`${google.length ? i + 2 : i + 1}. ${dir?.name ?? job.subject}`)} ${dim(`${i + 1} of ${queue.length}`)}`);
106
- if (job.blockMessage) console.log(dim(job.blockMessage));
107
- if (url) console.log(`${dim("Page:")} ${url}`);
108
- console.log(WHAT[job.blockType ?? ""] ?? "Do what the note above asks.");
109
- console.log(dim("Asked to sign in with Google? Press g: Google refuses sign-in in the agents' window."));
110
- console.log(dim("Check never passes there? Submit it from your own browser instead, then press s."));
102
+ const todo = yours
103
+ ? `Do this one in your own browser: ${(job.blockMessage ?? "").split(/your own browser:?/i)[1]?.trim().replace(/,? then press s in dirpush checks\.?$/i, "") ?? "submit rdyrct"}.`
104
+ : "Tick the box in the window that opened. No box? Just press enter.";
105
+ item(i + 1 + (google.length ? 1 : 0), dir?.name ?? job.subject, url ? host(url) : "", todo);
111
106
  open();
112
107
  for (;;) {
113
- const k = await key(`${cyan("enter")} cleared, agent finishes it ${cyan("s")} I submitted it ${cyan("o")} reopen ${cyan("g")} Google sign-in ${cyan("n")} skip ${cyan("q")} quit › `, "\rsognq");
108
+ const k = await key(keys(yours ? [["s", "done"], ["n", "skip"], ["q", "quit"]] : [["enter", "done"], ["s", "I submitted it myself"], ["n", "skip"], ["q", "quit"]]), yours ? "sognq" : "\rsognq");
114
109
  if (k === "o") { open(); continue; }
115
110
  if (k === "g") { if (!signedIn) await googleSignIn(); open(); continue; }
116
111
  if (k === "q") return await summary();
@@ -119,7 +114,7 @@ export async function checks(db: Db, opts: { product?: string; types: string[] }
119
114
  J.unblock(db, job.id, "human cleared the check in the agents' Chrome; continue in the same browser session");
120
115
  handedBack.push(job.subject);
121
116
  } else {
122
- const link = await ask(`Listing URL, if there is one ${dim("(enter to skip)")}: `);
117
+ const link = await ask(` Listing link ${dim("(enter to skip)")}: `);
123
118
  J.recordManual(db, job.id, "pending", link || undefined);
124
119
  byYou.push(job.subject);
125
120
  }
@@ -140,12 +135,11 @@ async function startAgent(n: number) {
140
135
  const agent = known.find((a) => caps?.checked_by?.startsWith(a.id)) ?? known[0];
141
136
  const prompt = "continue my launch";
142
137
  if (!agent || process.platform !== "darwin") {
143
- console.log(`\nNext: open your agent and say ${bold(`“${prompt}”`)}; it finishes the ${n} job(s) above.`);
138
+ console.log(`Next: tell your agent ${bold(`“${prompt}”`)} to finish them.`);
144
139
  return;
145
140
  }
146
- console.log(`\nThe ${n} job(s) above are waiting for an agent to finish them.`);
147
- const k = await key(`${cyan("[enter]")} open ${agent.name} on “${prompt}” now ${cyan("[n]")} I'll do it later `, "\rn");
148
- if (k === "n") return console.log(dim(`Whenever you're ready: open your agent and say “${prompt}”.`));
141
+ const k = await key(` ${cyan("enter")} open ${agent.name} to finish them ${cyan("n")} later `, "\rn");
142
+ if (k === "n") return console.log(dim(` Later: tell your agent “${prompt}”.`));
149
143
  // A new Terminal window, so the human watches it work and approves what it asks for. Nothing runs unattended.
150
144
  const cmd = `cd ${JSON.stringify(process.cwd())} && ${agent.bin} ${JSON.stringify(prompt)}`;
151
145
  const ok = Bun.spawnSync(["osascript", "-e", `tell application "Terminal" to do script ${JSON.stringify(cmd)}`, "-e", `tell application "Terminal" to activate`], { stdin: "ignore", stdout: "ignore", stderr: "ignore" }).exitCode === 0;