commits
demo/client/index.ts now imports parseRecurrenceRule and formatRecurrenceRule
from @newpublic/recurrence (added in the NLP parser feature). Dockerfile.demo
was not copying the packages/ directory, so npm ci failed to resolve the
file: dependency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- packages/recurrence: parseRecurrenceRule() — plain-English → RecurrenceRule
(daily, weekly, monthly, quarterly, yearly; timezone abbrs; end conditions)
- packages/recurrence: formatRecurrenceRule() — RecurrenceRule → plain English
- Demo: NLP input in recurring form; Parse button populates all fields
- Demo: schedule cards use formatRecurrenceRule instead of hand-rolled describeRule
- DISABLE_RECURRING env var: rejects createSchedule at server level and shows
a notice in the demo UI (single deferred posts are unaffected)
- packages/recurrence README; docs/api.md client utilities section
- Dead code cleanup in parser.ts (unreachable yearly_on_month_day branch)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a schedules system that lets clients define recurring posts driven by a recurrence rule (daily, weekly, monthly). When a draft publishes, the scheduler automatically chains to the next occurrence.
Core changes:
- New `schedules` table (id, user_did, collection, record|content_url, recurrence_rule, status, fire_count, next_draft_uri, …)
- `DraftsTable` gains `schedule_id`, `trigger_key_hash`, `trigger_key_encrypted` columns
- 5 new XRPC endpoints: createSchedule, listSchedules, getSchedule, updateSchedule, deleteSchedule
- Scheduler: `handleScheduleChaining` — on publish, increments fire_count, computes next occurrence via @newpublic/recurrence, creates next draft
- Dynamic schedules: fetch `content_url?fireCount=N&scheduledAt=T` at publish time
- pause/resume: cancel pending draft; create new next draft + wake scheduler
- `@newpublic/recurrence` local package (luxon-based) — computeNextOccurrence + getOccurrenceRecord
- New lexicons for all 5 schedule endpoints + updated defs.json
- Full test coverage for storage, scheduler, server, schema
- Updated README and docs/api.md with schedule API reference and lifecycle diagram
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap boot handler body in try/catch so render() always runs, even on unexpected errors
- Add 8s timeout to oauthClient.init() via Promise.race to prevent indefinite hang
- Add 5s AbortSignal timeout to fetch('/api/config')
- Use optional chaining in updateScheduleFormVisibility show() helper to avoid crash on missing elements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
oauthScope is now computed once in getConfig() from allowedCollections.
oauth.ts and routes/oauth.ts both read config.oauthScope instead of
each building the scope string independently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
client-metadata.json and the authorize call both had their own hardcoded
scope strings, independent of the scope built in createOAuthClient.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The demo token is used solely to authenticate the user to ALF — ALF handles
PDS writes with its own OAuth session. No resource permissions needed in the
demo's sign-in token.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces transition:generic with the new ATProto granular scope system.
ALLOWED_COLLECTIONS defaults to * but can be narrowed to specific NSIDs
(e.g. app.bsky.feed.post) to restrict what record types ALF will accept.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
demo/client/index.ts now imports parseRecurrenceRule and formatRecurrenceRule
from @newpublic/recurrence (added in the NLP parser feature). Dockerfile.demo
was not copying the packages/ directory, so npm ci failed to resolve the
file: dependency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- packages/recurrence: parseRecurrenceRule() — plain-English → RecurrenceRule
(daily, weekly, monthly, quarterly, yearly; timezone abbrs; end conditions)
- packages/recurrence: formatRecurrenceRule() — RecurrenceRule → plain English
- Demo: NLP input in recurring form; Parse button populates all fields
- Demo: schedule cards use formatRecurrenceRule instead of hand-rolled describeRule
- DISABLE_RECURRING env var: rejects createSchedule at server level and shows
a notice in the demo UI (single deferred posts are unaffected)
- packages/recurrence README; docs/api.md client utilities section
- Dead code cleanup in parser.ts (unreachable yearly_on_month_day branch)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a schedules system that lets clients define recurring posts driven by a recurrence rule (daily, weekly, monthly). When a draft publishes, the scheduler automatically chains to the next occurrence.
Core changes:
- New `schedules` table (id, user_did, collection, record|content_url, recurrence_rule, status, fire_count, next_draft_uri, …)
- `DraftsTable` gains `schedule_id`, `trigger_key_hash`, `trigger_key_encrypted` columns
- 5 new XRPC endpoints: createSchedule, listSchedules, getSchedule, updateSchedule, deleteSchedule
- Scheduler: `handleScheduleChaining` — on publish, increments fire_count, computes next occurrence via @newpublic/recurrence, creates next draft
- Dynamic schedules: fetch `content_url?fireCount=N&scheduledAt=T` at publish time
- pause/resume: cancel pending draft; create new next draft + wake scheduler
- `@newpublic/recurrence` local package (luxon-based) — computeNextOccurrence + getOccurrenceRecord
- New lexicons for all 5 schedule endpoints + updated defs.json
- Full test coverage for storage, scheduler, server, schema
- Updated README and docs/api.md with schedule API reference and lifecycle diagram
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap boot handler body in try/catch so render() always runs, even on unexpected errors
- Add 8s timeout to oauthClient.init() via Promise.race to prevent indefinite hang
- Add 5s AbortSignal timeout to fetch('/api/config')
- Use optional chaining in updateScheduleFormVisibility show() helper to avoid crash on missing elements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>