@supermagicapps/remind-me 1.0.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/dist/api.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare function parseReminder(text: string): Promise<any>;
2
+ export declare function createReminder(parsed: any): Promise<any>;
3
+ export declare function listReminders(): Promise<any>;
4
+ export declare function deleteReminder(id: string): Promise<any>;
5
+ export declare function scheduleMessage(to: string, body: string, scheduledFor: string): Promise<any>;
6
+ export declare function listMessages(): Promise<any>;
7
+ export declare function getSettings(): Promise<any>;
8
+ export declare function updateSettings(settings: Record<string, any>): Promise<any>;
package/dist/api.js ADDED
@@ -0,0 +1,54 @@
1
+ import { requireConfig } from './config.js';
2
+ async function request(path, options = {}) {
3
+ const config = requireConfig();
4
+ const url = `${config.apiUrl}${path}`;
5
+ const res = await fetch(url, {
6
+ ...options,
7
+ headers: {
8
+ 'Content-Type': 'application/json',
9
+ 'Authorization': `Bearer ${config.apiKey}`,
10
+ ...options.headers,
11
+ },
12
+ });
13
+ if (!res.ok) {
14
+ const body = await res.json().catch(() => ({}));
15
+ throw new Error(body.error || `Request failed: ${res.status}`);
16
+ }
17
+ return res.json();
18
+ }
19
+ export async function parseReminder(text) {
20
+ return request('/api/reminders/parse', {
21
+ method: 'POST',
22
+ body: JSON.stringify({ text }),
23
+ });
24
+ }
25
+ export async function createReminder(parsed) {
26
+ return request('/api/reminders', {
27
+ method: 'POST',
28
+ body: JSON.stringify(parsed),
29
+ });
30
+ }
31
+ export async function listReminders() {
32
+ return request('/api/reminders');
33
+ }
34
+ export async function deleteReminder(id) {
35
+ return request(`/api/reminders/${id}`, { method: 'DELETE' });
36
+ }
37
+ export async function scheduleMessage(to, body, scheduledFor) {
38
+ return request('/v1/messages/schedule', {
39
+ method: 'POST',
40
+ body: JSON.stringify({ to, body, scheduledFor }),
41
+ });
42
+ }
43
+ export async function listMessages() {
44
+ return request('/v1/messages');
45
+ }
46
+ export async function getSettings() {
47
+ return request('/api/settings');
48
+ }
49
+ export async function updateSettings(settings) {
50
+ return request('/api/settings', {
51
+ method: 'PUT',
52
+ body: JSON.stringify(settings),
53
+ });
54
+ }
@@ -0,0 +1,8 @@
1
+ interface Config {
2
+ apiKey: string;
3
+ apiUrl: string;
4
+ }
5
+ export declare function getConfig(): Config | null;
6
+ export declare function saveConfig(config: Config): void;
7
+ export declare function requireConfig(): Config;
8
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,29 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ const CONFIG_DIR = join(homedir(), '.remind-me');
5
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
+ export function getConfig() {
7
+ if (!existsSync(CONFIG_FILE))
8
+ return null;
9
+ try {
10
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function saveConfig(config) {
17
+ if (!existsSync(CONFIG_DIR)) {
18
+ mkdirSync(CONFIG_DIR, { recursive: true });
19
+ }
20
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
21
+ }
22
+ export function requireConfig() {
23
+ const config = getConfig();
24
+ if (!config) {
25
+ console.error('Not configured. Run: remind-me init');
26
+ process.exit(1);
27
+ }
28
+ return config;
29
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { createInterface } from 'readline';
4
+ import { getConfig, saveConfig } from './config.js';
5
+ import * as api from './api.js';
6
+ function prompt(question) {
7
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
8
+ return new Promise(resolve => {
9
+ rl.question(question, answer => {
10
+ rl.close();
11
+ resolve(answer.trim());
12
+ });
13
+ });
14
+ }
15
+ program
16
+ .name('remind-me')
17
+ .description('CLI for the Remind Me reminders platform')
18
+ .version('1.0.0');
19
+ // ── Init ──
20
+ program
21
+ .command('init')
22
+ .description('Configure API key and server URL')
23
+ .action(async () => {
24
+ const existing = getConfig();
25
+ console.log('\n Remind Me CLI Setup\n');
26
+ if (existing) {
27
+ console.log(` Current API URL: ${existing.apiUrl}`);
28
+ console.log(` Current API key: ${existing.apiKey.slice(0, 12)}...`);
29
+ console.log('');
30
+ }
31
+ const apiUrl = await prompt(` API URL [${existing?.apiUrl || 'https://remind.supermagicapps.com'}]: `);
32
+ const apiKey = await prompt(' API Key (rmk_...): ');
33
+ if (!apiKey && !existing?.apiKey) {
34
+ console.error('\n API key is required. Generate one at your dashboard under Settings.');
35
+ process.exit(1);
36
+ }
37
+ saveConfig({
38
+ apiUrl: apiUrl || existing?.apiUrl || 'https://remind.supermagicapps.com',
39
+ apiKey: apiKey || existing.apiKey,
40
+ });
41
+ console.log('\n Config saved to ~/.remind-me/config.json');
42
+ console.log(' Run `remind-me list` to verify.\n');
43
+ });
44
+ // ── Create Reminder ──
45
+ program
46
+ .command('create <text...>')
47
+ .description('Create a reminder from natural language')
48
+ .action(async (textParts) => {
49
+ const text = textParts.join(' ');
50
+ try {
51
+ const parsed = await api.parseReminder(text);
52
+ if (parsed.intent !== 'create_reminder' || !parsed.parsed) {
53
+ console.error('Could not parse as a reminder. Try something like "call mom tomorrow at 3pm"');
54
+ process.exit(1);
55
+ }
56
+ const p = parsed.parsed;
57
+ console.log(`\n Title: ${p.title}`);
58
+ console.log(` Type: ${p.isRecurring ? 'Recurring' : 'One-time'}`);
59
+ if (p.fireTime)
60
+ console.log(` Time: ${p.fireTime}`);
61
+ if (p.rruleFreq)
62
+ console.log(` Schedule: ${p.rruleFreq}${p.rruleByDay?.length ? ' (' + p.rruleByDay.join(', ') + ')' : ''}`);
63
+ console.log('');
64
+ const confirm = await prompt(' Create this reminder? [Y/n] ');
65
+ if (confirm.toLowerCase() === 'n') {
66
+ console.log(' Cancelled.');
67
+ return;
68
+ }
69
+ const result = await api.createReminder(p);
70
+ console.log(` Created: ${result.reminder.title}`);
71
+ if (result.reminder.nextFireAt) {
72
+ console.log(` Next: ${new Date(result.reminder.nextFireAt).toLocaleString()}`);
73
+ }
74
+ console.log('');
75
+ }
76
+ catch (err) {
77
+ console.error(` Error: ${err.message}`);
78
+ process.exit(1);
79
+ }
80
+ });
81
+ // ── List Reminders ──
82
+ program
83
+ .command('list')
84
+ .description('List active reminders')
85
+ .action(async () => {
86
+ try {
87
+ const data = await api.listReminders();
88
+ const list = data.reminders;
89
+ if (list.length === 0) {
90
+ console.log('\n No active reminders.\n');
91
+ return;
92
+ }
93
+ console.log(`\n ${list.length} active reminder${list.length === 1 ? '' : 's'}:\n`);
94
+ for (const r of list) {
95
+ const type = r.isRecurring ? '[recurring]' : '[one-time]';
96
+ const next = r.nextFireAt ? ` -> ${new Date(r.nextFireAt).toLocaleString()}` : '';
97
+ console.log(` ${r.id.slice(0, 8)} ${type} ${r.title}${next}`);
98
+ }
99
+ console.log('');
100
+ }
101
+ catch (err) {
102
+ console.error(` Error: ${err.message}`);
103
+ process.exit(1);
104
+ }
105
+ });
106
+ // ── Delete Reminder ──
107
+ program
108
+ .command('delete <id>')
109
+ .description('Delete a reminder by ID (prefix match)')
110
+ .action(async (id) => {
111
+ try {
112
+ // Support prefix matching
113
+ const data = await api.listReminders();
114
+ const match = data.reminders.find((r) => r.id.startsWith(id));
115
+ if (!match) {
116
+ console.error(` No reminder found matching "${id}"`);
117
+ process.exit(1);
118
+ }
119
+ const confirm = await prompt(` Delete "${match.title}"? [y/N] `);
120
+ if (confirm.toLowerCase() !== 'y') {
121
+ console.log(' Cancelled.');
122
+ return;
123
+ }
124
+ await api.deleteReminder(match.id);
125
+ console.log(` Deleted: ${match.title}\n`);
126
+ }
127
+ catch (err) {
128
+ console.error(` Error: ${err.message}`);
129
+ process.exit(1);
130
+ }
131
+ });
132
+ // ── Schedule Message ──
133
+ program
134
+ .command('schedule')
135
+ .description('Schedule an SMS to a phone number')
136
+ .requiredOption('--to <phone>', 'Destination phone number (E.164)')
137
+ .requiredOption('--body <text>', 'Message body')
138
+ .requiredOption('--at <datetime>', 'ISO 8601 timestamp or natural date string')
139
+ .action(async (opts) => {
140
+ try {
141
+ const result = await api.scheduleMessage(opts.to, opts.body, opts.at);
142
+ console.log(`\n Scheduled: ${result.message.id.slice(0, 8)}`);
143
+ console.log(` To: ${result.message.to}`);
144
+ console.log(` At: ${new Date(result.message.scheduledFor).toLocaleString()}`);
145
+ console.log(` Status: ${result.message.status}\n`);
146
+ }
147
+ catch (err) {
148
+ console.error(` Error: ${err.message}`);
149
+ process.exit(1);
150
+ }
151
+ });
152
+ // ── Messages ──
153
+ program
154
+ .command('messages')
155
+ .description('List scheduled messages')
156
+ .action(async () => {
157
+ try {
158
+ const data = await api.listMessages();
159
+ const list = data.messages;
160
+ if (list.length === 0) {
161
+ console.log('\n No scheduled messages.\n');
162
+ return;
163
+ }
164
+ console.log(`\n ${list.length} message${list.length === 1 ? '' : 's'}:\n`);
165
+ for (const m of list) {
166
+ const at = new Date(m.scheduledFor).toLocaleString();
167
+ console.log(` ${m.id.slice(0, 8)} [${m.status}] ${m.to} "${m.body.slice(0, 40)}${m.body.length > 40 ? '...' : ''}" @ ${at}`);
168
+ }
169
+ console.log('');
170
+ }
171
+ catch (err) {
172
+ console.error(` Error: ${err.message}`);
173
+ process.exit(1);
174
+ }
175
+ });
176
+ // ── Settings ──
177
+ program
178
+ .command('settings')
179
+ .description('View or update settings')
180
+ .option('--timezone <tz>', 'Set timezone (e.g., America/New_York)')
181
+ .option('--digest-time <time>', 'Set digest time (e.g., 08:00)')
182
+ .option('--digest <on|off>', 'Enable or disable digest')
183
+ .action(async (opts) => {
184
+ try {
185
+ if (opts.timezone || opts.digestTime || opts.digest) {
186
+ const update = {};
187
+ if (opts.timezone)
188
+ update.timezone = opts.timezone;
189
+ if (opts.digestTime)
190
+ update.digestTime = opts.digestTime;
191
+ if (opts.digest)
192
+ update.digestEnabled = opts.digest === 'on';
193
+ await api.updateSettings(update);
194
+ console.log(' Settings updated.\n');
195
+ }
196
+ const data = await api.getSettings();
197
+ console.log(`\n Timezone: ${data.timezone}`);
198
+ console.log(` Digest: ${data.digestEnabled ? 'on' : 'off'} at ${data.digestTime?.slice(0, 5) || '08:00'}\n`);
199
+ }
200
+ catch (err) {
201
+ console.error(` Error: ${err.message}`);
202
+ process.exit(1);
203
+ }
204
+ });
205
+ // ── Auto-init on first run ──
206
+ const config = getConfig();
207
+ if (!config && process.argv[2] !== 'init' && process.argv[2] !== '--help' && process.argv[2] !== '-h' && process.argv[2] !== '--version' && process.argv[2] !== '-V') {
208
+ console.log('\n Welcome to Remind Me CLI!\n');
209
+ console.log(' Looks like this is your first time. Let\'s set up your API key.');
210
+ console.log(' You can generate one from the web dashboard.\n');
211
+ console.log(' Run: remind-me init\n');
212
+ process.exit(0);
213
+ }
214
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@supermagicapps/remind-me",
3
+ "version": "1.0.0",
4
+ "description": "CLI for the Remind Me reminders platform",
5
+ "type": "module",
6
+ "bin": {
7
+ "remind-me": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/index.ts"
12
+ },
13
+ "dependencies": {
14
+ "commander": "^13.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "tsx": "^4.19.2",
18
+ "typescript": "^5.7.3"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ]
26
+ }