@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 +8 -0
- package/dist/api.js +54 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +214 -0
- package/package.json +26 -0
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
|
+
}
|
package/dist/config.d.ts
ADDED
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
|
+
}
|
package/dist/index.d.ts
ADDED
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
|
+
}
|