autonag 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cli.ts","../apiClient.ts"],"sourcesContent":["import { join } from \"path\";\nimport { homedir } from \"os\";\nimport { mkdirSync, existsSync, readFileSync, writeFileSync } from \"fs\";\nimport JSON5 from \"json5\";\nimport { createApiClient, ApiError } from \"./apiClient\";\n\n// ---- Config ----\n\ntype Config = {\n server: string;\n token?: string;\n deviceId?: string;\n instanceId?: string;\n};\n\nlet CONFIG_PATH = join(homedir(), \".config\", \"autonag\", \"client.json5\");\n\nfunction loadConfig(): Config {\n if (!existsSync(CONFIG_PATH)) return { server: \"http://localhost:3000\" };\n let raw: string;\n try {\n raw = readFileSync(CONFIG_PATH, \"utf8\");\n } catch (e) {\n console.error(`Cannot read config file ${CONFIG_PATH}: ${e}`);\n process.exit(1);\n }\n try {\n return JSON5.parse(raw) as Config;\n } catch (e) {\n console.error(`Config file ${CONFIG_PATH} is not valid JSON5: ${e}`);\n process.exit(1);\n }\n}\n\nfunction saveConfig(config: Config): void {\n mkdirSync(join(CONFIG_PATH, \"..\"), { recursive: true });\n writeFileSync(CONFIG_PATH, JSON5.stringify(config, null, 2));\n}\n\n// ---- Expiry parsing ----\n\nfunction parseExpiry(input: string): number {\n // Explicit unix milliseconds: unix-ms:<n>\n if (input.startsWith(\"unix-ms:\")) {\n const n = Number(input.slice(8));\n if (!Number.isInteger(n) || n <= 0) {\n console.error(`Invalid expiry \"${input}\". Use unix-ms:<positive-integer>`);\n process.exit(1);\n }\n return n;\n }\n // ISO 8601 with required timezone: 2026-05-01T09:00:00Z or 2026-05-01T09:00:00+05:30\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})$/.test(input)) {\n const ms = Date.parse(input);\n if (isNaN(ms)) {\n console.error(`Invalid datetime \"${input}\"`);\n process.exit(1);\n }\n return ms;\n }\n // Duration: 1d, 30m, 2h, 90s, or combinations like 1d12h\n const match = input.match(/^(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?$/);\n if (match && (match[1] || match[2] || match[3] || match[4])) {\n const d = parseInt(match[1] ?? \"0\");\n const h = parseInt(match[2] ?? \"0\");\n const m = parseInt(match[3] ?? \"0\");\n const s = parseInt(match[4] ?? \"0\");\n return Date.now() + (d * 86400 + h * 3600 + m * 60 + s) * 1000;\n }\n console.error(`Cannot parse expiry \"${input}\". Use: 1d, 30m, 2h, 90s (relative duration), 2026-05-01T09:00:00Z (ISO 8601 with timezone), or unix-ms:<n>`);\n process.exit(1);\n}\n\n// ---- Formatting ----\n\nfunction fmtCountdown(ms: number): string {\n const sign = ms < 0 ? \"-\" : \"\";\n const abs = Math.abs(ms);\n const totalSec = Math.floor(abs / 1000);\n const d = Math.floor(totalSec / 86400);\n const h = Math.floor((totalSec % 86400) / 3600);\n const m = Math.floor((totalSec % 3600) / 60);\n const s = totalSec % 60;\n if (d > 0) return `${sign}${d}d ${h}h`;\n if (h > 0) return `${sign}${h}h ${m}m`;\n if (m > 0) return `${sign}${m}m ${s}s`;\n return `${sign}${s}s`;\n}\n\nfunction fmtDate(ms: number): string {\n return new Date(ms).toLocaleString();\n}\n\nfunction shortId(id: string): string {\n return id.slice(0, 8);\n}\n\nfunction col(s: string, w: number): string {\n return s.slice(0, w).padEnd(w);\n}\n\n// ---- ID resolution ----\n\nasync function resolveTimerId(prefix: string, cfg: Config & { token: string; instanceId: string }): Promise<string> {\n if (/^[0-9a-f-]{36}$/.test(prefix)) return prefix;\n const api = createApiClient(cfg.server, cfg.token);\n const state = await api.getState(cfg.instanceId, cfg.token);\n const matches = state.timers.filter(t => t.id.startsWith(prefix));\n if (matches.length === 0) { console.error(`Timer not found: ${prefix}`); process.exit(1); }\n if (matches.length > 1) {\n console.error(`Ambiguous prefix \"${prefix}\" matches: ${matches.map(t => shortId(t.id) + \"…\").join(\", \")}`);\n process.exit(1);\n }\n return matches[0]!.id;\n}\n\nasync function resolveEventId(prefix: string, cfg: Config & { token: string; instanceId: string }): Promise<string> {\n if (/^[0-9a-f-]{36}$/.test(prefix)) return prefix;\n const api = createApiClient(cfg.server, cfg.token);\n // Fetch all events (paginated default covers most cases)\n const allIds: string[] = [];\n let from = 1;\n while (true) {\n const { events, latestSeq } = await api.listEvents(cfg.instanceId, cfg.token, { from, limit: 200 });\n allIds.push(...events.map(e => e.id));\n if (events.length < 200 || events[events.length - 1]!.seq >= latestSeq) break;\n from = events[events.length - 1]!.seq + 1;\n }\n const matches = allIds.filter(id => id.startsWith(prefix));\n if (matches.length === 0) { console.error(`Event not found: ${prefix}`); process.exit(1); }\n if (matches.length > 1) {\n console.error(`Ambiguous prefix \"${prefix}\" matches: ${matches.map(id => shortId(id) + \"…\").join(\", \")}`);\n process.exit(1);\n }\n return matches[0]!;\n}\n\nasync function resolveInstanceId(prefix: string, cfg: Config & { token: string }): Promise<string> {\n if (/^[0-9a-f-]{36}$/.test(prefix)) return prefix;\n const api = createApiClient(cfg.server, cfg.token);\n const { instances } = await api.listInstances(cfg.token);\n const matches = instances.filter(i => i.id.startsWith(prefix));\n if (matches.length === 0) { console.error(`Instance not found: ${prefix}`); process.exit(1); }\n if (matches.length > 1) {\n console.error(`Ambiguous prefix \"${prefix}\" matches: ${matches.map(i => `\"${i.nickname}\" (${shortId(i.id)}…)`).join(\", \")}`);\n process.exit(1);\n }\n return matches[0]!.id;\n}\n\n// ---- Guards ----\n\nfunction requireToken(cfg: Config): asserts cfg is Config & { token: string } {\n if (!cfg.token) {\n console.error(\"Not registered. Run: autonag register <nickname>\");\n process.exit(1);\n }\n}\n\nfunction requireInstance(cfg: Config): asserts cfg is Config & { instanceId: string } {\n if (!cfg.instanceId) {\n console.error(\"No instance selected. Run: autonag instances select <id>\");\n process.exit(1);\n }\n}\n\n// ---- Commands ----\n\nasync function cmdRegister(nickname: string, cfg: Config): Promise<void> {\n const api = createApiClient(cfg.server);\n const { device, token } = await api.registerDevice(nickname);\n cfg.token = token;\n cfg.deviceId = device.id;\n saveConfig(cfg);\n console.log(`Registered as \"${nickname}\" (${shortId(device.id)}…)`);\n console.log(`Token saved to ${CONFIG_PATH}`);\n console.log(`Waiting for admin approval — run \"autonag status\" to check.`);\n}\n\nasync function cmdStatus(cfg: Config): Promise<void> {\n console.log(`Server: ${cfg.server}`);\n console.log(`Config: ${CONFIG_PATH}`);\n if (!cfg.token) {\n console.log(`Auth: not registered (run: autonag register <nickname>)`);\n return;\n }\n const api = createApiClient(cfg.server, cfg.token);\n try {\n const { device: d } = await api.getMe(cfg.token);\n const status = d.approvedAt ? `approved ${fmtDate(d.approvedAt)}` : \"PENDING APPROVAL\";\n console.log(`Auth: ${d.nickname} (${shortId(d.id)}…) [${status}]`);\n } catch (e) {\n if (e instanceof ApiError && e.status === 401) {\n console.log(`Auth: token invalid or expired`);\n } else throw e;\n }\n console.log(`Instance: ${cfg.instanceId ?? \"none selected (run: autonag instances select <id>)\"}`);\n}\n\nasync function cmdInstancesList(cfg: Config): Promise<void> {\n requireToken(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { instances } = await api.listInstances(cfg.token);\n if (instances.length === 0) { console.log(\"No accessible instances.\"); return; }\n for (const i of instances) {\n const status = i.approvedAt ? \"active\" : \"PENDING\";\n const marker = i.id === cfg.instanceId ? \" *\" : \" \";\n console.log(`${marker} ${shortId(i.id)}… \"${i.nickname}\" [${status}]`);\n }\n console.log(`\\n* = current default`);\n}\n\nasync function cmdInstancesRequest(nickname: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { instance } = await api.requestInstance(nickname, cfg.token);\n console.log(`Requested instance \"${instance.nickname}\" (${shortId(instance.id)}…)`);\n console.log(`Waiting for admin approval.`);\n}\n\nasync function cmdInstancesSelect(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n const instanceId = await resolveInstanceId(id, cfg);\n cfg.instanceId = instanceId;\n saveConfig(cfg);\n console.log(`Default instance set to ${instanceId}`);\n}\n\nasync function cmdInstancesJoin(code: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { invite } = await api.getInvite(code);\n if (!invite.valid) {\n console.error(`Invite is not valid: ${invite.reason ?? \"expired or already used\"}`);\n process.exit(1);\n }\n await api.requestInstanceAccess(invite.instanceId, cfg.token, code);\n console.log(`Joined instance \"${invite.instanceNickname}\" (${shortId(invite.instanceId)}…)`);\n console.log(`Run: autonag instances select ${invite.instanceId}`);\n}\n\nasync function cmdTimersList(cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const state = await api.getState(cfg.instanceId, cfg.token);\n\n const now = Date.now();\n const timers = [...state.timers].sort((a, b) => a.currentExpiry - b.currentExpiry);\n\n if (timers.length === 0) {\n console.log(\"No active timers.\");\n } else {\n console.log(`${\"ID\".padEnd(10)} ${\"Name\".padEnd(24)} ${\"Remaining\".padEnd(16)} Recur`);\n console.log(\"-\".repeat(60));\n for (const t of timers) {\n const remaining = t.currentExpiry - now;\n const label = remaining < 0\n ? `OVERDUE ${fmtCountdown(remaining)}`\n : `in ${fmtCountdown(remaining)}`;\n const recur = t.recurrence ? \"↻\" : \"\";\n console.log(`${col(shortId(t.id) + \"…\", 10)} ${col(t.name, 24)} ${col(label, 16)} ${recur}`);\n }\n }\n\n console.log();\n printAlarmStatus(state, now);\n}\n\nfunction printAlarmStatus(state: { alarm: { on: boolean; startsAt: number | null; urgentName: string | null; color: string }; shush: { expiry: number } | null }, now: number): void {\n const { alarm, shush } = state;\n const shushed = !!(shush && shush.expiry > now);\n const colorLabel: Record<string, string> = {\n blue: \"off\", green: \"green\", yellow: \"yellow\", red: \"red\", flashing: \"FLASHING\",\n };\n\n // Line 1: color + most urgent timer\n if (alarm.startsAt !== null) {\n const diff = alarm.startsAt - now;\n const colorStr = colorLabel[alarm.color] ?? alarm.color;\n const timeStr = diff <= 0 ? `overdue ${fmtCountdown(diff)}` : `in ${fmtCountdown(diff)}`;\n console.log(`[${colorStr}] \"${alarm.urgentName}\" — ${timeStr}`);\n }\n\n // Line 2: alarm state\n if (alarm.on) {\n console.log(`Alarm: ringing.`);\n } else if (shushed) {\n const overdue = alarm.startsAt !== null && alarm.startsAt <= now;\n // \"necessary\" = shush is actually blocking a ring (timer overdue now, or timer fires before shush ends)\n const shushEndsAfterTimer = alarm.startsAt !== null && shush!.expiry >= alarm.startsAt;\n const necessary = overdue || shushEndsAfterTimer;\n if (necessary) {\n const ringIn = fmtCountdown(shush!.expiry - now);\n console.log(`Alarm: off. Will ring in ${ringIn}: Shush expires: ${alarm.urgentName}.`);\n } else if (alarm.startsAt !== null) {\n // Shush ends before timer fires — timer still drives the ring time\n const ringIn = fmtCountdown(alarm.startsAt - now);\n const remaining = fmtCountdown(shush!.expiry - now);\n console.log(`Alarm: off. Will ring in ${ringIn}: ${alarm.urgentName}. (Shushed unnecessarily for ${remaining}.)`);\n } else {\n const remaining = fmtCountdown(shush!.expiry - now);\n console.log(`Alarm: off. Shushed (unnecessarily) for ${remaining}.`);\n }\n } else if (alarm.startsAt !== null) {\n const ringIn = fmtCountdown(alarm.startsAt - now);\n console.log(`Alarm: off. Will ring in ${ringIn}: ${alarm.urgentName}.`);\n } else {\n console.log(`Alarm: off.`);\n }\n}\n\nasync function cmdAlarm(cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const state = await api.getState(cfg.instanceId, cfg.token);\n printAlarmStatus(state, Date.now());\n}\n\nasync function cmdTimersAdd(\n name: string, expiryStr: string, cfg: Config,\n opts: { desc?: string; days?: number; anchor?: string; tz?: string; skipWeekends?: boolean }\n): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const expiry = parseExpiry(expiryStr);\n const recurrence = opts.days !== undefined ? {\n periodDays: opts.days,\n anchorTime: opts.anchor ?? \"09:00\",\n timezone: opts.tz ?? Intl.DateTimeFormat().resolvedOptions().timeZone,\n skipWeekends: opts.skipWeekends ?? false,\n } : undefined;\n await api.addTimer(cfg.instanceId, cfg.token, name, expiry, recurrence, opts.desc);\n const recurNote = recurrence ? ` (recurs every ${opts.days}d)` : \"\";\n console.log(`Timer \"${name}\" added, expires ${fmtDate(expiry)}${recurNote}`);\n}\n\nasync function cmdTimersRename(id: string, name: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.renameTimer(cfg.instanceId, cfg.token, timerId, name);\n console.log(`Timer ${shortId(timerId)}… renamed to \"${name}\"`);\n}\n\nasync function cmdTimersSnooze(id: string, expiryStr: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const expiry = parseExpiry(expiryStr);\n await api.updateExpiry(cfg.instanceId, cfg.token, timerId, expiry);\n console.log(`Timer ${shortId(timerId)}… snoozed until ${fmtDate(expiry)}`);\n}\n\nasync function cmdTimersComplete(id: string, reason: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const state = await api.getState(cfg.instanceId, cfg.token);\n const timerId = await resolveTimerId(id, cfg);\n const timer = state.timers.find(t => t.id === timerId);\n\n if (timer?.recurrence) {\n await api.batchActions(cfg.instanceId, cfg.token, [{ type: \"recurTimer\", timerId, closeReason: reason }]);\n const newState = await api.getState(cfg.instanceId, cfg.token);\n const updated = newState.timers.find(t => t.id === timerId);\n if (updated) {\n console.log(`Timer \"${timer.name}\" recurred — next: ${fmtDate(updated.currentExpiry)}`);\n } else {\n console.log(`Timer ${shortId(timerId)}… recurred`);\n }\n } else {\n await api.completeTimer(cfg.instanceId, cfg.token, timerId, reason);\n console.log(`Timer ${shortId(timerId)}… completed (${reason})`);\n }\n}\n\nasync function cmdTimersRemove(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.removeTimer(cfg.instanceId, cfg.token, timerId);\n console.log(`Timer ${shortId(timerId)}… removed`);\n}\n\nasync function cmdTimersDescribe(id: string, text: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.batchActions(cfg.instanceId, cfg.token, [{ type: \"updateDescription\", timerId, description: text }]);\n console.log(`Timer ${shortId(timerId)}… description updated`);\n}\n\nasync function cmdTimersRecurrenceSet(\n id: string, days: number, cfg: Config,\n opts: { anchor?: string; tz?: string; skipWeekends?: boolean }\n): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const recurrence = {\n periodDays: days,\n anchorTime: opts.anchor ?? \"09:00\",\n timezone: opts.tz ?? Intl.DateTimeFormat().resolvedOptions().timeZone,\n skipWeekends: opts.skipWeekends ?? false,\n };\n await api.batchActions(cfg.instanceId, cfg.token, [{ type: \"updateRecurrence\", timerId, recurrence }]);\n console.log(`Timer ${shortId(timerId)}… recurrence set to every ${days}d at ${recurrence.anchorTime} (${recurrence.timezone})`);\n}\n\nasync function cmdTimersRecurrenceRemove(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.batchActions(cfg.instanceId, cfg.token, [{ type: \"removeRecurrence\", timerId }]);\n console.log(`Timer ${shortId(timerId)}… recurrence removed`);\n}\n\nasync function cmdTimersHistory(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const allEntries: Array<{ date: number; closeReason: string }> = [];\n let cursor: number | null = 0;\n while (cursor !== null) {\n const { events, nextCursor } = await api.getTimerHistory(cfg.instanceId, cfg.token, timerId, { after: cursor });\n for (const ev of events) {\n const p = ev.payload as Record<string, unknown>;\n const date = ev.type === \"TimerRecurred\"\n ? (p[\"occurredAt\"] as number) ?? ev.timestamp\n : ev.timestamp;\n allEntries.push({ date, closeReason: p[\"closeReason\"] as string });\n }\n cursor = nextCursor;\n }\n if (allEntries.length === 0) { console.log(\"No history yet.\"); return; }\n console.log(`${\"Date\".padEnd(22)} Reason`);\n console.log(\"-\".repeat(36));\n for (const e of allEntries) {\n console.log(`${col(fmtDate(e.date), 22)} ${e.closeReason}`);\n }\n}\n\nasync function cmdShush(durationStr: string | undefined, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const expiry = durationStr ? parseExpiry(durationStr) : Date.now() + 30 * 60 * 1000;\n await api.setShush(cfg.instanceId, cfg.token, expiry);\n console.log(`Alarm silenced until ${fmtDate(expiry)}`);\n}\n\nasync function cmdUnshush(cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.clearShush(cfg.instanceId, cfg.token);\n console.log(`Shush cleared`);\n}\n\nasync function cmdEventsList(fromSeq: number, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { events } = await api.listEvents(cfg.instanceId, cfg.token, { from: fromSeq });\n if (events.length === 0) { console.log(\"No events.\"); return; }\n console.log(`${\"seq\".padEnd(6)} ${\"ID\".padEnd(10)} ${\"Type\".padEnd(28)} ${\"Time\".padEnd(20)} Skipped`);\n console.log(\"-\".repeat(78));\n for (const e of events) {\n const skipped = e.skipped ? \"yes\" : \"\";\n console.log(`${String(e.seq).padEnd(6)} ${col(shortId(e.id) + \"…\", 10)} ${col(e.type, 28)} ${col(fmtDate(e.timestamp), 20)} ${skipped}`);\n }\n}\n\nasync function cmdEventsSkip(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const eventId = await resolveEventId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.skipEvent(cfg.instanceId, cfg.token, eventId);\n console.log(`Event ${shortId(eventId)}… skipped`);\n}\n\nasync function cmdEventsUnskip(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const eventId = await resolveEventId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.unskipEvent(cfg.instanceId, cfg.token, eventId);\n console.log(`Event ${shortId(eventId)}… unskipped`);\n}\n\nasync function cmdCommentsList(id: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { comments } = await api.listComments(cfg.instanceId, cfg.token, timerId);\n if (comments.length === 0) { console.log(\"No comments.\"); return; }\n for (const c of comments) {\n console.log(`[${fmtDate(c.createdAt)}] ${c.text}`);\n }\n}\n\nasync function cmdCommentsAdd(id: string, text: string, cfg: Config): Promise<void> {\n requireToken(cfg);\n requireInstance(cfg);\n const timerId = await resolveTimerId(id, cfg);\n const api = createApiClient(cfg.server, cfg.token);\n await api.addComment(cfg.instanceId, cfg.token, timerId, text);\n console.log(`Comment added`);\n}\n\n// ---- Tutorial ----\n\nfunction cmdTutorial(): void {\n console.log(`# autonag tutorial\n\nautonag is a recurring-task tracker with an alarm. Timers count down; when\nthey expire, your alarm client starts ringing. You mark them done — if the\ntimer is recurring, the next occurrence is automatically scheduled.\n\n## Config\n\nYour config lives at \\`~/.config/autonag/client.json5\\`. It is a JSON5 file\n(JSON with comments and trailing commas), so you can annotate it. After\nregistration it looks something like:\n\n\\`\\`\\`json5\n{\n server: 'https://your-server.example',\n token: '…',\n deviceId: '…',\n instanceId: '…',\n}\n\\`\\`\\`\n\nEdit it directly in a text editor whenever you need to change the server\nURL or switch instances. To use a different config file:\n\n\\`\\`\\`\nautonag --config /path/to/other.json5 timers list\n\\`\\`\\`\n\n## First-time setup\n\nRegister this device — give it a short nickname so the admin knows who you are:\n\n\\`\\`\\`\nautonag register myphone\n\\`\\`\\`\n\nYour auth token and device ID are saved to the config. The device starts in\nPENDING state until an admin approves it. Check your status:\n\n\\`\\`\\`\nautonag status\n\\`\\`\\`\n\n## Getting an instance\n\nTimers live in an instance. Create a new one:\n\n\\`\\`\\`\nautonag instances request home\n\\`\\`\\`\n\nWhen the admin approves it, it shows up in your list:\n\n\\`\\`\\`\nautonag instances list\n\\`\\`\\`\n\nSet it as your default:\n\n\\`\\`\\`\nautonag instances select <id>\n\\`\\`\\`\n\nTo join an instance someone else owns, ask them for an invite code. They\ngenerate one with:\n\n\\`\\`\\`\nautonag instances invite\n\\`\\`\\`\n\nThen you join with the code they share:\n\n\\`\\`\\`\nautonag instances join <code>\n\\`\\`\\`\n\nInvite codes are single-use and expire after 7 days. The instance is not\ndiscoverable by name — you need the code.\n\n## Adding timers\n\nOne-shot reminder due in 2 days:\n\n\\`\\`\\`\nautonag timers add \"Doctor follow-up\" 2d\n\\`\\`\\`\n\nRecurring task every 7 days at 9 AM:\n\n\\`\\`\\`\nautonag timers add \"Weekly review\" 7d --days 7 --anchor 09:00\n\\`\\`\\`\n\nThe first argument after the name is the initial expiry. \\`--days\\` makes it\nrecurring; \\`--anchor\\` sets the time of day; \\`--tz\\` overrides the timezone.\n\n## Checking timers\n\n\\`\\`\\`\nautonag timers list\n\\`\\`\\`\n\n\\`\\`\\`\nID Name Remaining Recur\n------------------------------------------------------------\na1b2c3d4… Doctor follow-up in 1d 23h\ne5f6a7b8… Weekly review in 6d 23h ↻\n\\`\\`\\`\n\nThe ↻ symbol marks recurring timers.\n\n## Completing a timer\n\nMark a timer done with a reason (pass, fail, or any custom string):\n\n\\`\\`\\`\nautonag timers complete e5f6 pass\n\\`\\`\\`\n\nFor a one-shot timer this removes it. For a recurring timer it schedules\nthe next occurrence and tells you when:\n\n\\`\\`\\`\nTimer \"Weekly review\" recurred — next: 4/24/2026, 9:00:00 AM\n\\`\\`\\`\n\nTo permanently stop a recurring timer, remove the recurrence first:\n\n\\`\\`\\`\nautonag timers recurrence remove e5f6\nautonag timers complete e5f6 done\n\\`\\`\\`\n\n## Snoozing\n\nPush a timer's deadline out:\n\n\\`\\`\\`\nautonag timers snooze e5f6 3d\n\\`\\`\\`\n\n## Silencing the alarm\n\nSilence the alarm for 30 minutes (the default):\n\n\\`\\`\\`\nautonag shush\n\\`\\`\\`\n\nOr for a specific duration:\n\n\\`\\`\\`\nautonag shush 2h\n\\`\\`\\`\n\nResume:\n\n\\`\\`\\`\nautonag unshush\n\\`\\`\\`\n\n## History\n\nSee every time a recurring timer was completed:\n\n\\`\\`\\`\nautonag timers history e5f6\n\\`\\`\\`\n\n## Notes and comments\n\nAttach a description to a timer:\n\n\\`\\`\\`\nautonag timers describe e5f6 \"Prep the agenda before this\"\n\\`\\`\\`\n\nAdd timestamped comments (good for logging what happened):\n\n\\`\\`\\`\nautonag comments add e5f6 \"Completed but flagged the follow-up\"\n\\`\\`\\`\n\n## Event log\n\nEvery action is recorded. View the log:\n\n\\`\\`\\`\nautonag events list\n\\`\\`\\`\n\nYou can skip events to undo them — state is recomputed as if the\nskipped event never happened:\n\n\\`\\`\\`\nautonag events skip <event-id>\nautonag events unskip <event-id>\n\\`\\`\\`\n\n---\nRun \\`autonag --help\\` for a full command reference.`);\n}\n\n// ---- Help ----\n\nconst HELP = `autonag client CLI\n\nUsage: autonag [options] <command> [args]\n\nOptions:\n -h, --help Show this help\n -s, --server <url> Server URL (default: http://localhost:3000)\n -i, --instance <id> Instance ID (overrides saved default)\n -c, --config <path> Config file (default: ~/.config/autonag/client.json5)\n\n tutorial Walk through setup and daily workflow\n\nAuth:\n register <nickname> Register this device and save token\n status Show auth status and saved config\n\nInstances:\n instances list\n instances request <nickname> Create a new instance\n instances join <code> Join an existing instance via invite code\n instances invite Create an invite code for this instance\n instances select <id>\n\nTimers:\n timers list\n timers add <name> <expiry> [--desc <text>] [--days <n>] [--anchor <HH:MM>] [--tz <tz>] [--skip-weekends]\n timers rename <id> <name>\n timers snooze <id> <expiry> Update expiry: 1d, 30m, 2h, 90s | 2026-05-01T09:00:00Z | unix-ms:<n>\n timers complete <id> <reason> Complete or recur (reason: pass, fail, or custom)\n timers remove <id>\n timers describe <id> <text> Set description\n timers recurrence set <id> <days> [--anchor <HH:MM>] [--tz <tz>] [--skip-weekends]\n timers recurrence remove <id>\n timers history <id> Show completion history\n\nAlarm:\n alarm show Show current alarm state\n\nShush:\n shush [duration] Silence alarm (default: 30m)\n unshush Clear shush\n\nEvents:\n events list [--from <seq>]\n events skip <id>\n events unskip <id>\n\nComments:\n comments list <timerId>\n comments add <timerId> <text>\n\nConfig saved to ~/.config/autonag/client.json5`;\n\n// ---- Flag parsing ----\n\ntype FlagSchema = Record<string, \"string\" | \"int\" | \"boolean\">;\ntype ParsedFlags = { positional: string[]; flags: Record<string, string | number | boolean> };\n\nfunction parseFlags(args: string[], schema: FlagSchema): ParsedFlags {\n const positional: string[] = [];\n const flags: Record<string, string | number | boolean> = {};\n for (let i = 0; i < args.length; i++) {\n const a = args[i]!;\n if (a.startsWith(\"--\")) {\n const key = a.slice(2);\n if (!(key in schema)) {\n console.error(`Unknown flag: --${key}`);\n process.exit(1);\n }\n const type = schema[key]!;\n if (type === \"boolean\") {\n flags[key] = true;\n } else {\n const next = args[i + 1];\n if (!next || next.startsWith(\"--\")) {\n console.error(`Flag --${key} requires a value`);\n process.exit(1);\n }\n if (type === \"int\") {\n const n = Number(next);\n if (!Number.isInteger(n)) {\n console.error(`Flag --${key} must be an integer, got: \"${next}\"`);\n process.exit(1);\n }\n flags[key] = n;\n } else {\n flags[key] = next;\n }\n i++;\n }\n } else {\n positional.push(a);\n }\n }\n return { positional, flags };\n}\n\n// ---- Arg parsing & dispatch ----\n\nconst rawArgs = process.argv.slice(2);\nconst topArgs: string[] = [];\nlet serverOverride: string | undefined;\nlet instanceOverride: string | undefined;\n\nfor (let i = 0; i < rawArgs.length; i++) {\n const a = rawArgs[i]!;\n if (a === \"-h\" || a === \"--help\") { console.log(HELP); process.exit(0); }\n else if (a === \"-s\" || a === \"--server\") {\n if (!rawArgs[i + 1] || rawArgs[i + 1]!.startsWith(\"-\")) { console.error(\"Flag -s/--server requires a URL\"); process.exit(1); }\n serverOverride = rawArgs[++i];\n }\n else if (a === \"-i\" || a === \"--instance\") {\n if (!rawArgs[i + 1] || rawArgs[i + 1]!.startsWith(\"-\")) { console.error(\"Flag -i/--instance requires an ID\"); process.exit(1); }\n instanceOverride = rawArgs[++i];\n }\n else if (a === \"-c\" || a === \"--config\") {\n if (!rawArgs[i + 1] || rawArgs[i + 1]!.startsWith(\"-\")) { console.error(\"Flag -c/--config requires a path\"); process.exit(1); }\n CONFIG_PATH = rawArgs[++i]!;\n }\n else topArgs.push(a);\n}\n\nconst cfg = loadConfig();\nif (serverOverride) cfg.server = serverOverride;\nif (instanceOverride) cfg.instanceId = instanceOverride;\n\nconst [cmd, sub, ...rest] = topArgs;\n\ntry {\n if (!cmd || cmd === \"help\") {\n parseFlags(rest, {});\n console.log(HELP);\n } else if (cmd === \"tutorial\") {\n parseFlags(rest, {});\n cmdTutorial();\n\n } else if (cmd === \"register\") {\n parseFlags(rest, {});\n if (!sub) { console.error(\"Usage: autonag register <nickname>\"); process.exit(1); }\n await cmdRegister(sub, cfg);\n\n } else if (cmd === \"status\") {\n parseFlags(rest, {});\n await cmdStatus(cfg);\n\n } else if (cmd === \"instances\") {\n if (!sub || sub === \"list\") {\n parseFlags(rest, {});\n await cmdInstancesList(cfg);\n } else if (sub === \"request\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag instances request <nickname>\"); process.exit(1); }\n await cmdInstancesRequest(positional[0], cfg);\n } else if (sub === \"join\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag instances join <code>\"); process.exit(1); }\n await cmdInstancesJoin(positional[0], cfg);\n } else if (sub === \"invite\") {\n parseFlags(rest, {});\n requireToken(cfg);\n requireInstance(cfg);\n const api = createApiClient(cfg.server, cfg.token);\n const { invite } = await api.createInvite(cfg.instanceId, cfg.token);\n console.log(`Invite code: ${invite.code}`);\n console.log(`Expires: ${fmtDate(invite.expiresAt)}`);\n console.log(`Share this code — it can only be used once.`);\n } else if (sub === \"select\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag instances select <id>\"); process.exit(1); }\n await cmdInstancesSelect(positional[0], cfg);\n } else {\n console.error(`Unknown subcommand: instances ${sub}`); process.exit(1);\n }\n\n } else if (cmd === \"timers\") {\n if (!sub || sub === \"list\") {\n parseFlags(rest, {});\n await cmdTimersList(cfg);\n } else if (sub === \"add\") {\n const { positional, flags } = parseFlags(rest, {\n desc: \"string\", days: \"int\", anchor: \"string\", tz: \"string\", \"skip-weekends\": \"boolean\",\n });\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag timers add <name> <expiry> [--desc <text>] [--days <n>] [--anchor <HH:MM>] [--tz <tz>] [--skip-weekends]\"); process.exit(1); }\n await cmdTimersAdd(positional[0], positional[1], cfg, {\n desc: flags.desc as string | undefined,\n days: flags.days as number | undefined,\n anchor: flags.anchor as string | undefined,\n tz: flags.tz as string | undefined,\n skipWeekends: flags[\"skip-weekends\"] === true,\n });\n } else if (sub === \"rename\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag timers rename <id> <name>\"); process.exit(1); }\n await cmdTimersRename(positional[0], positional[1], cfg);\n } else if (sub === \"snooze\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag timers snooze <id> <expiry>\"); process.exit(1); }\n await cmdTimersSnooze(positional[0], positional[1], cfg);\n } else if (sub === \"complete\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag timers complete <id> <reason>\"); process.exit(1); }\n await cmdTimersComplete(positional[0], positional[1], cfg);\n } else if (sub === \"remove\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag timers remove <id>\"); process.exit(1); }\n await cmdTimersRemove(positional[0], cfg);\n } else if (sub === \"describe\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag timers describe <id> <text>\"); process.exit(1); }\n await cmdTimersDescribe(positional[0], positional[1], cfg);\n } else if (sub === \"recurrence\") {\n const recSub = rest[0];\n if (recSub === \"set\") {\n const { positional, flags } = parseFlags(rest, { anchor: \"string\", tz: \"string\", \"skip-weekends\": \"boolean\" });\n // positional = [\"set\", id, days]\n if (!positional[1] || !positional[2]) { console.error(\"Usage: autonag timers recurrence set <id> <days> [--anchor <HH:MM>] [--tz <tz>] [--skip-weekends]\"); process.exit(1); }\n const days = Number(positional[2]);\n if (!Number.isInteger(days) || days <= 0) { console.error(`<days> must be a positive integer, got: \"${positional[2]}\"`); process.exit(1); }\n await cmdTimersRecurrenceSet(positional[1], days, cfg, {\n anchor: flags.anchor as string | undefined,\n tz: flags.tz as string | undefined,\n skipWeekends: flags[\"skip-weekends\"] === true,\n });\n } else if (recSub === \"remove\") {\n const { positional } = parseFlags(rest, {});\n // positional = [\"remove\", id]\n if (!positional[1]) { console.error(\"Usage: autonag timers recurrence remove <id>\"); process.exit(1); }\n await cmdTimersRecurrenceRemove(positional[1], cfg);\n } else {\n console.error(\"Usage: autonag timers recurrence set|remove ...\"); process.exit(1);\n }\n } else if (sub === \"history\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag timers history <id>\"); process.exit(1); }\n await cmdTimersHistory(positional[0], cfg);\n } else {\n console.error(`Unknown subcommand: timers ${sub}`); process.exit(1);\n }\n\n } else if (cmd === \"alarm\") {\n if (sub !== \"show\") { console.error(\"Usage: autonag alarm show\"); process.exit(1); }\n parseFlags(rest, {});\n await cmdAlarm(cfg);\n\n } else if (cmd === \"shush\") {\n parseFlags(rest, {});\n await cmdShush(sub, cfg);\n } else if (cmd === \"unshush\") {\n parseFlags(rest, {});\n await cmdUnshush(cfg);\n\n } else if (cmd === \"events\") {\n if (!sub || sub === \"list\") {\n const { flags } = parseFlags(rest, { from: \"int\" });\n await cmdEventsList((flags.from as number | undefined) ?? 1, cfg);\n } else if (sub === \"skip\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag events skip <id>\"); process.exit(1); }\n await cmdEventsSkip(positional[0], cfg);\n } else if (sub === \"unskip\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag events unskip <id>\"); process.exit(1); }\n await cmdEventsUnskip(positional[0], cfg);\n } else {\n console.error(`Unknown subcommand: events ${sub}`); process.exit(1);\n }\n\n } else if (cmd === \"comments\") {\n if (!sub || sub === \"list\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0]) { console.error(\"Usage: autonag comments list <timerId>\"); process.exit(1); }\n await cmdCommentsList(positional[0], cfg);\n } else if (sub === \"add\") {\n const { positional } = parseFlags(rest, {});\n if (!positional[0] || !positional[1]) { console.error(\"Usage: autonag comments add <timerId> <text>\"); process.exit(1); }\n await cmdCommentsAdd(positional[0], positional[1], cfg);\n } else {\n console.error(`Unknown subcommand: comments ${sub}`); process.exit(1);\n }\n\n } else {\n console.error(`Unknown command: ${cmd}`);\n console.error(`Run \"autonag --help\" for usage.`);\n process.exit(1);\n }\n} catch (e) {\n if (e instanceof ApiError) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n throw e;\n}\n","import type { RecurrenceSpec, StoredEvent, Comment, InstanceState } from \"./types\";\n\nexport class ApiError extends Error {\n constructor(public status: number, message: string) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type BatchActionItem =\n | { type: \"addTimer\"; name: string; description?: string; expiry: number; recurrence?: RecurrenceSpec }\n | { type: \"updateExpiry\"; timerId: string; expiry: number }\n | { type: \"completeTimer\"; timerId: string; closeReason: string }\n | { type: \"removeTimer\"; timerId: string }\n | { type: \"shush\"; expiry: number }\n | { type: \"clearShush\" }\n | { type: \"recurTimer\"; timerId: string; closeReason: string }\n | { type: \"updateRecurrence\"; timerId: string; recurrence: RecurrenceSpec }\n | { type: \"removeRecurrence\"; timerId: string }\n | { type: \"updateDescription\"; timerId: string; description: string }\n | { type: \"assert\"; path: string; expectStatus?: number; expect?: unknown };\n\nexport function createApiClient(baseUrl: string, defaultToken?: string) {\n async function apiFetch<T>(\n path: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const tok = token ?? defaultToken;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...(options.headers as Record<string, string>),\n };\n if (tok) headers[\"Authorization\"] = `Bearer ${tok}`;\n\n const res = await fetch(`${baseUrl}${path}`, { ...options, headers });\n if (!res.ok) {\n let message = `HTTP ${res.status}`;\n try {\n const body = await res.json() as { error?: string };\n if (body.error) message = body.error;\n } catch { /* ignore */ }\n throw new ApiError(res.status, message);\n }\n return res.json() as Promise<T>;\n }\n\n return {\n registerDevice: (nickname: string) =>\n apiFetch<{ device: { id: string; nickname: string }; token: string }>(\n \"/devices/request\", { method: \"POST\", body: JSON.stringify({ nickname }) }\n ),\n\n getMe: (token: string) =>\n apiFetch<{ device: { id: string; nickname: string; approvedAt: number | null } }>(\n \"/devices/me\", {}, token\n ),\n\n requestInstance: (nickname: string, token: string) =>\n apiFetch<{ instance: { id: string; nickname: string } }>(\n \"/instances/request\", { method: \"POST\", body: JSON.stringify({ nickname }) }, token\n ),\n\n requestInstanceAccess: (instanceId: string, token: string, inviteCode?: string) =>\n apiFetch<{ request?: { id: string; status: string }; status?: string }>(\n `/instances/${instanceId}/access/request`,\n { method: \"POST\", body: JSON.stringify(inviteCode ? { inviteCode } : {}) },\n token\n ),\n\n getInvite: (code: string) =>\n apiFetch<{ invite: { code: string; instanceId: string; instanceNickname: string; expiresAt: number; valid: boolean; reason?: string } }>(\n `/invites/${code}`\n ),\n\n createInvite: (instanceId: string, token: string) =>\n apiFetch<{ invite: { code: string; expiresAt: number } }>(\n `/instances/${instanceId}/invites`, { method: \"POST\", body: JSON.stringify({}) }, token\n ),\n\n listInstances: (token: string) =>\n apiFetch<{ instances: { id: string; nickname: string; approvedAt: number | null }[] }>(\n \"/instances\", {}, token\n ),\n\n addTimer: (instanceId: string, token: string, name: string, expiry: number, recurrence?: RecurrenceSpec, description?: string) =>\n apiFetch<{ timer: { id: string }; event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers`,\n { method: \"POST\", body: JSON.stringify({ name, expiry, ...(description ? { description } : {}), ...(recurrence ? { recurrence } : {}) }) },\n token\n ),\n\n renameTimer: (instanceId: string, token: string, timerId: string, name: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/name`,\n { method: \"PATCH\", body: JSON.stringify({ name }) },\n token\n ),\n\n updateExpiry: (instanceId: string, token: string, timerId: string, expiry: number) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/expiry`,\n { method: \"PATCH\", body: JSON.stringify({ expiry }) },\n token\n ),\n\n completeTimer: (instanceId: string, token: string, timerId: string, closeReason: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/complete`,\n { method: \"POST\", body: JSON.stringify({ closeReason }) },\n token\n ),\n\n removeTimer: (instanceId: string, token: string, timerId: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}`,\n { method: \"DELETE\" },\n token\n ),\n\n setShush: (instanceId: string, token: string, expiry: number) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/shush`,\n { method: \"POST\", body: JSON.stringify({ expiry }) },\n token\n ),\n\n clearShush: (instanceId: string, token: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/shush`,\n { method: \"DELETE\" },\n token\n ),\n\n listEvents: (instanceId: string, token: string, opts: { from?: number; before?: number; limit?: number } = {}) => {\n const { from = 1, before, limit = 200 } = opts;\n const params = before !== undefined\n ? `before=${before}&limit=${limit}`\n : `from=${from}&limit=${limit}`;\n return apiFetch<{ events: StoredEvent[]; latestSeq: number; prevCursor?: number | null }>(\n `/instances/${instanceId}/events?${params}`, {}, token\n );\n },\n\n getTimerHistory: (instanceId: string, token: string, timerId: string, opts: {\n after?: number; limit?: number; from?: number; to?: number;\n } = {}) => {\n const params = new URLSearchParams();\n if (opts.after !== undefined) params.set(\"after\", String(opts.after));\n if (opts.limit !== undefined) params.set(\"limit\", String(opts.limit));\n if (opts.from !== undefined) params.set(\"from\", String(opts.from));\n if (opts.to !== undefined) params.set(\"to\", String(opts.to));\n const qs = params.toString();\n return apiFetch<{ events: StoredEvent[]; nextCursor: number | null }>(\n `/instances/${instanceId}/timers/${timerId}/history${qs ? `?${qs}` : \"\"}`, {}, token\n );\n },\n\n skipEvent: (instanceId: string, token: string, eventId: string) =>\n apiFetch<{ ok: boolean }>(\n `/instances/${instanceId}/events/${eventId}/skip`, { method: \"POST\" }, token\n ),\n\n unskipEvent: (instanceId: string, token: string, eventId: string) =>\n apiFetch<{ ok: boolean }>(\n `/instances/${instanceId}/events/${eventId}/skip`, { method: \"DELETE\" }, token\n ),\n\n listComments: (instanceId: string, token: string, threadId: string) =>\n apiFetch<{ comments: Comment[] }>(\n `/instances/${instanceId}/threads/${threadId}/comments`, {}, token\n ),\n\n addComment: (instanceId: string, token: string, threadId: string, text: string) =>\n apiFetch<{ comment: Comment }>(\n `/instances/${instanceId}/threads/${threadId}/comments`,\n { method: \"POST\", body: JSON.stringify({ text }) },\n token\n ),\n\n getState: (instanceId: string, token: string) =>\n apiFetch<InstanceState>(\n `/instances/${instanceId}/state`, {}, token\n ),\n\n batchActions: (instanceId: string, token: string, actions: BatchActionItem[]) =>\n apiFetch<{ events: { id: string; seq: number; type: string }[] }>(\n `/instances/${instanceId}/actions`,\n { method: \"POST\", body: JSON.stringify({ actions }) },\n token\n ),\n };\n}\n"],"mappings":"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","names":["cfg"]}
@@ -0,0 +1,265 @@
1
+ type RecurrenceSpec = {
2
+ periodDays: number;
3
+ anchorTime: string;
4
+ timezone: string;
5
+ skipWeekends: boolean;
6
+ };
7
+ type Timer = {
8
+ id: string;
9
+ name: string;
10
+ description?: string;
11
+ created: number;
12
+ currentExpiry: number;
13
+ recurrence?: RecurrenceSpec;
14
+ };
15
+ type Comment = {
16
+ id: string;
17
+ instanceId: string;
18
+ threadId: string;
19
+ text: string;
20
+ deviceId: string | null;
21
+ createdAt: number;
22
+ };
23
+ type Shush = {
24
+ expiry: number;
25
+ } | null;
26
+ type InstanceState = {
27
+ timers: Timer[];
28
+ shush: Shush;
29
+ alarm: {
30
+ on: boolean;
31
+ startsAt: number | null;
32
+ urgentName: string | null;
33
+ color: string;
34
+ };
35
+ };
36
+ type StoredEvent = {
37
+ id: string;
38
+ instanceId: string;
39
+ seq: number;
40
+ type: string;
41
+ payload: Record<string, unknown>;
42
+ timestamp: number;
43
+ skipped: boolean;
44
+ };
45
+ type WsMessage = {
46
+ type: "snapshot";
47
+ seq: number;
48
+ state: InstanceState;
49
+ } | {
50
+ type: "event";
51
+ event: StoredEvent;
52
+ } | {
53
+ type: "skip";
54
+ eventId: string;
55
+ seq: number;
56
+ state: InstanceState;
57
+ } | {
58
+ type: "unskip";
59
+ eventId: string;
60
+ seq: number;
61
+ state: InstanceState;
62
+ } | {
63
+ type: "comment";
64
+ comment: Comment;
65
+ } | {
66
+ type: "error";
67
+ message: string;
68
+ };
69
+ type ConnectionStatus = "connecting" | "connected" | "disconnected";
70
+ type Device = {
71
+ id: string;
72
+ nickname: string;
73
+ approvedAt: number | null;
74
+ };
75
+ type Instance = {
76
+ id: string;
77
+ nickname: string;
78
+ createdAt: number;
79
+ approvedAt: number | null;
80
+ };
81
+
82
+ declare class ApiError extends Error {
83
+ status: number;
84
+ constructor(status: number, message: string);
85
+ }
86
+ type BatchActionItem = {
87
+ type: "addTimer";
88
+ name: string;
89
+ description?: string;
90
+ expiry: number;
91
+ recurrence?: RecurrenceSpec;
92
+ } | {
93
+ type: "updateExpiry";
94
+ timerId: string;
95
+ expiry: number;
96
+ } | {
97
+ type: "completeTimer";
98
+ timerId: string;
99
+ closeReason: string;
100
+ } | {
101
+ type: "removeTimer";
102
+ timerId: string;
103
+ } | {
104
+ type: "shush";
105
+ expiry: number;
106
+ } | {
107
+ type: "clearShush";
108
+ } | {
109
+ type: "recurTimer";
110
+ timerId: string;
111
+ closeReason: string;
112
+ } | {
113
+ type: "updateRecurrence";
114
+ timerId: string;
115
+ recurrence: RecurrenceSpec;
116
+ } | {
117
+ type: "removeRecurrence";
118
+ timerId: string;
119
+ } | {
120
+ type: "updateDescription";
121
+ timerId: string;
122
+ description: string;
123
+ } | {
124
+ type: "assert";
125
+ path: string;
126
+ expectStatus?: number;
127
+ expect?: unknown;
128
+ };
129
+ declare function createApiClient(baseUrl: string, defaultToken?: string): {
130
+ registerDevice: (nickname: string) => Promise<{
131
+ device: {
132
+ id: string;
133
+ nickname: string;
134
+ };
135
+ token: string;
136
+ }>;
137
+ getMe: (token: string) => Promise<{
138
+ device: {
139
+ id: string;
140
+ nickname: string;
141
+ approvedAt: number | null;
142
+ };
143
+ }>;
144
+ requestInstance: (nickname: string, token: string) => Promise<{
145
+ instance: {
146
+ id: string;
147
+ nickname: string;
148
+ };
149
+ }>;
150
+ requestInstanceAccess: (instanceId: string, token: string, inviteCode?: string) => Promise<{
151
+ request?: {
152
+ id: string;
153
+ status: string;
154
+ };
155
+ status?: string;
156
+ }>;
157
+ getInvite: (code: string) => Promise<{
158
+ invite: {
159
+ code: string;
160
+ instanceId: string;
161
+ instanceNickname: string;
162
+ expiresAt: number;
163
+ valid: boolean;
164
+ reason?: string;
165
+ };
166
+ }>;
167
+ createInvite: (instanceId: string, token: string) => Promise<{
168
+ invite: {
169
+ code: string;
170
+ expiresAt: number;
171
+ };
172
+ }>;
173
+ listInstances: (token: string) => Promise<{
174
+ instances: {
175
+ id: string;
176
+ nickname: string;
177
+ approvedAt: number | null;
178
+ }[];
179
+ }>;
180
+ addTimer: (instanceId: string, token: string, name: string, expiry: number, recurrence?: RecurrenceSpec, description?: string) => Promise<{
181
+ timer: {
182
+ id: string;
183
+ };
184
+ event: {
185
+ id: string;
186
+ seq: number;
187
+ };
188
+ }>;
189
+ renameTimer: (instanceId: string, token: string, timerId: string, name: string) => Promise<{
190
+ event: {
191
+ id: string;
192
+ seq: number;
193
+ };
194
+ }>;
195
+ updateExpiry: (instanceId: string, token: string, timerId: string, expiry: number) => Promise<{
196
+ event: {
197
+ id: string;
198
+ seq: number;
199
+ };
200
+ }>;
201
+ completeTimer: (instanceId: string, token: string, timerId: string, closeReason: string) => Promise<{
202
+ event: {
203
+ id: string;
204
+ seq: number;
205
+ };
206
+ }>;
207
+ removeTimer: (instanceId: string, token: string, timerId: string) => Promise<{
208
+ event: {
209
+ id: string;
210
+ seq: number;
211
+ };
212
+ }>;
213
+ setShush: (instanceId: string, token: string, expiry: number) => Promise<{
214
+ event: {
215
+ id: string;
216
+ seq: number;
217
+ };
218
+ }>;
219
+ clearShush: (instanceId: string, token: string) => Promise<{
220
+ event: {
221
+ id: string;
222
+ seq: number;
223
+ };
224
+ }>;
225
+ listEvents: (instanceId: string, token: string, opts?: {
226
+ from?: number;
227
+ before?: number;
228
+ limit?: number;
229
+ }) => Promise<{
230
+ events: StoredEvent[];
231
+ latestSeq: number;
232
+ prevCursor?: number | null;
233
+ }>;
234
+ getTimerHistory: (instanceId: string, token: string, timerId: string, opts?: {
235
+ after?: number;
236
+ limit?: number;
237
+ from?: number;
238
+ to?: number;
239
+ }) => Promise<{
240
+ events: StoredEvent[];
241
+ nextCursor: number | null;
242
+ }>;
243
+ skipEvent: (instanceId: string, token: string, eventId: string) => Promise<{
244
+ ok: boolean;
245
+ }>;
246
+ unskipEvent: (instanceId: string, token: string, eventId: string) => Promise<{
247
+ ok: boolean;
248
+ }>;
249
+ listComments: (instanceId: string, token: string, threadId: string) => Promise<{
250
+ comments: Comment[];
251
+ }>;
252
+ addComment: (instanceId: string, token: string, threadId: string, text: string) => Promise<{
253
+ comment: Comment;
254
+ }>;
255
+ getState: (instanceId: string, token: string) => Promise<InstanceState>;
256
+ batchActions: (instanceId: string, token: string, actions: BatchActionItem[]) => Promise<{
257
+ events: {
258
+ id: string;
259
+ seq: number;
260
+ type: string;
261
+ }[];
262
+ }>;
263
+ };
264
+
265
+ export { ApiError, type BatchActionItem, type Comment, type ConnectionStatus, type Device, type Instance, type InstanceState, type RecurrenceSpec, type Shush, type StoredEvent, type Timer, type WsMessage, createApiClient };
package/dist/index.js ADDED
@@ -0,0 +1,156 @@
1
+ // apiClient.ts
2
+ var ApiError = class extends Error {
3
+ constructor(status, message) {
4
+ super(message);
5
+ this.status = status;
6
+ this.name = "ApiError";
7
+ }
8
+ status;
9
+ };
10
+ function createApiClient(baseUrl, defaultToken) {
11
+ async function apiFetch(path, options = {}, token) {
12
+ const tok = token ?? defaultToken;
13
+ const headers = {
14
+ "Content-Type": "application/json",
15
+ ...options.headers
16
+ };
17
+ if (tok) headers["Authorization"] = `Bearer ${tok}`;
18
+ const res = await fetch(`${baseUrl}${path}`, { ...options, headers });
19
+ if (!res.ok) {
20
+ let message = `HTTP ${res.status}`;
21
+ try {
22
+ const body = await res.json();
23
+ if (body.error) message = body.error;
24
+ } catch {
25
+ }
26
+ throw new ApiError(res.status, message);
27
+ }
28
+ return res.json();
29
+ }
30
+ return {
31
+ registerDevice: (nickname) => apiFetch(
32
+ "/devices/request",
33
+ { method: "POST", body: JSON.stringify({ nickname }) }
34
+ ),
35
+ getMe: (token) => apiFetch(
36
+ "/devices/me",
37
+ {},
38
+ token
39
+ ),
40
+ requestInstance: (nickname, token) => apiFetch(
41
+ "/instances/request",
42
+ { method: "POST", body: JSON.stringify({ nickname }) },
43
+ token
44
+ ),
45
+ requestInstanceAccess: (instanceId, token, inviteCode) => apiFetch(
46
+ `/instances/${instanceId}/access/request`,
47
+ { method: "POST", body: JSON.stringify(inviteCode ? { inviteCode } : {}) },
48
+ token
49
+ ),
50
+ getInvite: (code) => apiFetch(
51
+ `/invites/${code}`
52
+ ),
53
+ createInvite: (instanceId, token) => apiFetch(
54
+ `/instances/${instanceId}/invites`,
55
+ { method: "POST", body: JSON.stringify({}) },
56
+ token
57
+ ),
58
+ listInstances: (token) => apiFetch(
59
+ "/instances",
60
+ {},
61
+ token
62
+ ),
63
+ addTimer: (instanceId, token, name, expiry, recurrence, description) => apiFetch(
64
+ `/instances/${instanceId}/timers`,
65
+ { method: "POST", body: JSON.stringify({ name, expiry, ...description ? { description } : {}, ...recurrence ? { recurrence } : {} }) },
66
+ token
67
+ ),
68
+ renameTimer: (instanceId, token, timerId, name) => apiFetch(
69
+ `/instances/${instanceId}/timers/${timerId}/name`,
70
+ { method: "PATCH", body: JSON.stringify({ name }) },
71
+ token
72
+ ),
73
+ updateExpiry: (instanceId, token, timerId, expiry) => apiFetch(
74
+ `/instances/${instanceId}/timers/${timerId}/expiry`,
75
+ { method: "PATCH", body: JSON.stringify({ expiry }) },
76
+ token
77
+ ),
78
+ completeTimer: (instanceId, token, timerId, closeReason) => apiFetch(
79
+ `/instances/${instanceId}/timers/${timerId}/complete`,
80
+ { method: "POST", body: JSON.stringify({ closeReason }) },
81
+ token
82
+ ),
83
+ removeTimer: (instanceId, token, timerId) => apiFetch(
84
+ `/instances/${instanceId}/timers/${timerId}`,
85
+ { method: "DELETE" },
86
+ token
87
+ ),
88
+ setShush: (instanceId, token, expiry) => apiFetch(
89
+ `/instances/${instanceId}/shush`,
90
+ { method: "POST", body: JSON.stringify({ expiry }) },
91
+ token
92
+ ),
93
+ clearShush: (instanceId, token) => apiFetch(
94
+ `/instances/${instanceId}/shush`,
95
+ { method: "DELETE" },
96
+ token
97
+ ),
98
+ listEvents: (instanceId, token, opts = {}) => {
99
+ const { from = 1, before, limit = 200 } = opts;
100
+ const params = before !== void 0 ? `before=${before}&limit=${limit}` : `from=${from}&limit=${limit}`;
101
+ return apiFetch(
102
+ `/instances/${instanceId}/events?${params}`,
103
+ {},
104
+ token
105
+ );
106
+ },
107
+ getTimerHistory: (instanceId, token, timerId, opts = {}) => {
108
+ const params = new URLSearchParams();
109
+ if (opts.after !== void 0) params.set("after", String(opts.after));
110
+ if (opts.limit !== void 0) params.set("limit", String(opts.limit));
111
+ if (opts.from !== void 0) params.set("from", String(opts.from));
112
+ if (opts.to !== void 0) params.set("to", String(opts.to));
113
+ const qs = params.toString();
114
+ return apiFetch(
115
+ `/instances/${instanceId}/timers/${timerId}/history${qs ? `?${qs}` : ""}`,
116
+ {},
117
+ token
118
+ );
119
+ },
120
+ skipEvent: (instanceId, token, eventId) => apiFetch(
121
+ `/instances/${instanceId}/events/${eventId}/skip`,
122
+ { method: "POST" },
123
+ token
124
+ ),
125
+ unskipEvent: (instanceId, token, eventId) => apiFetch(
126
+ `/instances/${instanceId}/events/${eventId}/skip`,
127
+ { method: "DELETE" },
128
+ token
129
+ ),
130
+ listComments: (instanceId, token, threadId) => apiFetch(
131
+ `/instances/${instanceId}/threads/${threadId}/comments`,
132
+ {},
133
+ token
134
+ ),
135
+ addComment: (instanceId, token, threadId, text) => apiFetch(
136
+ `/instances/${instanceId}/threads/${threadId}/comments`,
137
+ { method: "POST", body: JSON.stringify({ text }) },
138
+ token
139
+ ),
140
+ getState: (instanceId, token) => apiFetch(
141
+ `/instances/${instanceId}/state`,
142
+ {},
143
+ token
144
+ ),
145
+ batchActions: (instanceId, token, actions) => apiFetch(
146
+ `/instances/${instanceId}/actions`,
147
+ { method: "POST", body: JSON.stringify({ actions }) },
148
+ token
149
+ )
150
+ };
151
+ }
152
+ export {
153
+ ApiError,
154
+ createApiClient
155
+ };
156
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../apiClient.ts"],"sourcesContent":["import type { RecurrenceSpec, StoredEvent, Comment, InstanceState } from \"./types\";\n\nexport class ApiError extends Error {\n constructor(public status: number, message: string) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport type BatchActionItem =\n | { type: \"addTimer\"; name: string; description?: string; expiry: number; recurrence?: RecurrenceSpec }\n | { type: \"updateExpiry\"; timerId: string; expiry: number }\n | { type: \"completeTimer\"; timerId: string; closeReason: string }\n | { type: \"removeTimer\"; timerId: string }\n | { type: \"shush\"; expiry: number }\n | { type: \"clearShush\" }\n | { type: \"recurTimer\"; timerId: string; closeReason: string }\n | { type: \"updateRecurrence\"; timerId: string; recurrence: RecurrenceSpec }\n | { type: \"removeRecurrence\"; timerId: string }\n | { type: \"updateDescription\"; timerId: string; description: string }\n | { type: \"assert\"; path: string; expectStatus?: number; expect?: unknown };\n\nexport function createApiClient(baseUrl: string, defaultToken?: string) {\n async function apiFetch<T>(\n path: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const tok = token ?? defaultToken;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...(options.headers as Record<string, string>),\n };\n if (tok) headers[\"Authorization\"] = `Bearer ${tok}`;\n\n const res = await fetch(`${baseUrl}${path}`, { ...options, headers });\n if (!res.ok) {\n let message = `HTTP ${res.status}`;\n try {\n const body = await res.json() as { error?: string };\n if (body.error) message = body.error;\n } catch { /* ignore */ }\n throw new ApiError(res.status, message);\n }\n return res.json() as Promise<T>;\n }\n\n return {\n registerDevice: (nickname: string) =>\n apiFetch<{ device: { id: string; nickname: string }; token: string }>(\n \"/devices/request\", { method: \"POST\", body: JSON.stringify({ nickname }) }\n ),\n\n getMe: (token: string) =>\n apiFetch<{ device: { id: string; nickname: string; approvedAt: number | null } }>(\n \"/devices/me\", {}, token\n ),\n\n requestInstance: (nickname: string, token: string) =>\n apiFetch<{ instance: { id: string; nickname: string } }>(\n \"/instances/request\", { method: \"POST\", body: JSON.stringify({ nickname }) }, token\n ),\n\n requestInstanceAccess: (instanceId: string, token: string, inviteCode?: string) =>\n apiFetch<{ request?: { id: string; status: string }; status?: string }>(\n `/instances/${instanceId}/access/request`,\n { method: \"POST\", body: JSON.stringify(inviteCode ? { inviteCode } : {}) },\n token\n ),\n\n getInvite: (code: string) =>\n apiFetch<{ invite: { code: string; instanceId: string; instanceNickname: string; expiresAt: number; valid: boolean; reason?: string } }>(\n `/invites/${code}`\n ),\n\n createInvite: (instanceId: string, token: string) =>\n apiFetch<{ invite: { code: string; expiresAt: number } }>(\n `/instances/${instanceId}/invites`, { method: \"POST\", body: JSON.stringify({}) }, token\n ),\n\n listInstances: (token: string) =>\n apiFetch<{ instances: { id: string; nickname: string; approvedAt: number | null }[] }>(\n \"/instances\", {}, token\n ),\n\n addTimer: (instanceId: string, token: string, name: string, expiry: number, recurrence?: RecurrenceSpec, description?: string) =>\n apiFetch<{ timer: { id: string }; event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers`,\n { method: \"POST\", body: JSON.stringify({ name, expiry, ...(description ? { description } : {}), ...(recurrence ? { recurrence } : {}) }) },\n token\n ),\n\n renameTimer: (instanceId: string, token: string, timerId: string, name: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/name`,\n { method: \"PATCH\", body: JSON.stringify({ name }) },\n token\n ),\n\n updateExpiry: (instanceId: string, token: string, timerId: string, expiry: number) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/expiry`,\n { method: \"PATCH\", body: JSON.stringify({ expiry }) },\n token\n ),\n\n completeTimer: (instanceId: string, token: string, timerId: string, closeReason: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}/complete`,\n { method: \"POST\", body: JSON.stringify({ closeReason }) },\n token\n ),\n\n removeTimer: (instanceId: string, token: string, timerId: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/timers/${timerId}`,\n { method: \"DELETE\" },\n token\n ),\n\n setShush: (instanceId: string, token: string, expiry: number) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/shush`,\n { method: \"POST\", body: JSON.stringify({ expiry }) },\n token\n ),\n\n clearShush: (instanceId: string, token: string) =>\n apiFetch<{ event: { id: string; seq: number } }>(\n `/instances/${instanceId}/shush`,\n { method: \"DELETE\" },\n token\n ),\n\n listEvents: (instanceId: string, token: string, opts: { from?: number; before?: number; limit?: number } = {}) => {\n const { from = 1, before, limit = 200 } = opts;\n const params = before !== undefined\n ? `before=${before}&limit=${limit}`\n : `from=${from}&limit=${limit}`;\n return apiFetch<{ events: StoredEvent[]; latestSeq: number; prevCursor?: number | null }>(\n `/instances/${instanceId}/events?${params}`, {}, token\n );\n },\n\n getTimerHistory: (instanceId: string, token: string, timerId: string, opts: {\n after?: number; limit?: number; from?: number; to?: number;\n } = {}) => {\n const params = new URLSearchParams();\n if (opts.after !== undefined) params.set(\"after\", String(opts.after));\n if (opts.limit !== undefined) params.set(\"limit\", String(opts.limit));\n if (opts.from !== undefined) params.set(\"from\", String(opts.from));\n if (opts.to !== undefined) params.set(\"to\", String(opts.to));\n const qs = params.toString();\n return apiFetch<{ events: StoredEvent[]; nextCursor: number | null }>(\n `/instances/${instanceId}/timers/${timerId}/history${qs ? `?${qs}` : \"\"}`, {}, token\n );\n },\n\n skipEvent: (instanceId: string, token: string, eventId: string) =>\n apiFetch<{ ok: boolean }>(\n `/instances/${instanceId}/events/${eventId}/skip`, { method: \"POST\" }, token\n ),\n\n unskipEvent: (instanceId: string, token: string, eventId: string) =>\n apiFetch<{ ok: boolean }>(\n `/instances/${instanceId}/events/${eventId}/skip`, { method: \"DELETE\" }, token\n ),\n\n listComments: (instanceId: string, token: string, threadId: string) =>\n apiFetch<{ comments: Comment[] }>(\n `/instances/${instanceId}/threads/${threadId}/comments`, {}, token\n ),\n\n addComment: (instanceId: string, token: string, threadId: string, text: string) =>\n apiFetch<{ comment: Comment }>(\n `/instances/${instanceId}/threads/${threadId}/comments`,\n { method: \"POST\", body: JSON.stringify({ text }) },\n token\n ),\n\n getState: (instanceId: string, token: string) =>\n apiFetch<InstanceState>(\n `/instances/${instanceId}/state`, {}, token\n ),\n\n batchActions: (instanceId: string, token: string, actions: BatchActionItem[]) =>\n apiFetch<{ events: { id: string; seq: number; type: string }[] }>(\n `/instances/${instanceId}/actions`,\n { method: \"POST\", body: JSON.stringify({ actions }) },\n token\n ),\n };\n}\n"],"mappings":"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","names":[]}