dirpush 0.1.0 → 0.1.1
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/.agents/skills/dirpush/SKILL.md +1 -1
- package/README.md +2 -1
- package/package.json +1 -1
- package/scripts/ab +2 -2
- package/src/cli.ts +39 -0
|
@@ -61,7 +61,7 @@ Workers read `dirpush capabilities` at the start of a session: email verificatio
|
|
|
61
61
|
- **"Set up dirpush"** / **"set up this product"** / **"add a product"**: `dirpush init <product-id>`, run the Setup check above if `dirpush capabilities` says it hasn't been done, then help the user fill `workspace/products/<id>/product.yaml` (and the shared `workspace/identity.yaml`, `permissions.yaml` the first time). Validate with `dirpush product show --product <id>`.
|
|
62
62
|
- **"Find N free directories I haven't submitted to and submit"**: `dirpush directories recommend --free --unsubmitted --limit N --product <id>`, show the list, `dirpush submissions enqueue <dir-id…> --product <id>`, then run the loop.
|
|
63
63
|
- **"Continue my launch"**: the whole routine, no other instructions needed (nothing from a previous conversation is either):
|
|
64
|
-
1. `dirpush actions` — tell the human what's waiting on them.
|
|
64
|
+
1. `dirpush actions` — tell the human what's waiting on them. CAPTCHAs and bot checks: tell them to run `dirpush checks` in their terminal (it opens each one in the agents' Chrome and hands the job back); never solve them yourself.
|
|
65
65
|
2. `dirpush submissions check` — marks pending listings live (skip if run in the last day).
|
|
66
66
|
3. Run the loop until `work next` is empty. Jobs still showing another worker with an expired lease are abandoned: `work next` hands them to you; continue from their last step.
|
|
67
67
|
4. Queue empty → `dirpush directories recommend --free --unsubmitted --limit 10 --product <id>`, show the list, enqueue only what the human approves.
|
package/README.md
CHANGED
|
@@ -24,7 +24,8 @@ dirpush init acme # sets up ~/.dirpush and installs the agent ski
|
|
|
24
24
|
| `dirpush directories recommend --free --unsubmitted --product <id>` | Directories that fit and haven't been tried |
|
|
25
25
|
| `dirpush submissions list --product <id>` | Every submission and its outcome |
|
|
26
26
|
| `dirpush submissions check` | Re-check pending listings and mark live ones |
|
|
27
|
-
| `dirpush
|
|
27
|
+
| `dirpush actions` | What needs you: CAPTCHAs, verification emails, questions |
|
|
28
|
+
| `dirpush checks` | Clear CAPTCHAs and bot checks one by one in the agents' Chrome; agents pick each job back up |
|
|
28
29
|
| `dirpush registry updates [--apply]` | Update your registry from the published one on dirpush.com |
|
|
29
30
|
| `dirpush registry audit` | Measure link type, noindex and bot checks on real listing pages |
|
|
30
31
|
| `dirpush submissions add <id or URL>` | Record a listing you made yourself |
|
package/package.json
CHANGED
package/scripts/ab
CHANGED
|
@@ -20,7 +20,7 @@ AB="agent-browser --headed --session ${AB_SESSION:-default} --profile ${AB_PROFI
|
|
|
20
20
|
# instead of launching a second Chrome on the same profile.
|
|
21
21
|
PORT="${LAUNCH_BROWSER_PORT:-9222}"
|
|
22
22
|
if [ -z "$AB_PROFILE" ] && curl -s -m 1 "http://127.0.0.1:$PORT/json/version" >/dev/null 2>&1; then
|
|
23
|
-
exec perl -e 'alarm shift; exec @ARGV' "${AB_TIMEOUT:-90}" agent-browser --session "${AB_SESSION:-default}" --cdp "$PORT" "$@"
|
|
23
|
+
exec perl -e 'alarm shift; exec @ARGV or die "cannot run $ARGV[0]: $!\n"' "${AB_TIMEOUT:-90}" agent-browser --session "${AB_SESSION:-default}" --cdp "$PORT" "$@"
|
|
24
24
|
fi
|
|
25
25
|
for host in mail.google.com gmail.com www.gmail.com inbox.google.com \
|
|
26
26
|
drive.google.com drive.usercontent.google.com docs.google.com sheets.google.com slides.google.com forms.google.com \
|
|
@@ -37,4 +37,4 @@ for path in "b/*/ManageAccount*" "ManageAccount*" "security*" "*/personal-info*"
|
|
|
37
37
|
done
|
|
38
38
|
# Per-call time limit (AB_TIMEOUT seconds, default 90): a hung call (seen on file uploads) must not freeze a worker.
|
|
39
39
|
# Exit 142 = timed out; then run `dirpush ab close` and start over from the last checkpoint.
|
|
40
|
-
exec perl -e 'alarm shift; exec @ARGV' "${AB_TIMEOUT:-90}" $AB "$CHROME" "$@"
|
|
40
|
+
exec perl -e 'alarm shift; exec @ARGV or die "cannot run $ARGV[0]: $!\n"' "${AB_TIMEOUT:-90}" $AB "$CHROME" "$@"
|
package/src/cli.ts
CHANGED
|
@@ -523,6 +523,44 @@ const commands: Record<string, (() => Promise<unknown> | unknown) | undefined> =
|
|
|
523
523
|
.orderBy(desc(productApprovals.id)).get() ?? null;
|
|
524
524
|
},
|
|
525
525
|
"assets capture-complete": () => consumeProductApproval(db, Number(arg(0, "approval-id"))),
|
|
526
|
+
/**
|
|
527
|
+
* The human's side of bot checks, one job at a time in the terminal: opens the page in the agents' Chrome, waits,
|
|
528
|
+
* then hands the job back to the agents (or records that the human submitted it). Agents never solve these.
|
|
529
|
+
*/
|
|
530
|
+
async "checks"() {
|
|
531
|
+
const types = (flags.type ?? "captcha,bot_protection").split(",").map((t) => t.trim());
|
|
532
|
+
const queue = db.select().from(jobs)
|
|
533
|
+
.where(and(eq(jobs.kind, "submission"), eq(jobs.status, "blocked"), inArray(jobs.blockType, types))).all()
|
|
534
|
+
.filter((j) => !flags.product || j.productId === flags.product);
|
|
535
|
+
if (!queue.length) return { done: 0, note: `nothing blocked on ${types.join(" or ")}` };
|
|
536
|
+
if (!process.stdin.isTTY) throw new Error("checks is interactive: run it yourself in a terminal (it's the human's part)");
|
|
537
|
+
const ab = (...a: string[]) => Bun.spawnSync([join(PKG, "scripts/ab"), ...a], { env: { ...process.env, LAUNCH_HOME: HOME }, stdin: "ignore", stdout: "ignore", stderr: "ignore" }).exitCode;
|
|
538
|
+
const lines = console[Symbol.asyncIterator]();
|
|
539
|
+
const ask = async (q: string) => { process.stdout.write(q); return ((await lines.next()).value ?? "q").trim().toLowerCase(); };
|
|
540
|
+
const tally = { handedBack: [] as string[], submittedByYou: [] as string[], skipped: [] as string[] };
|
|
541
|
+
for (const [i, job] of queue.entries()) {
|
|
542
|
+
const dir = await loadDirectory(job.subject).then((d) => d.directory).catch(() => null);
|
|
543
|
+
const url = job.blockMessage?.match(/https?:\/\/[^\s)'"]+/)?.[0]?.replace(/[.,;]$/, "") ?? dir?.submission_url ?? dir?.website;
|
|
544
|
+
console.log(`\n[${i + 1}/${queue.length}] ${dir?.name ?? job.subject} (${job.blockType})\n${job.blockMessage ?? ""}\n`);
|
|
545
|
+
if (url && ab("open", url) === 0) Bun.spawnSync([join(PKG, "scripts/show-browser")], { env: { ...process.env, LAUNCH_HOME: HOME }, stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
|
546
|
+
else if (url) console.log(`couldn't open the agents' Chrome; open it yourself: ${url}`);
|
|
547
|
+
console.log("Clear the check in that window (and anything the message asks for).");
|
|
548
|
+
let a = "";
|
|
549
|
+
while (!["d", "s", "n", "q"].includes(a)) a = await ask("[d] cleared, agents take it from here [s] I submitted it myself [n] skip [q] quit: ");
|
|
550
|
+
if (a === "q") break;
|
|
551
|
+
if (a === "n") { tally.skipped.push(job.subject); continue; }
|
|
552
|
+
if (a === "d") {
|
|
553
|
+
J.unblock(db, job.id, "human cleared the bot check in the agents' Chrome; continue in the same browser session");
|
|
554
|
+
tally.handedBack.push(job.subject);
|
|
555
|
+
} else {
|
|
556
|
+
const link = await ask("listing URL, if there is one (Enter to skip): ");
|
|
557
|
+
J.recordManual(db, job.id, "pending", link && link !== "q" ? link : undefined);
|
|
558
|
+
tally.submittedByYou.push(job.subject);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return { ...tally, next: tally.handedBack.length ? `ask your agent to "continue my launch": ${tally.handedBack.length} job(s) are queued again` : undefined };
|
|
562
|
+
},
|
|
563
|
+
|
|
526
564
|
/** Everything waiting on the human, grouped. --product narrows it. */
|
|
527
565
|
"actions": () => {
|
|
528
566
|
const mine = <T extends { productId: string }>(xs: T[]) => xs.filter((x) => !flags.product || !x.productId || x.productId === flags.product);
|
|
@@ -661,6 +699,7 @@ const HELP: [string, [string, string][]][] = [
|
|
|
661
699
|
["products list", "all products in this workspace"],
|
|
662
700
|
["permission <group.action>", "automatic | confirm | never for an action, e.g. accounts.oauth_google"],
|
|
663
701
|
["actions [--product id]", "everything waiting on the human, grouped"],
|
|
702
|
+
["checks [--type captcha,bot_protection] [--product id]", "for the human: clear CAPTCHAs and bot checks one by one in the agents' Chrome, then hand each job back"],
|
|
664
703
|
]],
|
|
665
704
|
["Work loop", [
|
|
666
705
|
["work next [--claim]", "next submission job (unknown ones first); --claim leases it to you"],
|