Initial backup 2026-02-17

This commit is contained in:
Krilly
2026-02-17 15:50:53 +00:00
commit 8902a93add
941 changed files with 131420 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
#!/usr/bin/env node
/**
* Create a calendar event
*
* Usage:
* node create-event.mjs --title "Meeting" --start "2026-02-03 14:00:00" --end "2026-02-03 15:00:00"
*/
import { parseArgs } from 'util';
import { createEvent, DEFAULT_CALENDAR_ID, DEFAULT_TIMEZONE } from '../lib/calendar.mjs';
import { resolveNames, getDisplayNameSync } from '../lib/employees.mjs';
const { values } = parseArgs({
options: {
title: { type: 'string' },
description: { type: 'string', default: '' },
start: { type: 'string' },
end: { type: 'string' },
attendees: { type: 'string', default: '' },
'attendee-ids': { type: 'string', default: '' },
location: { type: 'string', default: '' },
timezone: { type: 'string', default: DEFAULT_TIMEZONE },
calendar: { type: 'string', default: DEFAULT_CALENDAR_ID },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Create a Lark calendar event
Options:
--title Event title (required)
--description Event description
--start Start time: YYYY-MM-DD HH:MM:SS (required)
--end End time: YYYY-MM-DD HH:MM:SS (required)
--attendees Comma-separated names (auto-resolved to user_ids)
--attendee-ids Comma-separated user_ids directly
--location Event location
--timezone IANA timezone (default: Asia/Singapore)
--calendar Calendar ID (uses default Claw calendar if omitted)
-h, --help Show this help
Examples:
node create-event.mjs --title "Team Sync" --start "2026-02-03 10:00:00" --end "2026-02-03 10:30:00"
node create-event.mjs --title "Review" --start "2026-02-03 14:00:00" --end "2026-02-03 15:00:00" --attendees "Boyang,RK,jc"
`);
process.exit(0);
}
// Validate required fields
if (!values.title) {
console.error('Error: --title is required');
process.exit(1);
}
if (!values.start) {
console.error('Error: --start is required');
process.exit(1);
}
if (!values.end) {
console.error('Error: --end is required');
process.exit(1);
}
// Resolve attendees
let attendeeIds = [];
if (values['attendee-ids']) {
attendeeIds = values['attendee-ids'].split(',').map(s => s.trim()).filter(Boolean);
}
if (values.attendees) {
const names = values.attendees.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
attendeeIds = [...new Set([...attendeeIds, ...resolved])];
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
}
async function main() {
try {
console.log('Creating event...');
console.log(` Title: ${values.title}`);
console.log(` Start: ${values.start}`);
console.log(` End: ${values.end}`);
console.log(` Timezone: ${values.timezone}`);
console.log(` Attendees: ${attendeeIds.map(id => getDisplayNameSync(id)).join(', ') || '(Boyang will be added automatically)'}`);
if (values.location) console.log(` Location: ${values.location}`);
console.log('');
const result = await createEvent({
title: values.title,
description: values.description,
startTime: values.start,
endTime: values.end,
attendeeIds,
location: values.location,
timezone: values.timezone,
calendarId: values.calendar
});
console.log('✅ Event created successfully!');
console.log('');
console.log('Event Details:');
console.log(` Event ID: ${result.event.event_id}`);
console.log(` Title: ${result.event.summary}`);
console.log(` Link: ${result.event.app_link || 'N/A'}`);
console.log(` Attendees: ${result.attendeeNames || 'N/A'}`);
// Output JSON for programmatic use
if (process.env.JSON_OUTPUT) {
console.log('\nJSON:');
console.log(JSON.stringify(result, null, 2));
}
} catch (error) {
console.error('❌ Failed to create event:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env node
/**
* Create a task
*
* Usage:
* node create-task.mjs --title "Review PR" --due "2026-02-05 18:00:00"
*/
import { parseArgs } from 'util';
import { createTask } from '../lib/task.mjs';
import { DEFAULT_TIMEZONE } from '../lib/calendar.mjs';
import { resolveNames, getDisplayNameSync } from '../lib/employees.mjs';
const { values } = parseArgs({
options: {
title: { type: 'string' },
description: { type: 'string', default: '' },
due: { type: 'string' },
assignees: { type: 'string', default: '' },
'assignee-ids': { type: 'string', default: '' },
timezone: { type: 'string', default: DEFAULT_TIMEZONE },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Create a Lark task
Options:
--title Task title (required)
--description Task description
--due Due time: YYYY-MM-DD HH:MM:SS (required)
--assignees Comma-separated names (auto-resolved to user_ids)
--assignee-ids Comma-separated user_ids directly
--timezone IANA timezone (default: Asia/Singapore)
-h, --help Show this help
Examples:
node create-task.mjs --title "Review PR #123" --due "2026-02-05 18:00:00"
node create-task.mjs --title "Finish report" --due "2026-02-03 17:00:00" --assignees "Boyang,jc"
`);
process.exit(0);
}
// Validate required fields
if (!values.title) {
console.error('Error: --title is required');
process.exit(1);
}
if (!values.due) {
console.error('Error: --due is required');
process.exit(1);
}
// Resolve assignees
let assigneeIds = [];
if (values['assignee-ids']) {
assigneeIds = values['assignee-ids'].split(',').map(s => s.trim()).filter(Boolean);
}
if (values.assignees) {
const names = values.assignees.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
assigneeIds = [...new Set([...assigneeIds, ...resolved])];
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
}
async function main() {
try {
console.log('Creating task...');
console.log(` Title: ${values.title}`);
console.log(` Due: ${values.due}`);
console.log(` Timezone: ${values.timezone}`);
console.log(` Assignees: ${assigneeIds.map(id => getDisplayNameSync(id)).join(', ') || '(none)'}`);
console.log('');
const task = await createTask({
title: values.title,
description: values.description,
dueTime: values.due,
assigneeIds,
timezone: values.timezone
});
console.log('✅ Task created successfully!');
console.log('');
console.log('Task Details:');
console.log(` Task ID: ${task.guid}`);
console.log(` Title: ${task.summary}`);
console.log(` URL: ${task.url || 'N/A'}`);
// Output JSON for programmatic use
if (process.env.JSON_OUTPUT) {
console.log('\nJSON:');
console.log(JSON.stringify(task, null, 2));
}
} catch (error) {
console.error('❌ Failed to create task:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env node
/**
* Delete a calendar event
*
* Usage:
* node delete-event.mjs --event-id "f9900f6b-b472-4b17-a818-7b5584abdc37_0"
*/
import { parseArgs } from 'util';
import { deleteEvent, DEFAULT_CALENDAR_ID } from '../lib/calendar.mjs';
const { values } = parseArgs({
options: {
'event-id': { type: 'string' },
calendar: { type: 'string', default: DEFAULT_CALENDAR_ID },
'no-notify': { type: 'boolean', default: false },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Delete a Lark calendar event
Options:
--event-id Event ID to delete (required)
--calendar Calendar ID
--no-notify Don't send notifications to attendees
-h, --help Show this help
Examples:
node delete-event.mjs --event-id "f9900f6b-b472-4b17-a818-7b5584abdc37_0"
`);
process.exit(0);
}
if (!values['event-id']) {
console.error('Error: --event-id is required');
process.exit(1);
}
async function main() {
try {
console.log(`Deleting event: ${values['event-id']}`);
await deleteEvent(values['event-id'], values.calendar, !values['no-notify']);
console.log('✅ Event deleted successfully!');
} catch (error) {
console.error('❌ Failed to delete event:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env node
/**
* Delete a task
*
* Usage:
* node delete-task.mjs --task-id "35fc5310-a1b1-49c7-be75-be631d3079ee"
*/
import { parseArgs } from 'util';
import { deleteTask } from '../lib/task.mjs';
const { values } = parseArgs({
options: {
'task-id': { type: 'string' },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Delete a Lark task
Options:
--task-id Task GUID to delete (required)
-h, --help Show this help
Examples:
node delete-task.mjs --task-id "35fc5310-a1b1-49c7-be75-be631d3079ee"
`);
process.exit(0);
}
if (!values['task-id']) {
console.error('Error: --task-id is required');
process.exit(1);
}
async function main() {
try {
console.log(`Deleting task: ${values['task-id']}`);
await deleteTask(values['task-id']);
console.log('✅ Task deleted successfully!');
} catch (error) {
console.error('❌ Failed to delete task:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env node
/**
* List calendar events
*
* Usage:
* node list-events.mjs
* node list-events.mjs --start "2026-02-01" --end "2026-02-28"
*/
import { parseArgs } from 'util';
import { listEvents, timestampToDatetime, DEFAULT_CALENDAR_ID, DEFAULT_TIMEZONE } from '../lib/calendar.mjs';
const { values } = parseArgs({
options: {
start: { type: 'string' },
end: { type: 'string' },
timezone: { type: 'string', default: DEFAULT_TIMEZONE },
calendar: { type: 'string', default: DEFAULT_CALENDAR_ID },
json: { type: 'boolean' },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
List Lark calendar events
Options:
--start Start date: YYYY-MM-DD (default: today)
--end End date: YYYY-MM-DD (default: 7 days from now)
--timezone IANA timezone (default: Asia/Singapore)
--calendar Calendar ID
--json Output as JSON
-h, --help Show this help
Examples:
node list-events.mjs
node list-events.mjs --start "2026-02-01" --end "2026-02-28"
`);
process.exit(0);
}
async function main() {
try {
const events = await listEvents({
calendarId: values.calendar,
startTime: values.start,
endTime: values.end,
timezone: values.timezone
});
if (values.json) {
console.log(JSON.stringify(events, null, 2));
return;
}
if (events.length === 0) {
console.log('No events found in the specified range.');
return;
}
console.log(`Found ${events.length} event(s):\n`);
for (const event of events) {
const startTime = event.start_time?.timestamp
? timestampToDatetime(parseInt(event.start_time.timestamp), values.timezone)
: 'N/A';
const endTime = event.end_time?.timestamp
? timestampToDatetime(parseInt(event.end_time.timestamp), values.timezone)
: 'N/A';
console.log(`📅 ${event.summary || '(No title)'}`);
console.log(` ID: ${event.event_id}`);
console.log(` Time: ${startTime}${endTime}`);
if (event.location?.name) {
console.log(` Location: ${event.location.name}`);
}
if (event.description) {
console.log(` Description: ${event.description.substring(0, 100)}${event.description.length > 100 ? '...' : ''}`);
}
console.log('');
}
} catch (error) {
console.error('❌ Failed to list events:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env node
/**
* Add or remove attendees from a calendar event
*
* Usage:
* node manage-attendees.mjs --event-id "xxx" --add "RK,jc"
* node manage-attendees.mjs --event-id "xxx" --remove "jc"
*/
import { parseArgs } from 'util';
import { addEventAttendees, removeEventAttendees, DEFAULT_CALENDAR_ID } from '../lib/calendar.mjs';
import { resolveNames, getDisplayNameSync } from '../lib/employees.mjs';
const { values } = parseArgs({
options: {
'event-id': { type: 'string' },
add: { type: 'string' },
remove: { type: 'string' },
'user-ids': { type: 'string' },
calendar: { type: 'string', default: DEFAULT_CALENDAR_ID },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Manage calendar event attendees
Options:
--event-id Event ID (required)
--add Comma-separated names to add
--remove Comma-separated names to remove
--user-ids Comma-separated user_ids directly (use with --add or --remove)
--calendar Calendar ID
-h, --help Show this help
Examples:
node manage-attendees.mjs --event-id "xxx" --add "RK,jc"
node manage-attendees.mjs --event-id "xxx" --remove "jc"
`);
process.exit(0);
}
if (!values['event-id']) {
console.error('Error: --event-id is required');
process.exit(1);
}
if (!values.add && !values.remove) {
console.error('Error: Either --add or --remove is required');
process.exit(1);
}
async function main() {
try {
const calendarId = values.calendar;
const eventId = values['event-id'];
if (values.add) {
const names = values.add.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
if (values['user-ids']) {
resolved.push(...values['user-ids'].split(',').map(s => s.trim()).filter(Boolean));
}
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
if (resolved.length === 0) {
console.error('No valid attendees to add');
process.exit(1);
}
console.log(`Adding attendees: ${resolved.map(id => getDisplayNameSync(id)).join(', ')}`);
const result = await addEventAttendees(calendarId, eventId, resolved);
console.log('✅ Attendees added successfully!');
console.log(`Updated attendees: ${(result.attendees || []).map(a => a.display_name).join(', ')}`);
}
if (values.remove) {
const names = values.remove.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
if (resolved.length === 0) {
console.error('No valid attendees to remove');
process.exit(1);
}
console.log(`Removing attendees: ${resolved.map(id => getDisplayNameSync(id)).join(', ')}`);
await removeEventAttendees(calendarId, eventId, resolved);
console.log('✅ Attendees removed successfully!');
}
} catch (error) {
console.error('❌ Failed:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env node
/**
* Add or remove members from a task
*
* Usage:
* node manage-task-members.mjs --task-id "xxx" --add "RK,jc"
* node manage-task-members.mjs --task-id "xxx" --remove "jc"
*/
import { parseArgs } from 'util';
import { addTaskMembers, removeTaskMembers } from '../lib/task.mjs';
import { resolveNames, getDisplayNameSync } from '../lib/employees.mjs';
const { values } = parseArgs({
options: {
'task-id': { type: 'string' },
add: { type: 'string' },
remove: { type: 'string' },
'user-ids': { type: 'string' },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Manage task members/assignees
Options:
--task-id Task GUID (required)
--add Comma-separated names to add
--remove Comma-separated names to remove
--user-ids Comma-separated user_ids directly (use with --add or --remove)
-h, --help Show this help
Examples:
node manage-task-members.mjs --task-id "xxx" --add "RK,jc"
node manage-task-members.mjs --task-id "xxx" --remove "jc"
`);
process.exit(0);
}
if (!values['task-id']) {
console.error('Error: --task-id is required');
process.exit(1);
}
if (!values.add && !values.remove) {
console.error('Error: Either --add or --remove is required');
process.exit(1);
}
async function main() {
try {
const taskId = values['task-id'];
if (values.add) {
const names = values.add.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
if (values['user-ids']) {
resolved.push(...values['user-ids'].split(',').map(s => s.trim()).filter(Boolean));
}
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
if (resolved.length === 0) {
console.error('No valid members to add');
process.exit(1);
}
console.log(`Adding members: ${resolved.map(id => getDisplayNameSync(id)).join(', ')}`);
await addTaskMembers(taskId, resolved);
console.log('✅ Members added successfully!');
}
if (values.remove) {
const names = values.remove.split(',').map(s => s.trim()).filter(Boolean);
const { resolved, unresolved } = resolveNames(names);
if (unresolved.length > 0) {
console.warn(`Warning: Could not resolve names: ${unresolved.join(', ')}`);
}
if (resolved.length === 0) {
console.error('No valid members to remove');
process.exit(1);
}
console.log(`Removing members: ${resolved.map(id => getDisplayNameSync(id)).join(', ')}`);
await removeTaskMembers(taskId, resolved);
console.log('✅ Members removed successfully!');
}
} catch (error) {
console.error('❌ Failed:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env node
/**
* Update a calendar event
*
* Usage:
* node update-event.mjs --event-id "xxx" --title "New Title"
*/
import { parseArgs } from 'util';
import { updateEvent, DEFAULT_CALENDAR_ID, DEFAULT_TIMEZONE } from '../lib/calendar.mjs';
const { values } = parseArgs({
options: {
'event-id': { type: 'string' },
title: { type: 'string' },
description: { type: 'string' },
start: { type: 'string' },
end: { type: 'string' },
location: { type: 'string' },
timezone: { type: 'string', default: DEFAULT_TIMEZONE },
calendar: { type: 'string', default: DEFAULT_CALENDAR_ID },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Update a Lark calendar event
Options:
--event-id Event ID to update (required)
--title New event title
--description New event description
--start New start time: YYYY-MM-DD HH:MM:SS
--end New end time: YYYY-MM-DD HH:MM:SS
--location New event location
--timezone IANA timezone (default: Asia/Singapore)
--calendar Calendar ID
-h, --help Show this help
Examples:
node update-event.mjs --event-id "xxx" --title "Updated Meeting"
node update-event.mjs --event-id "xxx" --start "2026-02-03 15:00:00" --end "2026-02-03 16:00:00"
`);
process.exit(0);
}
if (!values['event-id']) {
console.error('Error: --event-id is required');
process.exit(1);
}
async function main() {
try {
console.log(`Updating event: ${values['event-id']}`);
const event = await updateEvent({
eventId: values['event-id'],
title: values.title,
description: values.description,
startTime: values.start,
endTime: values.end,
location: values.location,
timezone: values.timezone,
calendarId: values.calendar
});
console.log('✅ Event updated successfully!');
console.log('');
console.log('Updated Event:');
console.log(` Title: ${event.summary}`);
console.log(` Event ID: ${event.event_id}`);
} catch (error) {
console.error('❌ Failed to update event:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env node
/**
* Update a task
*
* Usage:
* node update-task.mjs --task-id "xxx" --title "New Title"
*/
import { parseArgs } from 'util';
import { updateTask } from '../lib/task.mjs';
import { DEFAULT_TIMEZONE } from '../lib/calendar.mjs';
const { values } = parseArgs({
options: {
'task-id': { type: 'string' },
title: { type: 'string' },
description: { type: 'string' },
due: { type: 'string' },
timezone: { type: 'string', default: DEFAULT_TIMEZONE },
help: { type: 'boolean', short: 'h' }
}
});
if (values.help) {
console.log(`
Update a Lark task
Options:
--task-id Task GUID to update (required)
--title New task title
--description New task description
--due New due time: YYYY-MM-DD HH:MM:SS
--timezone IANA timezone (default: Asia/Singapore)
-h, --help Show this help
Examples:
node update-task.mjs --task-id "xxx" --title "Updated Task"
node update-task.mjs --task-id "xxx" --due "2026-02-06 18:00:00"
`);
process.exit(0);
}
if (!values['task-id']) {
console.error('Error: --task-id is required');
process.exit(1);
}
async function main() {
try {
console.log(`Updating task: ${values['task-id']}`);
const task = await updateTask({
taskId: values['task-id'],
title: values.title,
description: values.description,
dueTime: values.due,
timezone: values.timezone
});
console.log('✅ Task updated successfully!');
console.log('');
console.log('Updated Task:');
console.log(` Title: ${task.summary}`);
console.log(` Task ID: ${task.guid}`);
} catch (error) {
console.error('❌ Failed to update task:', error.message);
if (error.larkResponse) {
console.error('Lark response:', JSON.stringify(error.larkResponse, null, 2));
}
process.exit(1);
}
}
main();