@rackbops/plugin-tracker 0.38.0 → 0.39.0
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/README.md +11 -9
- package/dist/plugin.js +2187 -1530
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -48,6 +48,8 @@ as in a server (plan 5.5, item 39).
|
|
|
48
48
|
| `/scout new interests [lens] [for] [notes] [every]` | registered | An interest scout (docket-types' `scout` type, since 0.6.0; plan 1.2 row 1): every `every` days (default 1, at most 30) at your preferred hour, a model run looks on the web for 5 to 10 things published or available in the last 30 days that fit the `interests` (separated by commas, at most 20, each at most 80 characters) through the `lens` -- `general` (the default), `birthday` (fun or a little grandiose), `anniversary` (a romantic angle) or `christmas` (tied to their interests) -- for `for` (someone else, e.g. Anne; empty means you), weighing `notes`, and DMs them to you (and accepted recipients). A link it showed before is not shown again (it remembers the newest 300); fewer than five come with the run's reason. At most 3 scouts per person; each run counts against the daily budget (below), up to 1.50 USD. Answers that the scout is not available while the city-hall Executor is not set up. |
|
|
49
49
|
| `/want name source [target] [max] [currency] [exclude] [hours] [judge]` | registered | A want-list watch (docket-types' `wantlist` type, or `wantjudge` when judged, since 0.6.0; plan 1.2 row 2). `source` is `page`: `target` is a shop's listing or search page, checked like `/price`'s page and read once at once -- nothing is made unless its structured product data (JSON-LD `ItemList`s and `Product`s) lists something; `bgg`: `target` is a BoardGameGeek game's address or id, read through BGG's XML API while `TRACKER_BGG_TOKEN` is set; `shopify` (since 0.32.0): `target` is the words to search Shopify stores for (the name when empty), one Shopify catalog search per look through Firecrawl's Alexandria while `TRACKER_FIRECRAWL_KEY` is set (refused without it), for new copies in stock at or under `max`; a product counts only when its title has every word, and each look costs 5 Firecrawl credits, so `hours` is 24 to 168 for it; or `ebay`: with the eBay keys (`TRACKER_EBAY_CLIENT_ID` and `TRACKER_EBAY_CLIENT_SECRET`, since 0.33.0), one search per look through eBay's own Browse API (docket-types' `ebaySource`; eBay US, the newest fifty, narrowed by `max`), for listings whose title has every word of `target` (the words, the name when empty), new and used, an auction at its current bid (or its Buy It Now price, when it has one), the seller's name, feedback percentage and score shown; every hour by default, and the watcher still never opens an eBay page. Without the keys, `ebay` is an **inbox watch** (since 0.19.0; docket-types' `inbox` source), whose listings are sent in rather than read -- the watcher never opens eBay. A watch made before the keys were set stays an inbox watch; one made with them that outlives the keys fails each look and tells its owner once. An eBay API watch takes no alert emails: an alert for its words is left without a DM. It keeps no eBay data, so that the keyset may opt out of eBay's account-deletion notices: no finding is stored, its listings are in the DM only, six hours after a run its stored copy of the DM keeps only its first line and a link to eBay's own search (`ebay-forget.ts`; a send still owed then sends that), and it is never judged, whatever `judge` says, since a judged watch's Job is kept by city-hall's queue. For an inbox eBay watch, `target` is the words to search eBay for (the name when empty); the answer gives the watch's id, how listings reach it -- you search eBay in your own browser, such as with Claude in Chrome, and send what it finds to `POST /api/v1/tasks/<id>/listings` (Task API below), or, while the alert-email route is on (0.25.0, Alert emails below), forward eBay's saved-search alert emails to your alert address, which the answer gives -- and the eBay search itself, with `max` as eBay's own top price, to save on eBay too. `bgg` while the token is unset (BGG declined the application) is an inbox watch the same way: `target` is then a BGG game (kept as its id) or the words to look for (the name when empty). An inbox watch needs the web area (`TRACKER_WEB_URL`), since its listings arrive through the task API; without it, it is refused. It also needs the bot's `HTTP_PORT`, which the tracker cannot see: with `TRACKER_WEB_URL` set and `HTTP_PORT` unset, the watch is made but nothing can send it listings. Every `hours` (default 12 for a page, 24 for BGG and Shopify, 1 for eBay's API and for an inbox watch, whose reads cost nothing, at most 168) on the `poll` tick, the watch DMs each listing at or under `max` (then only listings that show a price) and in `currency` (when set) that it has not DMed before, up to 5 lines with the rest in `/task history`'s second message (up to 20, as many as fit in one message, the newest kept; the task's page in the web area shows the newest 50), with a Done button that ends the watch. Each listing DMed is a finding (an eBay API watch's excepted, above: its extra listings are left to eBay's own search). Three reads in a row that find nothing tell you once (a page whose list is empty is a search with no results, not nothing read). At most 20 new listings a run, the rest the next run. A redirect to eBay is refused on every hop. At most 20 watches per person, judged or not. eBay's pages are refused as a `page`. **Judged** (`judge`, default yes while the model runner is set up; `judge: true` without it is refused; fixed once made): the reads stay plain code, and new listings within the limits go to one `claude -p` Job first (docket-types' `wantjudge`, plan 5.4, item 63), which may open only those listings' pages on the watch's own site (never eBay, no web search) and says for each `match`, `maybe` or `no`, why, and what its page shows about the seller, as signals; the DM shows the `match` and `maybe` ones with those notes, and every listing is a finding with its verdict. 12 turns, 0.50 USD, 5 minutes a look; a look that fails for good sends the listings unchecked. Its reads are uncharged and its looks charged, like any model run; both wait while its owner is at a daily ceiling (with the budgets on). The judge may open only pages on the watch's own host (the pasted page's, or BGG's), and none at all for an inbox, Shopify or eBay watch: it judges from what each listing says. **`exclude`** (since 0.38.0, docket#77): words or phrases separated by commas, at most 20 of 80 characters each (1,000 characters in all); a listing whose title has any of them, as whole words in that order (case, accents and punctuation ignored, docket-types' `isExcluded`), is left out of every source before it is DMed or judged, and the confirmation names them. For a Shopify or eBay-API watch, a one-word entry that is one of the words searched for is refused, since every listing it keeps has that word. A field with no words in it is refused. |
|
|
50
50
|
| `/scout edit task [interests] [lens] [for] [notes] [every]` | registered | Changes your scout: `interests` replaces the whole list; anything left out stays; `for` or `notes` set to `-` clears it. A change of `every` keeps the start day. |
|
|
51
|
+
| `/news new subject [include] [exclude] [disambiguate] [shows] [every]` | registered | A news watch (docket-types' `news` type, since docket 0.19.0; plan item 153): every `every` days (default 7, at most 30) at your preferred hour, a model run looks on the web for items about the `subject` (at most 200 characters, with enough to tell it apart) from the last 30 days that are of a kind `include` names (separated by commas, at most 10, each at most 80 characters; empty means any news). Items seen before, eBay links, and items whose title or kind has an `exclude` word or phrase (separated by commas, at most 20) are dropped; `disambiguate` (at most 500 characters) says what similarly named acts or things it is not, and `shows` asks for each show's city, venue, date and tickets. When anything is left, a second run checks each item against its own page, and only what it keeps is DMed to you (and accepted recipients), each kept as a finding; a run with nothing new sends nothing and says "no news" in its history, and three failed runs in a row (search or review) are told to you once. At most 3 news watches per person; each run counts against the daily budget (below), up to 1.50 USD for the search and 0.75 USD for the review, each try (a failed try is retried once). Answers that the news watch is not available while the city-hall Executor is not set up. |
|
|
52
|
+
| `/news edit task [subject] [include] [exclude] [disambiguate] [shows] [every]` | registered | Changes your news watch: anything left out stays; `include`, `exclude` or `disambiguate` set to `-` clears it. A change of `every` keeps the start day. |
|
|
51
53
|
| `/tasks` | registered | Your active tasks and the ones you receive, each with its next run; then your paused ones, each naming up to three people it could not DM (then how many more). A list longer than one message (Discord's 2000 characters) is cut between whole lines and keeps your paused tasks in preference to the active ones that do not fit, since their ids are what `/task resume` needs (any paused ones that still do not fit are counted); it says how many active ones it left out and, when the web area is set up, points to `/web`, which lists them all. |
|
|
52
54
|
| `/task done task` / `/task snooze task [until]` | the task's owner | Answers the task's latest reminder (snooze: an hour, or until `until`). |
|
|
53
55
|
| `/task decide task choice [amount]` | the renewal's owner | Answers the renewal's latest ask -- keep, cancel or renewed -- with the amount actually paid when it changed (a button cannot carry one). Use it instead of the button, not after it: a run is answered once. |
|
|
@@ -236,7 +238,7 @@ served at a hashed path, cached for a year).
|
|
|
236
238
|
| `/` | My tasks: the same list as `/tasks` (active tasks owned and received, next run in your zone; paused ones and why), each linking to its history, and links to make a new one. |
|
|
237
239
|
| `/tasks/<id>` | A task's history: the same as `/task history`, for the owner, an accepted recipient or an admin. Anyone else gets the same 404 as an unknown id. The owner also sees Edit (not for a research request), Pause or Resume, and Delete. |
|
|
238
240
|
| `/new/wantlist` | A new want-list watch (GET, POST), by `/want`'s rules (`judge` too, yes or no): a page, a BGG game, or eBay (an inbox watch: `target` is the words to search for, the name when empty). Its edit changes the name, top price, currency, exclude words and hours; an emptied top price, currency or exclude field clears it, and where it looks -- an inbox watch's key and words too -- stays as made. |
|
|
239
|
-
| `/new/reminder`, `/new/renewal`, `/new/price`, `/new/research`, `/new/scout` | The editor's new-task forms (GET), and making one (POST). `/new/research` and `/new/
|
|
241
|
+
| `/new/reminder`, `/new/renewal`, `/new/price`, `/new/research`, `/new/scout`, `/new/news` | The editor's new-task forms (GET), and making one (POST). `/new/research`, `/new/scout` and `/new/news` are linked from My tasks only while the model runner is set up (a news watch's kinds go one per line, and an emptied "What counts", "What never counts" or "How to tell it apart" clears it); without it the page says the type is not available instead of showing a form. A scout's interests go in a text box, separated by commas or one per line; its edit form shows the list one per line, and an emptied "Who it is for" or "Notes" clears it. |
|
|
240
242
|
| `/tasks/<id>/edit` | The owner's edit form (GET) and saving it (POST). |
|
|
241
243
|
| `/tasks/<id>/pause`, `/resume`, `/delete` | POST only. Delete answers a confirmation first; only a second post carrying `confirm=yes` deletes. |
|
|
242
244
|
| `/resume-dms` | POST only: Resume DMs, the button on the banner every page shows a person whose DMs are paused (Pause after failed DMs, above). It resumes them as their next command would, and My tasks then says how many tasks are back on; pressed when they are not paused, it changes nothing. |
|
|
@@ -722,15 +724,15 @@ a research request cannot be edited, here or in Discord, and a `PATCH` of one an
|
|
|
722
724
|
| Method and path | What |
|
|
723
725
|
|---|---|
|
|
724
726
|
| `GET /api/v1/me` | The token's owner (id, name, zone, preferred hour) and the token (id, name, made, expires). |
|
|
725
|
-
| `GET /api/v1/types` | Each type this bot makes now (research and
|
|
726
|
-
| `GET /api/v1/capabilities` | What this bot can make for you, in one read: `types` (as `GET /types`), `owner` (your zone and preferred hour), `limits` (`liveTasks`, `priceTrackers`, `researchWaiting`, `scouts`, `wantWatches`, `pageSize`, `requestsBurst`, `requestsPerSecond`) and `sources`, which say whether each is set up on this bot -- `runner` (research and
|
|
727
|
+
| `GET /api/v1/types` | Each type this bot makes now (research, scout and news only while the model runner is set up), whether a `PATCH` takes it (`editable`; false for research, whose `edit` is empty), and its create and edit fields: name, JSON type, required (never, for an edit), description, and limits (`maxLength`, `minimum`, `maximum`, `enum`). This describes the tracker's own editor fields -- what these endpoints take -- and is **not** docket-core's `TaskType.intake` (`IntakeSpec`), which describes a type's config. |
|
|
728
|
+
| `GET /api/v1/capabilities` | What this bot can make for you, in one read: `types` (as `GET /types`), `owner` (your zone and preferred hour), `limits` (`liveTasks`, `priceTrackers`, `researchWaiting`, `scouts`, `wantWatches`, `pageSize`, `requestsBurst`, `requestsPerSecond`, and `newsWatches` since 0.39.0) and `sources`, which say whether each is set up on this bot -- `runner` (research, scouts and news watches), `bgg`, `ebay` (eBay's own API), `shopify`, `mail` (alert email) and `webArea` (the web area, `TRACKER_WEB_URL`, which browser research needs) -- as true or false only, never a key, token or address. (0.37.0, #369.) |
|
|
727
729
|
| `GET /api/v1/tasks` | Your tasks that are not deleted -- active, paused and done -- oldest first. Not the ones shared with you. With `?limit=N` (1 to 200) one page of them, and `nextCursor` (null on the last page): send it back as `?limit=N&cursor=<nextCursor>` for the next. A task made or deleted between two pages moves no other across the cut, so each one still there is listed exactly once. Without `limit`, all of them, and no `nextCursor`. Any other query parameter is ignored; `limit` or `cursor` given twice is `400 invalid`. (0.37.0, #369.) |
|
|
728
|
-
| `POST /api/v1/tasks` | Makes one: `type` is `reminder`, `renewal`, `price`, `research`, `scout` or `wantlist`, and the rest are that type's create fields. `201`, with `Location`. A price's page is read first, and nothing is made unless a price is found in it. A research request's fields are `question` (required), `context`, `deadline`, `at` and `sources` (`web`, the default, or `browser`: it waits for the pages your browser sends in, below), all text. A scout's are `interests` (required, text, separated by commas or new lines), `lens`, `for` and `notes` (text) and `every` (a whole number of days). A want-list watch's (`wantlist`) are `name` (required), `target` (required but for an inbox watch), `source` (`page`, `bgg` or `ebay`), `max` (a number), `currency` (text), `exclude` (text: words or phrases separated by commas; an empty string clears it in an edit, and `settings.exclude` reads them back joined by `, `), `hours` (a whole number) and `judge` (`"yes"` or `"no"`; `"yes"` without the model runner is a `503`); a judged one comes back as `type: "wantjudge"`, edited by the same fields but `judge`; a task's JSON shows its `source` and `target`, which no edit changes -- an inbox watch's `source` reads back as `inbox` and its `target` is its inbox key -- and for an inbox watch its `site` (`ebay` or `bgg`), its words to `search` for and a BGG `game` when one was named. |
|
|
729
|
-
| `POST /api/v1/tasks/validate` | A dry run of `POST /tasks`: the same body, checked, and nothing made or written. A reminder, a renewal or a
|
|
730
|
+
| `POST /api/v1/tasks` | Makes one: `type` is `reminder`, `renewal`, `price`, `research`, `scout`, `news` or `wantlist`, and the rest are that type's create fields. A news watch's are `subject` (required), `include`, `exclude` and `disambiguate` (text; an empty string clears one in an edit), `shows` (`"yes"` or `"no"`) and `every` (a whole number of days); without the model runner it is a `503`, as a scout is. `201`, with `Location`. A price's page is read first, and nothing is made unless a price is found in it. A research request's fields are `question` (required), `context`, `deadline`, `at` and `sources` (`web`, the default, or `browser`: it waits for the pages your browser sends in, below), all text. A scout's are `interests` (required, text, separated by commas or new lines), `lens`, `for` and `notes` (text) and `every` (a whole number of days). A want-list watch's (`wantlist`) are `name` (required), `target` (required but for an inbox watch), `source` (`page`, `bgg` or `ebay`), `max` (a number), `currency` (text), `exclude` (text: words or phrases separated by commas; an empty string clears it in an edit, and `settings.exclude` reads them back joined by `, `), `hours` (a whole number) and `judge` (`"yes"` or `"no"`; `"yes"` without the model runner is a `503`); a judged one comes back as `type: "wantjudge"`, edited by the same fields but `judge`; a task's JSON shows its `source` and `target`, which no edit changes -- an inbox watch's `source` reads back as `inbox` and its `target` is its inbox key -- and for an inbox watch its `site` (`ebay` or `bgg`), its words to `search` for and a BGG `game` when one was named. |
|
|
731
|
+
| `POST /api/v1/tasks/validate` | A dry run of `POST /tasks`: the same body, checked, and nothing made or written. A reminder, a renewal, a scout or a news watch gets every check its create runs before it writes (the type's rules, the live-task cap, the scout or news-watch cap) and `{"valid": true, "checked": "full", "timeZone", "cadence", "firstAt", "thenAt", "message"}`: its first run and the run after, as UTC instants, placed in your zone as the scheduler places them (a renewal already within its lead is asked at once: its `firstAt` is then the ask's own date, already past, as the made task's `nextAt` will be). A price, a want-list watch or a research request gets its fields checked and the live-task cap (`"checked": "fields"`), and none of its type's own checks: its create reads a page or a source, or asks the model runner, so the rest is known only when it is made, and its create may refuse first for another reason (a bad address, the price-tracker cap). A refusal is the create's own (`400 invalid`, `unknown_field`, `409 limit_reached`, `503 unavailable`). Advice, not a reservation: a create made after it can still meet a cap. Takes no `If-Match`; an `Idempotency-Key` is ignored, as nothing is written. (0.37.0, #369.) |
|
|
730
732
|
| `GET /api/v1/tasks/<id>` | One of your tasks (a deleted one too), with its history: the newest runs and changes, as `/task history` shows them. Each run carries `delivery` (0.37.0, #369): `state` is `not_run` (not finished yet: queued or running, so no copy is planned), `nothing_to_send` (no copy: it fired with no message for anyone, was skipped, or failed before it had one), `sending` (a copy still owed: not tried, out now, or retrying), `delivered` (every copy went out), `unconfirmed` (a send failed in a way that does not say whether it went out; never resent) or `failed` (a copy failed for good); when copies differ, `failed` outranks `unconfirmed`, which outranks `sending`, then `delivered`. `reason` is the error of the failed or unconfirmed copy; `sends` lists each copy, `to`, `status` and `error`. |
|
|
731
733
|
| `PATCH /api/v1/tasks/<id>` | Edits it: the fields to change. A price's page is not editable. |
|
|
732
734
|
| `POST /api/v1/tasks/<id>/pause`, `/resume` | Body `{}`. As the web's Pause and Resume, and `/task resume`. |
|
|
733
|
-
| `POST /api/v1/tasks/<id>/run` | Body `{}`. One extra run now, off the schedule: `202`, and its lane's next tick takes it -- a reminder within a minute (`notify`), a price or want-list watch within a minute (`poll`), a scout or judged watch to the model runner on the `execute` tick, against your daily ceilings like any model run (one met holds it until midnight Eastern). The next scheduled run stays where it was. For an active reminder that repeats, a price, a want-list watch (judged or not) or a
|
|
735
|
+
| `POST /api/v1/tasks/<id>/run` | Body `{}`. One extra run now, off the schedule: `202`, and its lane's next tick takes it -- a reminder within a minute (`notify`), a price or want-list watch within a minute (`poll`), a scout, news watch or judged watch to the model runner on the `execute` tick, against your daily ceilings like any model run (one met holds it until midnight Eastern). The next scheduled run stays where it was. For an active reminder that repeats, a price, a want-list watch (judged or not), a scout or a news watch; anything else is `409 cannot_run_now`: a one-off (its own run stays queued, so it would run twice), a renewal (its ask is about its own date), a research request, a task with a run already due, or one waiting on a retry of its last look (or, for a news watch, on its review). A paused or finished task is `409 conflict`. Pausing the task drops a run now not yet taken; a pause for failed DMs keeps it, to run on resume. At most 10 runs now per person in any 24 hours, over all your tasks: `409 limit_reached` past that. An extra run of a price or want-list watch counts toward its "nothing found N times in a row" warning like any run. Takes no `If-Match`. (0.37.0, #369; docket 0.18.0's `runNowKey`.) |
|
|
734
736
|
| `POST /api/v1/tasks/<id>/listings` | Sends listings in to your inbox watch (an eBay watch made without the bot's eBay keys, or a BGG one made without BGG's API): `{"listings": [{"title", "url", "price", "currency", "condition", "seller"}, ...]}`, 1 to 40 of them, `title` and an absolute http(s) `url` on the watch's own site (one of eBay's own domains, `EBAY_DOMAINS` in `src/web/api-listings.ts`, or a subdomain, for an eBay watch; `boardgamegeek.com` or a subdomain for a BGG one) required, `price` a number or text such as `"$30.00"`, `currency` three letters. Each is cleaned by docket-types' `submittedListing` (an eBay item's address becomes its bare `/itm/<number>`, so one item stays one listing); the usable ones are kept, the others counted: `200 {"accepted", "rejected", "message"}`. None usable, or the wrong shape, is `400`; more than 40 is `400 too_many`; anyone else's, a deleted or finished watch, or not a watch, is `404`; a page, BGG-API, Shopify or eBay-API watch is `409` (its listings are read, not sent). The next look DMs the new ones within the watch's limits, each once. The newest 100 per watch are kept, as many as one look reads: more than 100 sent between two looks loses the oldest. Body up to 64 KiB. |
|
|
735
737
|
| `POST /api/v1/tasks/<id>/sources` | Sends pages in to your research request made with `sources: browser` (0.28.0, #125), or to one waiting for pages for a fuller pass (0.29.0, its JSON's `waitingForPages`): `{"pages": [{"url", "title", "text"}, ...]}`, 1 to 10 of them, an absolute http(s) `url` and the page's main `text` required. Each is cleaned by docket-types' `gatheredPages` (the text cut to 12,000 characters, the title to one line), and a second page with the same address in one request is left out; a page whose address the request already holds replaces it in its place: `200 {"accepted", "rejected", "overBudget", "pages", "message"}`, `accepted` being how many were stored, `rejected` how many were unusable (no absolute http(s) address or no text, or an address sent twice), `overBudget` how many were left out whole because their text would take the request past 99,800 characters of page text in all (what docket's 100,000-character budget across a request's pages uses whole, the text a replaced page held not counted), and `pages` how many it holds. A request holds at most 10 pages: more is `409 too_many`, saying how many it holds; when no page fits the text budget, `409 too_much_text`. None usable, or the wrong shape, is `400`; more than 10 in one request `400 too_many`; anyone else's, a deleted or finished one, or not a research request, is `404`; a web request not waiting for a fuller pass, or one whose run was moved or has started, is `409`. Body up to 1 MiB. |
|
|
736
738
|
| `POST /api/v1/tasks/<id>/sources/ready` | Body `{}`. Starts that request on the pages it holds: its queued run is moved to now (docket-core's `reschedule`), and the execute lane submits it within a minute or two. For a request waiting for a fuller pass (0.29.0), it moves the request to now and then deletes the queued follow-up its wait rests on, under the task's lock, so the next execute tick runs the second pass on the pages; refused (`409`) once that follow-up has started, fired or has its Job out at the runner. No page in yet, a paused request, or one no longer waiting is `409`; the others as for `/sources`. From then the request takes no more pages; nor does it once a run of it has started, `ready` or not (the run at its expiry). Both routes take the task's lock: while the model runner has the task, either is `409 busy`. |
|
|
@@ -779,7 +781,7 @@ version_mismatch` and nothing is written. Without `If-Match` a write goes throug
|
|
|
779
781
|
| 404 | `not_found` | No such endpoint, or no such task of yours. Anyone else's task -- one shared with you, or any task to an admin's token -- answers exactly as an unknown id. |
|
|
780
782
|
| 405 | `method_not_allowed` | With `Allow`. |
|
|
781
783
|
| 409 | `conflict` | A finished task edited, a research request edited, a pause of a task not active (a resume of one not paused), or pages sent to (or `ready` for) a research request that does not wait for them. |
|
|
782
|
-
| 409 | `limit_reached` | 200 active or paused tasks, 20 price trackers, 5 research requests waiting (one waiting for a fuller pass not counted), 3 scouts, or 20 want-list watches, as the commands count them; or 10 runs now in the last 24 hours. |
|
|
784
|
+
| 409 | `limit_reached` | 200 active or paused tasks, 20 price trackers, 5 research requests waiting (one waiting for a fuller pass not counted), 3 scouts, 3 news watches, or 20 want-list watches, as the commands count them; or 10 runs now in the last 24 hours. |
|
|
783
785
|
| 409 | `cannot_run_now` | A run now of a task that cannot run now: a one-off, a renewal, a research request, one with a run already due, or one waiting on a retry (its message says which). |
|
|
784
786
|
| 409 | `too_many` | Pages sent to a research request that would then hold more than 10. (As a `400`, more than a request takes in one body.) |
|
|
785
787
|
| 409 | `too_much_text` | None of the pages sent to a research request fits in the text it may still hold (99,800 characters in all). |
|
|
@@ -822,7 +824,7 @@ $ curl -s https://clerk.example.com/tracker/api/v1/tasks/t9 -H "Authorization: B
|
|
|
822
824
|
| Piece | File | Notes |
|
|
823
825
|
|---|---|---|
|
|
824
826
|
| Store | `src/store.ts`, `src/schema.ts` | docket's `Store` port on `bun:sqlite`, in `<dataDir>/tracker/tracker.sqlite` (WAL). A Discord id belongs to at most one user. Schema versioned by `PRAGMA user_version`; a shipped migration is never edited. Schema 5 (0.9.0) is docket 0.4.0's: a run's `record`, a series point's `key`, `deliveries` (from `delivery_claims`), `usage` and `notices`. Schema 6 (0.11.0) is docket 0.5.0's, all additive: `findings`, a charge's `usage.key`, and the Executor's `executor_jobs`. Forget-me erases a person's findings and their runs' Job records too. Schema 8 (0.19.0) adds `want_inbox`, the listings sent in for an inbox watch. Schema 9 (0.20.0) adds `users.usr_subject`, a person's usr user id, null until linked, unique when set. Schema 10 (0.25.0) adds `mail_addresses`. Schema 11 (0.28.0) adds `research_sources`, the pages a browser sent in for a research request (at most 10 per request, one per address, 99,800 characters of text in all), dropped once the request ends (by the execute tick's housekeeping, so only while the execute lane is configured) and always erased by forget-me. Schema 12 (0.34.0) adds `intake_conversations`, the intake's DM conversations. `store.test.ts` runs docket's `STORE_CONTRACT` against it. |
|
|
825
|
-
| Notify lane | `src/notify-lane.ts` | docket's `Lanes.tickNotify` on two host ticks, every 60 s, one pass per task with work (a due run, a run in flight, a DM owed or claimed), each under that task's lock through a view of the store limited to that task: `notify` runs every type but the page readers, `poll` runs only them (`price`, `wantlist`) with the Fetch port, so a slow page never holds up a reminder. A task the execute tick holds is left for the next notify tick, never waited on. Registers `reminder`, `renewal`, `price`, `research`, `scout`, `wantlist` and `wantjudge`. |
|
|
827
|
+
| Notify lane | `src/notify-lane.ts` | docket's `Lanes.tickNotify` on two host ticks, every 60 s, one pass per task with work (a due run, a run in flight, a DM owed or claimed), each under that task's lock through a view of the store limited to that task: `notify` runs every type but the page readers, `poll` runs only them (`price`, `wantlist`) with the Fetch port, so a slow page never holds up a reminder. A task the execute tick holds is left for the next notify tick, never waited on. Registers `reminder`, `renewal`, `price`, `research`, `scout`, `news`, `wantlist` and `wantjudge`. |
|
|
826
828
|
| Want-list watcher | `src/want.ts`, `src/inbox.ts`, `src/web/api-listings.ts`; the types and sources are `@rackbops/docket-types`' (0.7.0: `wantlist`, `wantjudge`, `pageSource`, `bggSource`, `inboxSource`) | `/want`'s rules; the `wantlist` type over a `Source` port; the `inbox` source over the `want_inbox` table (schema 8, 0.19.0: the listings sent in per watch, keyed `ebay-` or `bgg-` and 24 random hex digits, the newest 100 kept, as many as one look reads; a watch's rows go when it is deleted or finishes (#208), and forget-me erases them with the person's tasks), filled by the task API's listings route; the `page` source (JSON-LD listings through the fenced Fetch port, no HTML guessing) and the `bgg` source (BGG's XML API with the Bearer token, on `boardgamegeek.com`, never `www.`, at least 5 s between requests (a read due sooner is put back for the next tick, never waited on), read by a bounded `indexOf` parser, never a pattern over the whole body; a 401 or 403 is the token refused, a 202, 429 or 5xx a miss). The BGG fixture is hand-written to BGG's documented shape: BGG declined the application, so there is no token to capture a real response with. The judged watch (`wantjudge`) is an execute-lane type: its `prepare` reads the source in plain code (an uncharged `NoJob` when nothing is new), keeps new listings in the task's state as `pending` and asks for a follow-up, whose `prepare` makes the judge Job; `finish` maps the verdicts back by number, so a Job collected after a restart still has its listings. On this lane a BGG read due too soon waits out the spacing once. |
|
|
827
829
|
| Execute lane | `src/execute-lane.ts`, `src/executor.ts`, `src/research.ts`, `src/research-sources.ts`, `src/web/api-sources.ts`, `src/scout.ts` (the `scout` and `research` types are docket-types'; `research` over `researchType({ readSources })`, 0.8.0, reading a browser request's pages from `research_sources`, and since 0.29.0 a fuller pass's, given only with the web area) | Only when the city-hall Executor is configured: a third host tick, `execute`, that starts docket's `Lanes.tickExecute` in the background (one at a time, at most once per host tick, about a minute) over every task with a due execute-lane run, reserving those tasks and then holding their locks in id order, through one docket `Lanes` kept while the plugin is active (docket keeps its usage-limit pause on it). The Executor is city-hall#18's source pair (above), its I/O injected; `executor_jobs` keeps each Job key's city-hall id. `/research`'s and `/scout`'s rules, and the scout type. |
|
|
828
830
|
| Intake | `src/intake.ts`, `src/intake-store.ts`; the turn is docket-types' (0.14.0: `intakeJob`, `readIntake`, `checkDraft`) | The DM hook, the turn loop on its own poll with its own city-hall Executor (Job ids in memory only), the draft card's buttons and Save through `web/api-tasks.ts`'s create; the conversations table. |
|
|
@@ -839,7 +841,7 @@ $ curl -s https://clerk.example.com/tracker/api/v1/tasks/t9 -H "Authorization: B
|
|
|
839
841
|
| Admin, forget-me | `src/admin.ts`, `src/roster.ts`; `src/web/admin.ts`, `src/web/admin-pages.ts` | The admin acts and forget-me, Discord-free; the SQL docket's Store has no method for (listing people and blocks, erasing a person); the routes and pages. |
|
|
840
842
|
| Web area | `src/web/` | `app.ts` authenticates and dispatches (pure over the injected store and clock), `routes.ts` names the paths; `pages.ts`, `editor-pages.ts` and `html.ts` render; `form-input.ts` reads the editor's fields and `editor.ts` calls the shared rules with them; `signin-link.ts` and `sessions.ts` hold the sign-in; `theme.ts` the stylesheet; `command.ts` is `/web`. |
|
|
841
843
|
| Task API | `src/web/api.ts`, `src/web/api-tasks.ts`, `src/web/api-tokens.ts`, `src/web/tokens.ts`, `src/web/token-pages.ts` | The JSON API's authentication, gates and rate limit; its task routes over editor.ts's writes; the tokens (SQL, hashed); the tokens page and its routes. |
|
|
842
|
-
| Task rules | `src/reminders.ts`, `src/tracked.ts`, `src/price.ts`, `src/research.ts`, `src/scout.ts`, `src/want.ts`, `src/edit.ts`, `src/manage.ts`, `src/limits.ts` | What makes, edits, pauses, resumes and deletes a task, and the limits on it, Discord-free, for the commands and the web editor alike. |
|
|
844
|
+
| Task rules | `src/reminders.ts`, `src/tracked.ts`, `src/price.ts`, `src/research.ts`, `src/scout.ts`, `src/news.ts`, `src/want.ts`, `src/edit.ts`, `src/manage.ts`, `src/limits.ts` | What makes, edits, pauses, resumes and deletes a task, and the limits on it, Discord-free, for the commands and the web editor alike. |
|
|
843
845
|
|
|
844
846
|
## Configuration
|
|
845
847
|
|