@robbeverhelst/do 1.343.0 → 1.344.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/do.js +1152 -82
- package/package.json +1 -1
package/dist/do.js
CHANGED
|
@@ -14420,7 +14420,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
|
|
|
14420
14420
|
|
|
14421
14421
|
// src/meta.ts
|
|
14422
14422
|
var CLI_NAME = "do";
|
|
14423
|
-
var VERSION = "1.
|
|
14423
|
+
var VERSION = "1.344.0";
|
|
14424
14424
|
var API_PREFIX = "/api/v1";
|
|
14425
14425
|
var DEFAULT_API_URL = "http://localhost:3001";
|
|
14426
14426
|
|
|
@@ -17984,9 +17984,13 @@ var tasks = pgTable("tasks", {
|
|
|
17984
17984
|
readyAt: timestamp("ready_at", { withTimezone: true }),
|
|
17985
17985
|
readyNote: text("ready_note"),
|
|
17986
17986
|
moveCount: integer2("move_count").notNull().default(0),
|
|
17987
|
-
movedFrom: timestamp("moved_from", { withTimezone: true })
|
|
17987
|
+
movedFrom: timestamp("moved_from", { withTimezone: true }),
|
|
17988
|
+
seriesId: text("series_id"),
|
|
17989
|
+
seriesLabel: text("series_label"),
|
|
17990
|
+
seriesSlot: date5("series_slot", { mode: "string" })
|
|
17988
17991
|
}, (t) => [
|
|
17989
17992
|
index("tasks_tenant_project_idx").on(t.tenantId, t.projectId),
|
|
17993
|
+
uniqueIndex("tasks_series_slot_uniq").on(t.seriesId, t.seriesSlot).where(sql`${t.seriesId} IS NOT NULL`),
|
|
17990
17994
|
unique("tasks_tenant_number_uniq").on(t.tenantId, t.number),
|
|
17991
17995
|
index("tasks_tenant_place_idx").on(t.tenantId, t.placeId),
|
|
17992
17996
|
index("tasks_milestone_idx").on(t.milestoneId),
|
|
@@ -18025,11 +18029,13 @@ var habits = pgTable("habits", {
|
|
|
18025
18029
|
retiredAt: timestamp("retired_at", { withTimezone: true }),
|
|
18026
18030
|
eventSourceId: text("event_source_id"),
|
|
18027
18031
|
eventMatch: text("event_match"),
|
|
18028
|
-
eventOffset: text("event_offset")
|
|
18032
|
+
eventOffset: text("event_offset"),
|
|
18033
|
+
durationMinutes: integer2("duration_minutes")
|
|
18029
18034
|
}, (t) => [
|
|
18030
18035
|
index("habits_tenant_idx").on(t.tenantId),
|
|
18031
18036
|
index("habits_tenant_area_idx").on(t.tenantId, t.areaId),
|
|
18032
|
-
check2("habits_time_of_day_chk", sql`${t.timeOfDay} IS NULL OR ${t.timeOfDay} IN ('morning', 'midday', 'evening') OR ${t.timeOfDay} ~ '^([01][0-9]|2[0-3]):[0-5][0-9]$'`)
|
|
18037
|
+
check2("habits_time_of_day_chk", sql`${t.timeOfDay} IS NULL OR ${t.timeOfDay} IN ('morning', 'midday', 'evening') OR ${t.timeOfDay} ~ '^([01][0-9]|2[0-3]):[0-5][0-9]$'`),
|
|
18038
|
+
check2("habits_duration_minutes_chk", sql`${t.durationMinutes} IS NULL OR ${t.durationMinutes} BETWEEN 1 AND 600`)
|
|
18033
18039
|
]);
|
|
18034
18040
|
var habitEntries = pgTable("habit_entries", {
|
|
18035
18041
|
id: text("id").primaryKey(),
|
|
@@ -18083,7 +18089,7 @@ var occurrences = pgTable("occurrences", {
|
|
|
18083
18089
|
|
|
18084
18090
|
// ../../packages/schema/src/db/core.ts
|
|
18085
18091
|
var routineKind = pgEnum("routine_kind", ["sequence", "time_blocked", "checklist"]);
|
|
18086
|
-
var routineStepRef = pgEnum("routine_step_ref", ["free", "habit", "task"]);
|
|
18092
|
+
var routineStepRef = pgEnum("routine_step_ref", ["free", "habit", "task", "routine"]);
|
|
18087
18093
|
var projectStatus = pgEnum("project_status", [
|
|
18088
18094
|
"planning",
|
|
18089
18095
|
"active",
|
|
@@ -18093,6 +18099,12 @@ var projectStatus = pgEnum("project_status", [
|
|
|
18093
18099
|
]);
|
|
18094
18100
|
var settingsTheme = pgEnum("settings_theme", ["system", "light", "dark"]);
|
|
18095
18101
|
var settingsDensity = pgEnum("settings_density", ["comfortable", "compact"]);
|
|
18102
|
+
var settingsGlass = pgEnum("settings_glass", ["liquid", "frosted", "solid"]);
|
|
18103
|
+
var settingsThemePack = pgEnum("settings_theme_pack", ["do", "skyrim", "pokemon"]);
|
|
18104
|
+
var settingsThemePackScope = pgEnum("settings_theme_pack_scope", [
|
|
18105
|
+
"section",
|
|
18106
|
+
"everywhere"
|
|
18107
|
+
]);
|
|
18096
18108
|
var settingsAccent = pgEnum("settings_accent", ["teal", "purple", "amber", "rose"]);
|
|
18097
18109
|
var routineRunStatus = pgEnum("routine_run_status", [
|
|
18098
18110
|
"in_progress",
|
|
@@ -18126,6 +18138,8 @@ var routineSteps = pgTable("routine_steps", {
|
|
|
18126
18138
|
refKind: routineStepRef("ref_kind").notNull().default("free"),
|
|
18127
18139
|
habitId: text("habit_id").references(() => habits.id),
|
|
18128
18140
|
taskId: text("task_id").references(() => tasks.id),
|
|
18141
|
+
childRoutineId: text("child_routine_id").references(() => routines.id),
|
|
18142
|
+
optional: boolean4("optional").notNull().default(false),
|
|
18129
18143
|
sortOrder: integer2("sort_order").notNull().default(0),
|
|
18130
18144
|
durationMinutes: integer2("duration_minutes"),
|
|
18131
18145
|
notes: text("notes"),
|
|
@@ -18133,11 +18147,14 @@ var routineSteps = pgTable("routine_steps", {
|
|
|
18133
18147
|
}, (t) => [
|
|
18134
18148
|
index("routine_steps_routine_order_idx").on(t.routineId, t.sortOrder),
|
|
18135
18149
|
index("routine_steps_habit_idx").on(t.habitId),
|
|
18150
|
+
index("routine_steps_child_routine_idx").on(t.childRoutineId),
|
|
18136
18151
|
check2("routine_steps_ref_kind_chk", sql`(
|
|
18137
|
-
(${t.refKind} = 'free'
|
|
18138
|
-
(${t.refKind} = 'habit'
|
|
18139
|
-
(${t.refKind} = 'task'
|
|
18140
|
-
|
|
18152
|
+
(${t.refKind} = 'free' AND ${t.habitId} IS NULL AND ${t.taskId} IS NULL AND ${t.childRoutineId} IS NULL) OR
|
|
18153
|
+
(${t.refKind} = 'habit' AND ${t.habitId} IS NOT NULL AND ${t.taskId} IS NULL AND ${t.childRoutineId} IS NULL) OR
|
|
18154
|
+
(${t.refKind} = 'task' AND ${t.taskId} IS NOT NULL AND ${t.habitId} IS NULL AND ${t.childRoutineId} IS NULL) OR
|
|
18155
|
+
(${t.refKind} = 'routine' AND ${t.childRoutineId} IS NOT NULL AND ${t.habitId} IS NULL AND ${t.taskId} IS NULL)
|
|
18156
|
+
)`),
|
|
18157
|
+
check2("routine_steps_not_self_chk", sql`${t.childRoutineId} IS NULL OR ${t.childRoutineId} <> ${t.routineId}`)
|
|
18141
18158
|
]);
|
|
18142
18159
|
var routineRuns = pgTable("routine_runs", {
|
|
18143
18160
|
id: text("id").primaryKey(),
|
|
@@ -18157,6 +18174,9 @@ var routineRunSteps = pgTable("routine_run_steps", {
|
|
|
18157
18174
|
refKind: routineStepRef("ref_kind").notNull().default("free"),
|
|
18158
18175
|
habitId: text("habit_id").references(() => habits.id),
|
|
18159
18176
|
taskId: text("task_id").references(() => tasks.id),
|
|
18177
|
+
childRoutineId: text("child_routine_id").references(() => routines.id),
|
|
18178
|
+
groupStepId: text("group_step_id").references(() => routineRunSteps.id),
|
|
18179
|
+
optional: boolean4("optional").notNull().default(false),
|
|
18160
18180
|
reason: text("reason"),
|
|
18161
18181
|
habitEntryId: text("habit_entry_id").references(() => habitEntries.id),
|
|
18162
18182
|
sortOrder: integer2("sort_order").notNull().default(0),
|
|
@@ -18167,7 +18187,8 @@ var routineRunSteps = pgTable("routine_run_steps", {
|
|
|
18167
18187
|
completedAt: timestamp("completed_at", { withTimezone: true })
|
|
18168
18188
|
}, (t) => [
|
|
18169
18189
|
index("routine_run_steps_run_order_idx").on(t.runId, t.sortOrder),
|
|
18170
|
-
check2("routine_run_steps_ref_kind_chk", sql`(${t.refKind} <> 'habit' OR ${t.habitId} IS NOT NULL)`)
|
|
18190
|
+
check2("routine_run_steps_ref_kind_chk", sql`(${t.refKind} <> 'habit' OR ${t.habitId} IS NOT NULL)`),
|
|
18191
|
+
check2("routine_run_steps_routine_chk", sql`(${t.refKind} <> 'routine' OR ${t.childRoutineId} IS NOT NULL)`)
|
|
18171
18192
|
]);
|
|
18172
18193
|
var projects = pgTable("projects", {
|
|
18173
18194
|
...leafColumns(),
|
|
@@ -18222,6 +18243,14 @@ var userSettings = pgTable("user_settings", {
|
|
|
18222
18243
|
calendarZoom: integer2("calendar_zoom"),
|
|
18223
18244
|
lifeBlockSharing: jsonb("life_block_sharing").$type(),
|
|
18224
18245
|
recentSearches: jsonb("recent_searches").$type().notNull().default([]),
|
|
18246
|
+
glass: settingsGlass("glass").notNull().default("liquid"),
|
|
18247
|
+
followSky: boolean4("follow_sky").notNull().default(true),
|
|
18248
|
+
nightUntilWake: boolean4("night_until_wake").notNull().default(false),
|
|
18249
|
+
largerText: boolean4("larger_text").notNull().default(false),
|
|
18250
|
+
reduceMotion: boolean4("reduce_motion").notNull().default(false),
|
|
18251
|
+
showStreakNumbers: boolean4("show_streak_numbers").notNull().default(true),
|
|
18252
|
+
themePack: settingsThemePack("theme_pack").notNull().default("do"),
|
|
18253
|
+
themePackScope: settingsThemePackScope("theme_pack_scope").notNull().default("section"),
|
|
18225
18254
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
18226
18255
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
18227
18256
|
}, (t) => [unique("user_settings_tenant_user_uniq").on(t.tenantId, t.userId)]);
|
|
@@ -19000,14 +19029,24 @@ var recipeIngredients = pgTable("recipe_ingredients", {
|
|
|
19000
19029
|
index("recipe_ingredients_tenant_idx").on(t.tenantId),
|
|
19001
19030
|
index("recipe_ingredients_product_idx").on(t.productId)
|
|
19002
19031
|
]);
|
|
19032
|
+
var MEAL_PLAN_STATES = ["planned", "cooked", "out", "unsure", "draft"];
|
|
19003
19033
|
var mealPlans = pgTable("meal_plans", {
|
|
19004
19034
|
...leafColumns(),
|
|
19005
19035
|
recipeId: text("recipe_id").references(() => recipes.id),
|
|
19006
19036
|
plannedFor: timestamp("planned_for", { withTimezone: true }).notNull(),
|
|
19007
19037
|
mealType: mealType("meal_type").notNull().default("dinner"),
|
|
19008
19038
|
servings: integer2("servings"),
|
|
19009
|
-
notes: text("notes")
|
|
19010
|
-
|
|
19039
|
+
notes: text("notes"),
|
|
19040
|
+
state: text("state", { enum: MEAL_PLAN_STATES }).notNull().default("planned"),
|
|
19041
|
+
cookUserId: text("cook_user_id").references(() => users.id, { onDelete: "set null" }),
|
|
19042
|
+
company: text("company"),
|
|
19043
|
+
leftoverPortions: integer2("leftover_portions"),
|
|
19044
|
+
leftoverEatBy: date5("leftover_eat_by", { mode: "string" }),
|
|
19045
|
+
leftoverFrozen: boolean4("leftover_frozen").notNull().default(false)
|
|
19046
|
+
}, (t) => [
|
|
19047
|
+
index("meal_plans_tenant_planned_idx").on(t.tenantId, t.plannedFor),
|
|
19048
|
+
check2("meal_plans_state_check", sql`${t.state} IN ('planned', 'cooked', 'out', 'unsure', 'draft')`)
|
|
19049
|
+
]);
|
|
19011
19050
|
|
|
19012
19051
|
// ../../packages/schema/src/db/reflections.ts
|
|
19013
19052
|
var reflectionKind = pgEnum("reflection_kind", ["daily", "weekly"]);
|
|
@@ -21357,7 +21396,8 @@ var habitSchema = createSelectSchema(habits, {
|
|
|
21357
21396
|
retiredAt: exports_coerce.date().nullable().default(null),
|
|
21358
21397
|
eventSourceId: string2().nullable().default(null),
|
|
21359
21398
|
eventMatch: string2().nullable().default(null),
|
|
21360
|
-
eventOffset: string2().nullable().default(null)
|
|
21399
|
+
eventOffset: string2().nullable().default(null),
|
|
21400
|
+
durationMinutes: number2().int().nullable().default(null)
|
|
21361
21401
|
});
|
|
21362
21402
|
var habitEntrySchema = createSelectSchema(habitEntries, {
|
|
21363
21403
|
occurredAt: exports_coerce.date(),
|
|
@@ -21374,6 +21414,8 @@ var habitTimeOfDaySchema = union([
|
|
|
21374
21414
|
_enum2(HABIT_PARTS_OF_DAY),
|
|
21375
21415
|
string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "Use a 24-hour time, like 07:30")
|
|
21376
21416
|
]);
|
|
21417
|
+
var HABIT_DURATION_MAX_MINUTES = 600;
|
|
21418
|
+
var habitDurationMinutesSchema = number2().int().min(1).max(HABIT_DURATION_MAX_MINUTES);
|
|
21377
21419
|
var habitAliasSchema = string2().trim().min(1).max(60);
|
|
21378
21420
|
var habitAliasesSchema = array(habitAliasSchema).max(12).transform((aliases) => aliases.filter((a, i) => aliases.findIndex((b) => b.toLowerCase() === a.toLowerCase()) === i));
|
|
21379
21421
|
var habitScheduleSchema = discriminatedUnion("kind", [
|
|
@@ -21412,7 +21454,8 @@ var createHabitInputSchema = createInsertSchema(habits, {
|
|
|
21412
21454
|
areaId: ulidSchema.nullish(),
|
|
21413
21455
|
aliases: habitAliasesSchema.optional(),
|
|
21414
21456
|
timeOfDay: habitTimeOfDaySchema.nullish(),
|
|
21415
|
-
remind: boolean2().optional()
|
|
21457
|
+
remind: boolean2().optional(),
|
|
21458
|
+
durationMinutes: habitDurationMinutesSchema.nullish()
|
|
21416
21459
|
}).omit({ tenantId: true, recurrenceRuleId: true, retiredAt: true, ...eventColumns }).extend({
|
|
21417
21460
|
title: string2().min(1),
|
|
21418
21461
|
schedule: habitScheduleSchema.optional(),
|
|
@@ -21428,7 +21471,8 @@ var updateHabitInputSchema = createUpdateSchema(habits, {
|
|
|
21428
21471
|
areaId: ulidSchema.nullish(),
|
|
21429
21472
|
aliases: habitAliasesSchema.optional(),
|
|
21430
21473
|
timeOfDay: habitTimeOfDaySchema.nullish(),
|
|
21431
|
-
remind: boolean2().optional()
|
|
21474
|
+
remind: boolean2().optional(),
|
|
21475
|
+
durationMinutes: habitDurationMinutesSchema.nullish()
|
|
21432
21476
|
}).omit({
|
|
21433
21477
|
id: true,
|
|
21434
21478
|
tenantId: true,
|
|
@@ -21479,7 +21523,7 @@ var habitLastCheckInSchema = object({
|
|
|
21479
21523
|
note: string2().nullish()
|
|
21480
21524
|
});
|
|
21481
21525
|
var habitPauseSchema = object({
|
|
21482
|
-
until: exports_iso.date(),
|
|
21526
|
+
until: exports_iso.date().nullable(),
|
|
21483
21527
|
by: _enum2(["habit", "away"])
|
|
21484
21528
|
});
|
|
21485
21529
|
var habitTodaySchema = object({
|
|
@@ -21495,6 +21539,7 @@ var habitSummarySchema = habitSchema.extend({
|
|
|
21495
21539
|
inRoutines: array(habitRoutineRefSchema).default([]),
|
|
21496
21540
|
lastCheckIn: habitLastCheckInSchema.nullable().default(null),
|
|
21497
21541
|
pause: habitPauseSchema.nullable().default(null),
|
|
21542
|
+
paused: boolean2().default(false),
|
|
21498
21543
|
today: habitTodaySchema.default({ due: true, done: false }),
|
|
21499
21544
|
schedule: habitScheduleSchema.default({ kind: "daily" }),
|
|
21500
21545
|
nextDue: array(exports_iso.date()).default([])
|
|
@@ -21522,6 +21567,20 @@ var habitYearSchema = object({
|
|
|
21522
21567
|
marks: array(habitDayMarkSchema)
|
|
21523
21568
|
}))
|
|
21524
21569
|
});
|
|
21570
|
+
var HABIT_DUE_MAX_DAYS = 62;
|
|
21571
|
+
var habitDueQuerySchema = object({ from: exports_iso.date(), to: exports_iso.date() }).strict().refine((q) => q.from <= q.to, { message: "`to` is before `from`", path: ["to"] }).refine((q) => (Date.parse(q.to) - Date.parse(q.from)) / 86400000 + 1 <= HABIT_DUE_MAX_DAYS, {
|
|
21572
|
+
message: `at most ${HABIT_DUE_MAX_DAYS} days`,
|
|
21573
|
+
path: ["to"]
|
|
21574
|
+
});
|
|
21575
|
+
var habitDueDateSchema = object({
|
|
21576
|
+
habitId: ulidSchema,
|
|
21577
|
+
title: string2(),
|
|
21578
|
+
date: exports_iso.date(),
|
|
21579
|
+
time: string2().regex(/^\d{2}:\d{2}$/).nullable(),
|
|
21580
|
+
scheduleLabel: string2().min(1),
|
|
21581
|
+
daily: boolean2(),
|
|
21582
|
+
streakLabel: string2().min(1)
|
|
21583
|
+
});
|
|
21525
21584
|
var habitEntriesQuerySchema = object({
|
|
21526
21585
|
before: exports_coerce.date().optional(),
|
|
21527
21586
|
limit: exports_coerce.number().int().min(1).max(200).default(50)
|
|
@@ -21552,7 +21611,7 @@ var habitSkipInputSchema = object({
|
|
|
21552
21611
|
date: exports_iso.date().optional(),
|
|
21553
21612
|
note: habitEntryNoteSchema.nullish()
|
|
21554
21613
|
}).strict();
|
|
21555
|
-
var habitPauseInputSchema = object({ until: exports_iso.date() }).strict();
|
|
21614
|
+
var habitPauseInputSchema = object({ until: exports_iso.date().nullish() }).strict();
|
|
21556
21615
|
var awayInputSchema = object({ until: exports_iso.date().nullable() }).strict();
|
|
21557
21616
|
var awayStateSchema = object({
|
|
21558
21617
|
until: exports_iso.date().nullable()
|
|
@@ -21896,6 +21955,78 @@ var taskBlockerSchema = object({
|
|
|
21896
21955
|
dueAt: exports_coerce.date().nullable()
|
|
21897
21956
|
});
|
|
21898
21957
|
|
|
21958
|
+
// ../../packages/schema/src/db/task-series.ts
|
|
21959
|
+
var taskSeries = pgTable("task_series", {
|
|
21960
|
+
id: text("id").primaryKey(),
|
|
21961
|
+
tenantId: text("tenant_id").notNull().references(() => tenants.id),
|
|
21962
|
+
title: text("title").notNull(),
|
|
21963
|
+
recurrenceRuleId: text("recurrence_rule_id").notNull().references(() => recurrenceRules.id),
|
|
21964
|
+
startsOn: date5("starts_on", { mode: "string" }).notNull(),
|
|
21965
|
+
occurrenceLabel: text("occurrence_label").notNull().default("date"),
|
|
21966
|
+
leadDays: integer2("lead_days").notNull().default(14),
|
|
21967
|
+
areaId: text("area_id").references(() => areas.id, { onDelete: "set null" }),
|
|
21968
|
+
projectId: text("project_id").references(() => projects.id, { onDelete: "set null" }),
|
|
21969
|
+
tags: text("tags").array().notNull().default([]),
|
|
21970
|
+
priority: workPriority("priority").notNull().default("none"),
|
|
21971
|
+
durationMinutes: integer2("duration_minutes"),
|
|
21972
|
+
ownerUserId: text("owner_user_id").references(() => users.id, { onDelete: "set null" }),
|
|
21973
|
+
ownerAgentId: text("owner_agent_id").references(() => agents.id, { onDelete: "set null" }),
|
|
21974
|
+
subtaskTitles: text("subtask_titles").array().notNull().default([]),
|
|
21975
|
+
active: boolean4("active").notNull().default(true),
|
|
21976
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
21977
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
21978
|
+
}, (t) => [
|
|
21979
|
+
index("task_series_tenant_idx").on(t.tenantId, t.active)
|
|
21980
|
+
]);
|
|
21981
|
+
|
|
21982
|
+
// ../../packages/schema/src/contracts/task-series.ts
|
|
21983
|
+
var taskSeriesRuleSchema = object({
|
|
21984
|
+
freq: _enum2(["weekly", "monthly", "yearly"]),
|
|
21985
|
+
interval: number2().int().min(1).max(52).default(1)
|
|
21986
|
+
}).strict();
|
|
21987
|
+
var taskSeriesLabelSchema = _enum2(["quarter", "month", "date"]);
|
|
21988
|
+
var leadDaysSchema2 = number2().int().min(0).max(365);
|
|
21989
|
+
var taskSeriesRowSchema = createSelectSchema(taskSeries, {
|
|
21990
|
+
occurrenceLabel: taskSeriesLabelSchema,
|
|
21991
|
+
startsOn: exports_iso.date(),
|
|
21992
|
+
createdAt: exports_coerce.date(),
|
|
21993
|
+
updatedAt: exports_coerce.date()
|
|
21994
|
+
});
|
|
21995
|
+
var makeTaskSeriesInputSchema = object({
|
|
21996
|
+
rule: taskSeriesRuleSchema,
|
|
21997
|
+
occurrenceLabel: taskSeriesLabelSchema.default("date"),
|
|
21998
|
+
leadDays: leadDaysSchema2.default(14)
|
|
21999
|
+
}).strict();
|
|
22000
|
+
var updateTaskSeriesInputSchema = object({
|
|
22001
|
+
title: string2().trim().min(1).max(500).optional(),
|
|
22002
|
+
rule: taskSeriesRuleSchema.optional(),
|
|
22003
|
+
occurrenceLabel: taskSeriesLabelSchema.optional(),
|
|
22004
|
+
leadDays: leadDaysSchema2.optional(),
|
|
22005
|
+
active: boolean2().optional()
|
|
22006
|
+
}).strict();
|
|
22007
|
+
var taskSeriesItemSchema = object({
|
|
22008
|
+
taskId: ulidSchema.nullable(),
|
|
22009
|
+
label: string2(),
|
|
22010
|
+
slot: exports_iso.date(),
|
|
22011
|
+
status: taskStatusSchema.nullable(),
|
|
22012
|
+
completedAt: exports_coerce.date().nullable()
|
|
22013
|
+
});
|
|
22014
|
+
var taskSeriesStripSchema = object({
|
|
22015
|
+
id: ulidSchema,
|
|
22016
|
+
title: string2(),
|
|
22017
|
+
rhythm: string2(),
|
|
22018
|
+
items: array(taskSeriesItemSchema),
|
|
22019
|
+
nextMadeOn: exports_iso.date().nullable()
|
|
22020
|
+
});
|
|
22021
|
+
var taskSeriesViewSchema = taskSeriesStripSchema.extend({
|
|
22022
|
+
rule: taskSeriesRuleSchema,
|
|
22023
|
+
occurrenceLabel: taskSeriesLabelSchema,
|
|
22024
|
+
leadDays: number2().int(),
|
|
22025
|
+
startsOn: exports_iso.date(),
|
|
22026
|
+
active: boolean2(),
|
|
22027
|
+
made: number2().int().nonnegative()
|
|
22028
|
+
});
|
|
22029
|
+
|
|
21899
22030
|
// ../../packages/schema/src/contracts/task-source.ts
|
|
21900
22031
|
var taskSourceSchema = object({
|
|
21901
22032
|
kind: string2().trim().min(1).max(40),
|
|
@@ -22068,7 +22199,10 @@ var taskSchema = createSelectSchema(tasks, {
|
|
|
22068
22199
|
movedFrom: exports_coerce.date().nullable().default(null),
|
|
22069
22200
|
waitingFor: string2().nullable().default(null),
|
|
22070
22201
|
nudgedAt: exports_coerce.date().nullable().default(null),
|
|
22071
|
-
nudgeCount: number2().int().nonnegative().default(0)
|
|
22202
|
+
nudgeCount: number2().int().nonnegative().default(0),
|
|
22203
|
+
seriesId: string2().nullable().default(null),
|
|
22204
|
+
seriesLabel: string2().nullable().default(null),
|
|
22205
|
+
seriesSlot: exports_iso.date().nullable().default(null)
|
|
22072
22206
|
});
|
|
22073
22207
|
var PARTY_RE = /^(?:(me)|(member|agent|contact):([^/]+?))(?:\/(.+))?$/;
|
|
22074
22208
|
function partySchema(allowDesk, allowContact = false) {
|
|
@@ -22207,7 +22341,8 @@ var taskViewSchema = taskSchema.omit({
|
|
|
22207
22341
|
blockedBy: array(taskBlockerSchema).default([]),
|
|
22208
22342
|
blocks: number2().int().nonnegative().default(0),
|
|
22209
22343
|
isBlocked: boolean2().default(false),
|
|
22210
|
-
recurrence: taskRecurrenceSchema.nullable().default(null)
|
|
22344
|
+
recurrence: taskRecurrenceSchema.nullable().default(null),
|
|
22345
|
+
series: taskSeriesStripSchema.nullable().default(null)
|
|
22211
22346
|
});
|
|
22212
22347
|
var taskProjectIdSchema = ulidSchema.nullish();
|
|
22213
22348
|
var taskAreaIdSchema = ulidSchema.nullish();
|
|
@@ -22243,6 +22378,9 @@ var createTaskInputSchema = createInsertSchema(tasks, {
|
|
|
22243
22378
|
completedByUserId: true,
|
|
22244
22379
|
completedByAgentId: true,
|
|
22245
22380
|
filingSuggestion: true,
|
|
22381
|
+
seriesId: true,
|
|
22382
|
+
seriesLabel: true,
|
|
22383
|
+
seriesSlot: true,
|
|
22246
22384
|
proposedAt: true,
|
|
22247
22385
|
proposedDesk: true,
|
|
22248
22386
|
notNowUntil: true,
|
|
@@ -22297,6 +22435,9 @@ var updateTaskInputSchema = createUpdateSchema(tasks, {
|
|
|
22297
22435
|
completedByUserId: true,
|
|
22298
22436
|
completedByAgentId: true,
|
|
22299
22437
|
filingSuggestion: true,
|
|
22438
|
+
seriesId: true,
|
|
22439
|
+
seriesLabel: true,
|
|
22440
|
+
seriesSlot: true,
|
|
22300
22441
|
proposedAt: true,
|
|
22301
22442
|
proposedDesk: true,
|
|
22302
22443
|
readyAt: true,
|
|
@@ -22426,7 +22567,9 @@ var attachTaskInputSchema = object({ taskId: ulidSchema });
|
|
|
22426
22567
|
var routineDayTimesSchema = partialRecord(_enum2(["MO", "TU", "WE", "TH", "FR", "SA", "SU"]), string2().regex(/^([01]\d|2[0-3]):[0-5]\d$/));
|
|
22427
22568
|
var routineSchema = createSelectSchema(routines);
|
|
22428
22569
|
var routineStepSchema = createSelectSchema(routineSteps, {
|
|
22429
|
-
createdAt: exports_coerce.date()
|
|
22570
|
+
createdAt: exports_coerce.date(),
|
|
22571
|
+
childRoutineId: string2().nullish(),
|
|
22572
|
+
optional: boolean2().optional()
|
|
22430
22573
|
});
|
|
22431
22574
|
var routineStepInputSchema = object({
|
|
22432
22575
|
id: ulidSchema,
|
|
@@ -22434,12 +22577,36 @@ var routineStepInputSchema = object({
|
|
|
22434
22577
|
refKind: routineStepRefSchema.default("free"),
|
|
22435
22578
|
habitId: ulidSchema.nullish(),
|
|
22436
22579
|
taskId: ulidSchema.nullish(),
|
|
22580
|
+
childRoutineId: ulidSchema.nullish(),
|
|
22437
22581
|
newHabit: createHabitInputSchema.optional(),
|
|
22582
|
+
optional: boolean2().optional(),
|
|
22438
22583
|
sortOrder: number2().int().min(0).optional(),
|
|
22439
22584
|
durationMinutes: number2().int().positive().nullish(),
|
|
22440
22585
|
notes: string2().nullish()
|
|
22441
22586
|
}).superRefine((step, ctx) => {
|
|
22442
|
-
if (step.refKind
|
|
22587
|
+
if (step.refKind !== "routine" && step.childRoutineId) {
|
|
22588
|
+
ctx.addIssue({
|
|
22589
|
+
code: ZodIssueCode.custom,
|
|
22590
|
+
message: "only a routine step sets childRoutineId",
|
|
22591
|
+
path: ["childRoutineId"]
|
|
22592
|
+
});
|
|
22593
|
+
}
|
|
22594
|
+
if (step.refKind === "routine") {
|
|
22595
|
+
if (!step.childRoutineId) {
|
|
22596
|
+
ctx.addIssue({
|
|
22597
|
+
code: ZodIssueCode.custom,
|
|
22598
|
+
message: "a routine step needs childRoutineId",
|
|
22599
|
+
path: ["childRoutineId"]
|
|
22600
|
+
});
|
|
22601
|
+
}
|
|
22602
|
+
if (step.habitId || step.taskId || step.newHabit) {
|
|
22603
|
+
ctx.addIssue({
|
|
22604
|
+
code: ZodIssueCode.custom,
|
|
22605
|
+
message: "a routine step cannot set a habit/task link",
|
|
22606
|
+
path: ["refKind"]
|
|
22607
|
+
});
|
|
22608
|
+
}
|
|
22609
|
+
} else if (step.refKind === "habit") {
|
|
22443
22610
|
const linkId = step.newHabit?.id ?? step.habitId;
|
|
22444
22611
|
if (!linkId) {
|
|
22445
22612
|
ctx.addIssue({
|
|
@@ -22495,6 +22662,16 @@ var createRoutineInputSchema = createInsertSchema(routines, {
|
|
|
22495
22662
|
parentId: ulidSchema.nullish(),
|
|
22496
22663
|
steps: array(routineStepInputSchema).optional(),
|
|
22497
22664
|
dayTimes: routineDayTimesSchema.optional()
|
|
22665
|
+
}).superRefine((input, ctx) => {
|
|
22666
|
+
for (const [i, step] of (input.steps ?? []).entries()) {
|
|
22667
|
+
if (step.childRoutineId && step.childRoutineId === input.id) {
|
|
22668
|
+
ctx.addIssue({
|
|
22669
|
+
code: ZodIssueCode.custom,
|
|
22670
|
+
message: "a routine cannot contain itself",
|
|
22671
|
+
path: ["steps", i, "childRoutineId"]
|
|
22672
|
+
});
|
|
22673
|
+
}
|
|
22674
|
+
}
|
|
22498
22675
|
});
|
|
22499
22676
|
var updateRoutineInputSchema = createUpdateSchema(routines, {
|
|
22500
22677
|
areaId: ulidSchema.nullish()
|
|
@@ -22525,7 +22702,10 @@ var routineRunSchema = createSelectSchema(routineRuns, {
|
|
|
22525
22702
|
var routineRunStepSchema = createSelectSchema(routineRunSteps, {
|
|
22526
22703
|
completedAt: exports_coerce.date().nullable(),
|
|
22527
22704
|
taskId: string2().nullable().default(null),
|
|
22528
|
-
reason: string2().nullable().default(null)
|
|
22705
|
+
reason: string2().nullable().default(null),
|
|
22706
|
+
childRoutineId: string2().nullish(),
|
|
22707
|
+
groupStepId: string2().nullish(),
|
|
22708
|
+
optional: boolean2().optional()
|
|
22529
22709
|
});
|
|
22530
22710
|
var routineRunStepViewSchema = routineRunStepSchema.extend({
|
|
22531
22711
|
habit: habitSummarySchema.nullable().default(null),
|
|
@@ -22615,14 +22795,16 @@ var addRoutineTaskStepInputSchema = object({
|
|
|
22615
22795
|
var reorderRoutineStepsInputSchema = object({
|
|
22616
22796
|
stepIds: array(ulidSchema).min(1).max(200).refine((ids) => new Set(ids).size === ids.length, "stepIds must not repeat")
|
|
22617
22797
|
}).strict();
|
|
22618
|
-
var routineTodayStepKindSchema = _enum2(["step", "habit", "task"]);
|
|
22798
|
+
var routineTodayStepKindSchema = _enum2(["step", "habit", "task", "routine"]);
|
|
22619
22799
|
var routineTodayStepSchema = object({
|
|
22620
22800
|
n: number2().int().positive(),
|
|
22621
22801
|
title: string2(),
|
|
22622
22802
|
kind: routineTodayStepKindSchema,
|
|
22623
22803
|
meta: string2().nullable(),
|
|
22624
22804
|
minutes: number2().int().nullable(),
|
|
22625
|
-
done: boolean2()
|
|
22805
|
+
done: boolean2(),
|
|
22806
|
+
depth: number2().int().nonnegative().default(0),
|
|
22807
|
+
optional: boolean2().default(false)
|
|
22626
22808
|
});
|
|
22627
22809
|
var routineTodayStateSchema = _enum2(["done", "running", "upcoming", "resting"]);
|
|
22628
22810
|
var routineTodaySchema = object({
|
|
@@ -24009,7 +24191,7 @@ var timeBlockViewSchema = timeBlockSchema.omit({
|
|
|
24009
24191
|
var timeBlockStepSchema = object({
|
|
24010
24192
|
id: string2(),
|
|
24011
24193
|
title: string2(),
|
|
24012
|
-
refKind:
|
|
24194
|
+
refKind: routineStepRefSchema,
|
|
24013
24195
|
status: _enum2(["done", "skipped", "open"]),
|
|
24014
24196
|
at: exports_coerce.date().nullable(),
|
|
24015
24197
|
habitId: string2().nullable().optional()
|
|
@@ -24229,6 +24411,7 @@ var calendarEventLinks = pgTable("calendar_event_links", {
|
|
|
24229
24411
|
connectionId: text("connection_id").notNull().references(() => connections.id, { onDelete: "cascade" }),
|
|
24230
24412
|
nodeId: text("node_id").notNull().references(() => nodes.id, { onDelete: "cascade" }),
|
|
24231
24413
|
purpose: calendarLinkPurpose("purpose").notNull().default("block"),
|
|
24414
|
+
slot: text("slot").notNull().default(""),
|
|
24232
24415
|
externalCalendarId: text("external_calendar_id").notNull(),
|
|
24233
24416
|
externalEventId: text("external_event_id").notNull(),
|
|
24234
24417
|
etag: text("etag"),
|
|
@@ -24237,7 +24420,7 @@ var calendarEventLinks = pgTable("calendar_event_links", {
|
|
|
24237
24420
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
24238
24421
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
24239
24422
|
}, (t) => [
|
|
24240
|
-
unique("calendar_event_links_connection_node_uniq").on(t.connectionId, t.nodeId, t.purpose),
|
|
24423
|
+
unique("calendar_event_links_connection_node_uniq").on(t.connectionId, t.nodeId, t.purpose, t.slot),
|
|
24241
24424
|
unique("calendar_event_links_connection_event_uniq").on(t.connectionId, t.externalEventId),
|
|
24242
24425
|
index("calendar_event_links_tenant_connection_idx").on(t.tenantId, t.connectionId),
|
|
24243
24426
|
index("calendar_event_links_node_idx").on(t.nodeId)
|
|
@@ -24299,7 +24482,8 @@ var calendarConnectionCalendars = pgTable("calendar_connection_calendars", {
|
|
|
24299
24482
|
var calendarEventLinkSchema = createSelectSchema(calendarEventLinks, {
|
|
24300
24483
|
createdAt: exports_coerce.date(),
|
|
24301
24484
|
updatedAt: exports_coerce.date(),
|
|
24302
|
-
lastSyncedAt: exports_coerce.date()
|
|
24485
|
+
lastSyncedAt: exports_coerce.date(),
|
|
24486
|
+
slot: string2().default("")
|
|
24303
24487
|
});
|
|
24304
24488
|
var calendarSyncResultSchema = object({
|
|
24305
24489
|
connectionId: string2(),
|
|
@@ -29876,6 +30060,27 @@ var habitOperations = defineOperations({
|
|
|
29876
30060
|
response: { description: "Today's habits and routines." },
|
|
29877
30061
|
operationId: "habits_today"
|
|
29878
30062
|
},
|
|
30063
|
+
due: {
|
|
30064
|
+
method: "get",
|
|
30065
|
+
path: "/habits/due",
|
|
30066
|
+
scopes: ["habits:read"],
|
|
30067
|
+
tag: "Habits",
|
|
30068
|
+
summary: "Habit due dates in a range",
|
|
30069
|
+
description: "Plan's Month and agendas (V3-22): every growing habit's due days from `from` through `to` (the key's member's civil days, both included, at most 62), by its schedule alone, the same rule the list and today read. A day it rests (paused, away) or before it began isn't one; quit and event-scheduled habits are left out. Each row is `{ habitId, title, date, time, scheduleLabel, daily, streakLabel }`: `time` its exact `HH:MM` or null, `daily` true for a habit due every day, `streakLabel` `N days` for a daily habit and `N in a row` otherwise. Sorted by date, then time.",
|
|
30070
|
+
query: [
|
|
30071
|
+
{ name: "from", description: "The first day (YYYY-MM-DD).", format: "date", required: true },
|
|
30072
|
+
{
|
|
30073
|
+
name: "to",
|
|
30074
|
+
description: "The last day (YYYY-MM-DD), at most 62 days on.",
|
|
30075
|
+
format: "date",
|
|
30076
|
+
required: true
|
|
30077
|
+
}
|
|
30078
|
+
],
|
|
30079
|
+
querySchema: habitDueQuerySchema,
|
|
30080
|
+
status: 200,
|
|
30081
|
+
response: { description: "The habits' due days." },
|
|
30082
|
+
errors: { 400: true }
|
|
30083
|
+
},
|
|
29879
30084
|
getAway: {
|
|
29880
30085
|
method: "get",
|
|
29881
30086
|
path: "/habits/away",
|
|
@@ -30052,8 +30257,9 @@ var habitOperations = defineOperations({
|
|
|
30052
30257
|
scopes: ["habits:write"],
|
|
30053
30258
|
tag: "Habits",
|
|
30054
30259
|
summary: "Pause a habit",
|
|
30055
|
-
description: '"Pause gym till the 26th": the habit rests until `until` (the day it\'s back, after today): off Now, no push, streak and plant wait. Pausing a paused habit moves its end. Returns the refreshed habit with `pause`; fires `habit.updated`.',
|
|
30260
|
+
description: '"Pause gym till the 26th": the habit rests until `until` (the day it\'s back, after today): off Now, no push, streak and plant wait. No body, or `until: null`, pauses open-ended: it rests until `/resume` (`paused` true, `pausedUntil` null). Pausing a paused habit moves its end. Returns the refreshed habit with `pause` and `paused`; fires `habit.updated`.',
|
|
30056
30261
|
body: habitPauseInputSchema,
|
|
30262
|
+
bodyOptional: true,
|
|
30057
30263
|
status: 201,
|
|
30058
30264
|
response: { description: "The refreshed habit." },
|
|
30059
30265
|
errors: {
|
|
@@ -31291,6 +31497,12 @@ var houseValueSchema = object({
|
|
|
31291
31497
|
}).nullable()
|
|
31292
31498
|
});
|
|
31293
31499
|
var upkeepMonthMarkSchema = _enum2(["done", "due", "planned", "late"]).nullable();
|
|
31500
|
+
var upkeepRoutineKindSchema = _enum2(["habit", "series"]);
|
|
31501
|
+
var upkeepSeriesRunSchema = object({
|
|
31502
|
+
year: number2().int(),
|
|
31503
|
+
mark: _enum2(["done", "running", "late"]),
|
|
31504
|
+
taskId: string2()
|
|
31505
|
+
});
|
|
31294
31506
|
var houseUpkeepRoutineSchema = object({
|
|
31295
31507
|
taskId: string2(),
|
|
31296
31508
|
title: string2(),
|
|
@@ -31300,7 +31512,14 @@ var houseUpkeepRoutineSchema = object({
|
|
|
31300
31512
|
months: array(upkeepMonthMarkSchema).length(12),
|
|
31301
31513
|
bookedOn: day.nullable().default(null),
|
|
31302
31514
|
itemState: string2().nullable().default(null),
|
|
31303
|
-
certified: boolean2().default(false)
|
|
31515
|
+
certified: boolean2().default(false),
|
|
31516
|
+
kind: upkeepRoutineKindSchema.default("habit"),
|
|
31517
|
+
placeTitle: string2().nullable().default(null),
|
|
31518
|
+
cadenceWords: string2().default(""),
|
|
31519
|
+
nextOn: day.nullable().default(null),
|
|
31520
|
+
nextWords: string2().default(""),
|
|
31521
|
+
late: boolean2().default(false),
|
|
31522
|
+
seriesRuns: array(upkeepSeriesRunSchema).default([])
|
|
31304
31523
|
});
|
|
31305
31524
|
var houseUpkeepNeedSchema = object({
|
|
31306
31525
|
id: string2(),
|
|
@@ -31317,6 +31536,12 @@ var houseUpkeepYearSchema = object({
|
|
|
31317
31536
|
year: number2().int(),
|
|
31318
31537
|
currentMonth: number2().int().min(1).max(12).nullable(),
|
|
31319
31538
|
routines: array(houseUpkeepRoutineSchema),
|
|
31539
|
+
counts: object({
|
|
31540
|
+
all: number2().int().min(0),
|
|
31541
|
+
habits: number2().int().min(0),
|
|
31542
|
+
series: number2().int().min(0),
|
|
31543
|
+
late: number2().int().min(0)
|
|
31544
|
+
}).default({ all: 0, habits: 0, series: 0, late: 0 }),
|
|
31320
31545
|
needsMonth: number2().int().min(1).max(12),
|
|
31321
31546
|
needs: array(houseUpkeepNeedSchema),
|
|
31322
31547
|
spent: object({
|
|
@@ -33148,6 +33373,7 @@ var apiKeys = pgTable("api_keys", {
|
|
|
33148
33373
|
scopes: text("scopes").array().notNull().default([]),
|
|
33149
33374
|
lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
|
|
33150
33375
|
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
33376
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
|
33151
33377
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
33152
33378
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
33153
33379
|
}, (t) => [index("api_keys_tenant_idx").on(t.tenantId)]);
|
|
@@ -33244,6 +33470,22 @@ var API_KEY_SCOPE_VALUES = [
|
|
|
33244
33470
|
"private:*",
|
|
33245
33471
|
"*"
|
|
33246
33472
|
];
|
|
33473
|
+
var API_TOKEN_SECTION_VALUES = [
|
|
33474
|
+
"today",
|
|
33475
|
+
"plan",
|
|
33476
|
+
"work",
|
|
33477
|
+
"habits",
|
|
33478
|
+
"journal",
|
|
33479
|
+
"home",
|
|
33480
|
+
"food",
|
|
33481
|
+
"collection",
|
|
33482
|
+
"money",
|
|
33483
|
+
"health",
|
|
33484
|
+
"people",
|
|
33485
|
+
"animals",
|
|
33486
|
+
"agents",
|
|
33487
|
+
"settings"
|
|
33488
|
+
];
|
|
33247
33489
|
var FULL_ACCESS_SCOPE = "*";
|
|
33248
33490
|
var CONNECTIONS_WRITE_SCOPE = "connections:write";
|
|
33249
33491
|
function scopeGranted(granted, required) {
|
|
@@ -33263,16 +33505,25 @@ var apiKeyScopesInputSchema = array(apiKeyScopeSchema).refine((scopes) => !scope
|
|
|
33263
33505
|
});
|
|
33264
33506
|
var apiKeySchema = createSelectSchema(apiKeys, {
|
|
33265
33507
|
scopes: array(apiKeyScopeSchema),
|
|
33508
|
+
expiresAt: exports_coerce.date().nullable().default(null),
|
|
33266
33509
|
agentId: string2().nullable().default(null),
|
|
33267
33510
|
lastUsedAt: exports_coerce.date().nullable(),
|
|
33268
33511
|
revokedAt: exports_coerce.date().nullable(),
|
|
33269
33512
|
createdAt: exports_coerce.date(),
|
|
33270
33513
|
updatedAt: exports_coerce.date()
|
|
33271
33514
|
});
|
|
33272
|
-
var
|
|
33515
|
+
var apiKeySectionAccessSchema = object({
|
|
33516
|
+
section: _enum2(API_TOKEN_SECTION_VALUES),
|
|
33517
|
+
access: _enum2(["read", "write"])
|
|
33518
|
+
}).strict();
|
|
33519
|
+
var apiKeyPublicSchema = apiKeySchema.omit({ keyHash: true }).extend({
|
|
33520
|
+
sections: array(apiKeySectionAccessSchema).default([]),
|
|
33521
|
+
scopeLine: string2().default("")
|
|
33522
|
+
});
|
|
33273
33523
|
var createApiKeyInputSchema = createInsertSchema(apiKeys, {
|
|
33274
33524
|
name: string2().min(1).max(120),
|
|
33275
33525
|
scopes: apiKeyScopesInputSchema.optional(),
|
|
33526
|
+
expiresAt: exports_coerce.date().nullish(),
|
|
33276
33527
|
agentId: ulidSchema.nullish()
|
|
33277
33528
|
}).omit({
|
|
33278
33529
|
id: true,
|
|
@@ -33284,7 +33535,27 @@ var createApiKeyInputSchema = createInsertSchema(apiKeys, {
|
|
|
33284
33535
|
revokedAt: true,
|
|
33285
33536
|
createdAt: true,
|
|
33286
33537
|
updatedAt: true
|
|
33287
|
-
}).
|
|
33538
|
+
}).extend({
|
|
33539
|
+
sections: array(apiKeySectionAccessSchema).min(1).max(API_TOKEN_SECTION_VALUES.length).optional()
|
|
33540
|
+
}).strict().refine((v) => v.scopes === undefined || v.sections === undefined, {
|
|
33541
|
+
message: "give scopes or sections, not both",
|
|
33542
|
+
path: ["sections"]
|
|
33543
|
+
}).refine((v) => !v.expiresAt || v.expiresAt.getTime() > Date.now(), {
|
|
33544
|
+
message: "an expiry must be in the future",
|
|
33545
|
+
path: ["expiresAt"]
|
|
33546
|
+
});
|
|
33547
|
+
var createKeyFromKeyInputSchema = object({
|
|
33548
|
+
name: string2().min(1).max(120),
|
|
33549
|
+
scopes: apiKeyScopesInputSchema.optional(),
|
|
33550
|
+
sections: array(apiKeySectionAccessSchema).min(1).max(API_TOKEN_SECTION_VALUES.length).optional(),
|
|
33551
|
+
expiresAt: exports_coerce.date().nullish()
|
|
33552
|
+
}).strict().refine((v) => v.scopes === undefined || v.sections === undefined, {
|
|
33553
|
+
message: "give scopes or sections, not both",
|
|
33554
|
+
path: ["sections"]
|
|
33555
|
+
}).refine((v) => !v.expiresAt || v.expiresAt.getTime() > Date.now(), {
|
|
33556
|
+
message: "an expiry must be in the future",
|
|
33557
|
+
path: ["expiresAt"]
|
|
33558
|
+
});
|
|
33288
33559
|
var createApiKeyResultSchema = apiKeyPublicSchema.extend({ key: string2() });
|
|
33289
33560
|
var updateApiKeyInputSchema = object({
|
|
33290
33561
|
name: string2().min(1).max(120).optional(),
|
|
@@ -33330,6 +33601,20 @@ var keyOperations = defineOperations({
|
|
|
33330
33601
|
response: { description: "The keys." },
|
|
33331
33602
|
errors: { 403: true }
|
|
33332
33603
|
},
|
|
33604
|
+
create: {
|
|
33605
|
+
method: "post",
|
|
33606
|
+
path: "/keys",
|
|
33607
|
+
scopes: ["keys:write"],
|
|
33608
|
+
tag: "Identity",
|
|
33609
|
+
summary: "Mint an API key",
|
|
33610
|
+
description: 'A key for the member behind this key, by `sections` (each `read` or `write`: Today/Inbox, Plan, Work, Habits, Journal, Home, Food, Collection, Money, Health, People, Animals, Agents, Settings; the server keeps the scopes they stand for) or by `scopes`, not both, with an optional `expiresAt` (null never expires; past it the key gets 401 "This token expired on <date>"). Never more than the calling key holds (403), never an Agent\'s key. The plaintext `key` is in the answer once; the key carries its `scopeLine` ("Work, Plan: read and write").',
|
|
33611
|
+
body: createKeyFromKeyInputSchema,
|
|
33612
|
+
status: 201,
|
|
33613
|
+
response: { description: "The key, with its one-time secret." },
|
|
33614
|
+
errors: { 400: true, 403: true },
|
|
33615
|
+
throttle: "strict",
|
|
33616
|
+
mcp: false
|
|
33617
|
+
},
|
|
33333
33618
|
update: {
|
|
33334
33619
|
method: "patch",
|
|
33335
33620
|
path: "/keys/{id}",
|
|
@@ -33892,19 +34177,37 @@ var mailCampaignOperations = defineOperations({
|
|
|
33892
34177
|
});
|
|
33893
34178
|
|
|
33894
34179
|
// ../../packages/schema/src/contracts/meal-plans.ts
|
|
34180
|
+
var mealPlanStateSchema = _enum2(MEAL_PLAN_STATES);
|
|
34181
|
+
var civilDate2 = string2().regex(/^\d{4}-\d{2}-\d{2}$/, "a date is YYYY-MM-DD");
|
|
34182
|
+
var weekMenuFields = {
|
|
34183
|
+
state: mealPlanStateSchema.optional(),
|
|
34184
|
+
cookUserId: ulidSchema.nullish(),
|
|
34185
|
+
company: string2().max(120).nullish(),
|
|
34186
|
+
leftoverPortions: number2().int().positive().nullish(),
|
|
34187
|
+
leftoverEatBy: civilDate2.nullish(),
|
|
34188
|
+
leftoverFrozen: boolean2().optional()
|
|
34189
|
+
};
|
|
33895
34190
|
var mealPlanSchema = createSelectSchema(mealPlans, {
|
|
33896
|
-
plannedFor: exports_coerce.date()
|
|
34191
|
+
plannedFor: exports_coerce.date(),
|
|
34192
|
+
state: mealPlanStateSchema.default("planned"),
|
|
34193
|
+
cookUserId: string2().nullable().default(null),
|
|
34194
|
+
company: string2().nullable().default(null),
|
|
34195
|
+
leftoverPortions: number2().int().nullable().default(null),
|
|
34196
|
+
leftoverEatBy: string2().nullable().default(null),
|
|
34197
|
+
leftoverFrozen: boolean2().default(false)
|
|
33897
34198
|
});
|
|
33898
34199
|
var createMealPlanInputSchema = createInsertSchema(mealPlans, {
|
|
33899
34200
|
id: ulidSchema,
|
|
33900
34201
|
recipeId: ulidSchema.nullish(),
|
|
33901
34202
|
plannedFor: exports_coerce.date(),
|
|
33902
|
-
servings: number2().int().positive().nullish()
|
|
34203
|
+
servings: number2().int().positive().nullish(),
|
|
34204
|
+
...weekMenuFields
|
|
33903
34205
|
}).omit({ tenantId: true }).extend({ title: string2().min(1) });
|
|
33904
34206
|
var updateMealPlanInputSchema = createUpdateSchema(mealPlans, {
|
|
33905
34207
|
recipeId: ulidSchema.nullish(),
|
|
33906
34208
|
plannedFor: exports_coerce.date().optional(),
|
|
33907
|
-
servings: number2().int().positive().nullish()
|
|
34209
|
+
servings: number2().int().positive().nullish(),
|
|
34210
|
+
...weekMenuFields
|
|
33908
34211
|
}).omit({ id: true, tenantId: true }).extend({ title: string2().min(1).optional() }).strict();
|
|
33909
34212
|
var mealPlanViewSchema = mealPlanSchema.extend({
|
|
33910
34213
|
title: string2(),
|
|
@@ -33914,6 +34217,10 @@ var mealPlanViewSchema = mealPlanSchema.extend({
|
|
|
33914
34217
|
deletedAt: exports_coerce.date().nullable(),
|
|
33915
34218
|
recipeTitle: string2().nullable()
|
|
33916
34219
|
});
|
|
34220
|
+
var mealPlanListQuerySchema = object({
|
|
34221
|
+
recipeId: ulidSchema.optional(),
|
|
34222
|
+
state: mealPlanStateSchema.optional()
|
|
34223
|
+
});
|
|
33917
34224
|
var generateGroceryListInputSchema = object({
|
|
33918
34225
|
id: ulidSchema,
|
|
33919
34226
|
title: string2().min(1),
|
|
@@ -33935,6 +34242,59 @@ var mealPlanCookResultSchema = object({
|
|
|
33935
34242
|
consumedCount: number2().int().min(0),
|
|
33936
34243
|
ingredients: array(cookedIngredientSchema)
|
|
33937
34244
|
});
|
|
34245
|
+
var cookMealPlanInputSchema = object({
|
|
34246
|
+
leftoverPortions: number2().int().positive().nullish(),
|
|
34247
|
+
leftoverEatBy: civilDate2.nullish()
|
|
34248
|
+
}).strict();
|
|
34249
|
+
var mealWeekQuerySchema = object({ start: civilDate2.optional() });
|
|
34250
|
+
var acceptMealDraftInputSchema = object({ start: civilDate2 }).strict();
|
|
34251
|
+
var mealWeekCookSchema = object({
|
|
34252
|
+
userId: string2(),
|
|
34253
|
+
name: string2(),
|
|
34254
|
+
initial: string2()
|
|
34255
|
+
});
|
|
34256
|
+
var mealWeekSlotSchema = object({
|
|
34257
|
+
id: ulidSchema,
|
|
34258
|
+
title: string2(),
|
|
34259
|
+
recipeId: ulidSchema.nullable(),
|
|
34260
|
+
state: mealPlanStateSchema,
|
|
34261
|
+
cook: mealWeekCookSchema.nullable(),
|
|
34262
|
+
company: string2().nullable(),
|
|
34263
|
+
notes: string2().nullable(),
|
|
34264
|
+
time: string2().nullable()
|
|
34265
|
+
});
|
|
34266
|
+
var mealWeekDaySchema = object({
|
|
34267
|
+
date: civilDate2,
|
|
34268
|
+
isToday: boolean2(),
|
|
34269
|
+
dinner: mealWeekSlotSchema.nullable(),
|
|
34270
|
+
lunch: mealWeekSlotSchema.nullable()
|
|
34271
|
+
});
|
|
34272
|
+
var mealWeekLeftoverSchema = object({
|
|
34273
|
+
mealPlanId: ulidSchema,
|
|
34274
|
+
title: string2(),
|
|
34275
|
+
portions: number2().int(),
|
|
34276
|
+
cookedOn: civilDate2,
|
|
34277
|
+
eatBy: civilDate2.nullable(),
|
|
34278
|
+
frozen: boolean2()
|
|
34279
|
+
});
|
|
34280
|
+
var mealWeekDraftDaySchema = object({
|
|
34281
|
+
date: civilDate2,
|
|
34282
|
+
dinner: mealWeekSlotSchema.nullable(),
|
|
34283
|
+
why: string2().nullable()
|
|
34284
|
+
});
|
|
34285
|
+
var mealWeekSchema = object({
|
|
34286
|
+
week: number2().int().min(1).max(53),
|
|
34287
|
+
start: civilDate2,
|
|
34288
|
+
end: civilDate2,
|
|
34289
|
+
days: array(mealWeekDaySchema).length(7),
|
|
34290
|
+
summary: object({
|
|
34291
|
+
planned: number2().int().min(0),
|
|
34292
|
+
cooked: number2().int().min(0),
|
|
34293
|
+
out: number2().int().min(0)
|
|
34294
|
+
}),
|
|
34295
|
+
leftovers: array(mealWeekLeftoverSchema),
|
|
34296
|
+
draft: array(mealWeekDraftDaySchema).length(7)
|
|
34297
|
+
});
|
|
33938
34298
|
|
|
33939
34299
|
// ../../packages/schema/src/operations/meal-plans.ts
|
|
33940
34300
|
var mealPlanOperations = defineOperations({
|
|
@@ -33945,8 +34305,43 @@ var mealPlanOperations = defineOperations({
|
|
|
33945
34305
|
read: "meals:read",
|
|
33946
34306
|
write: "meals:write",
|
|
33947
34307
|
create: createMealPlanInputSchema,
|
|
33948
|
-
update: updateMealPlanInputSchema
|
|
33949
|
-
|
|
34308
|
+
update: updateMealPlanInputSchema,
|
|
34309
|
+
listQuery: [
|
|
34310
|
+
{ name: "recipeId", description: "Only the plans of this recipe (its ULID)." },
|
|
34311
|
+
{
|
|
34312
|
+
name: "state",
|
|
34313
|
+
description: "Only plans in this state (`planned`, `cooked`, `out`, `unsure`, `draft`); `cooked` with `recipeId` is who cooked a recipe when."
|
|
34314
|
+
}
|
|
34315
|
+
],
|
|
34316
|
+
listQuerySchema: mealPlanListQuerySchema
|
|
34317
|
+
}),
|
|
34318
|
+
week: {
|
|
34319
|
+
method: "get",
|
|
34320
|
+
path: "/meal-plans/week",
|
|
34321
|
+
scopes: ["meals:read"],
|
|
34322
|
+
tag: "Meals",
|
|
34323
|
+
summary: "Show the week menu",
|
|
34324
|
+
description: "The week (Monday to Sunday) from `start` (any day is moved to its Monday; this week by default): each day's dinner and lunch with its state (planned \xB7 cooked \xB7 out \xB7 unsure), who cooks and the company of a meal out; the dinner counts (planned, cooked, out); the leftovers still to eat; and next week's `draft` dinners, each with its one-line why (the draft plan's notes).",
|
|
34325
|
+
query: [{ name: "start", description: "A day of the week, YYYY-MM-DD.", format: "date" }],
|
|
34326
|
+
querySchema: mealWeekQuerySchema,
|
|
34327
|
+
status: 200,
|
|
34328
|
+
response: { description: "The week menu." },
|
|
34329
|
+
errors: { 400: true },
|
|
34330
|
+
operationId: "meal_week"
|
|
34331
|
+
},
|
|
34332
|
+
acceptDraft: {
|
|
34333
|
+
method: "post",
|
|
34334
|
+
path: "/meal-plans/week/accept-draft",
|
|
34335
|
+
scopes: ["meals:write"],
|
|
34336
|
+
tag: "Meals",
|
|
34337
|
+
summary: "Accept a week's draft",
|
|
34338
|
+
description: "Turn every `draft` meal plan in the week from `start` into a `planned` one and return that week's menu. Accepting a week with no drafts left changes nothing.",
|
|
34339
|
+
body: acceptMealDraftInputSchema,
|
|
34340
|
+
status: 200,
|
|
34341
|
+
response: { description: "The week menu, its drafts planned." },
|
|
34342
|
+
errors: { 400: true },
|
|
34343
|
+
operationId: "meal_week_accept_draft"
|
|
34344
|
+
}
|
|
33950
34345
|
});
|
|
33951
34346
|
|
|
33952
34347
|
// ../../packages/schema/src/operations/members.ts
|
|
@@ -37554,7 +37949,8 @@ var recipeLibrarySchema = object({
|
|
|
37554
37949
|
recipes: array(object({
|
|
37555
37950
|
id: ulidSchema,
|
|
37556
37951
|
lastCookedAt: exports_coerce.date().nullable(),
|
|
37557
|
-
ingredients: array(string2())
|
|
37952
|
+
ingredients: array(string2()),
|
|
37953
|
+
cookedCount: number2().int().min(0).optional()
|
|
37558
37954
|
})),
|
|
37559
37955
|
menu: array(object({
|
|
37560
37956
|
planId: ulidSchema,
|
|
@@ -37563,7 +37959,9 @@ var recipeLibrarySchema = object({
|
|
|
37563
37959
|
plannedFor: exports_coerce.date(),
|
|
37564
37960
|
mealType: mealTypeSchema,
|
|
37565
37961
|
cooked: boolean2(),
|
|
37566
|
-
onList: array(string2())
|
|
37962
|
+
onList: array(string2()),
|
|
37963
|
+
state: mealPlanStateSchema.optional(),
|
|
37964
|
+
cook: mealWeekCookSchema.nullable().optional()
|
|
37567
37965
|
}))
|
|
37568
37966
|
});
|
|
37569
37967
|
var recipeIngredientMatchInputSchema = object({ foodId: string2().min(1) }).strict();
|
|
@@ -39039,6 +39437,9 @@ var PILL_PAGES_MAX = 4;
|
|
|
39039
39437
|
var CALENDAR_ZOOM_MIN = 75;
|
|
39040
39438
|
var CALENDAR_ZOOM_MAX = 300;
|
|
39041
39439
|
var calendarZoomSchema = number2().int().min(CALENDAR_ZOOM_MIN).max(CALENDAR_ZOOM_MAX);
|
|
39440
|
+
var settingsGlassSchema = _enum2(settingsGlass.enumValues);
|
|
39441
|
+
var themePackSchema = _enum2(settingsThemePack.enumValues);
|
|
39442
|
+
var themePackScopeSchema = _enum2(settingsThemePackScope.enumValues);
|
|
39042
39443
|
var userSettingsSchema = createSelectSchema(userSettings, {
|
|
39043
39444
|
pinnedAreaIds: array(string2()).default([]),
|
|
39044
39445
|
focusAreaId: string2().nullable().default(null),
|
|
@@ -39049,6 +39450,14 @@ var userSettingsSchema = createSelectSchema(userSettings, {
|
|
|
39049
39450
|
pillPages: array(string2()).default([]).transform((ids) => ids.filter((id) => pillPageSchema.safeParse(id).success)),
|
|
39050
39451
|
foodDiaryMode: boolean2().default(false),
|
|
39051
39452
|
calendarZoom: number2().int().nullable().default(null),
|
|
39453
|
+
glass: settingsGlassSchema.default("liquid"),
|
|
39454
|
+
followSky: boolean2().default(true),
|
|
39455
|
+
nightUntilWake: boolean2().default(false),
|
|
39456
|
+
largerText: boolean2().default(false),
|
|
39457
|
+
reduceMotion: boolean2().default(false),
|
|
39458
|
+
showStreakNumbers: boolean2().default(true),
|
|
39459
|
+
themePack: themePackSchema.default("do"),
|
|
39460
|
+
themePackScope: themePackScopeSchema.default("section"),
|
|
39052
39461
|
createdAt: exports_coerce.date(),
|
|
39053
39462
|
updatedAt: exports_coerce.date()
|
|
39054
39463
|
}).omit({ autoRollOverdue: true });
|
|
@@ -39068,7 +39477,15 @@ var updateUserSettingsInputSchema = createUpdateSchema(userSettings, {
|
|
|
39068
39477
|
quietHours: quietHoursSchema.nullish(),
|
|
39069
39478
|
lifeBlockSharing: lifeBlockSharingSchema.nullish(),
|
|
39070
39479
|
calendarZoom: calendarZoomSchema.nullish(),
|
|
39071
|
-
pillPages: array(pillPageSchema).transform((ids) => [...new Set(ids)]).pipe(array(pillPageSchema).max(PILL_PAGES_MAX)).optional()
|
|
39480
|
+
pillPages: array(pillPageSchema).transform((ids) => [...new Set(ids)]).pipe(array(pillPageSchema).max(PILL_PAGES_MAX)).optional(),
|
|
39481
|
+
glass: settingsGlassSchema.optional(),
|
|
39482
|
+
followSky: boolean2().optional(),
|
|
39483
|
+
nightUntilWake: boolean2().optional(),
|
|
39484
|
+
largerText: boolean2().optional(),
|
|
39485
|
+
reduceMotion: boolean2().optional(),
|
|
39486
|
+
showStreakNumbers: boolean2().optional(),
|
|
39487
|
+
themePack: themePackSchema.optional(),
|
|
39488
|
+
themePackScope: themePackScopeSchema.optional()
|
|
39072
39489
|
}).omit({
|
|
39073
39490
|
id: true,
|
|
39074
39491
|
tenantId: true,
|
|
@@ -39528,6 +39945,66 @@ var taskBlockOperations = defineOperations({
|
|
|
39528
39945
|
}
|
|
39529
39946
|
});
|
|
39530
39947
|
|
|
39948
|
+
// ../../packages/schema/src/operations/task-series.ts
|
|
39949
|
+
var taskSeriesOperations = defineOperations({
|
|
39950
|
+
make: {
|
|
39951
|
+
method: "post",
|
|
39952
|
+
path: "/tasks/{id}/make-series",
|
|
39953
|
+
scopes: ["tasks:write"],
|
|
39954
|
+
tag: "Tasks",
|
|
39955
|
+
summary: "Make a task the first of a series",
|
|
39956
|
+
description: "The task (by id or ref, `DO-150`) becomes slot 0 of a new series on `rule` (every N weeks, months or years; a quarter is 3 months), its Deadline (else its planned day, else today) the first slot. Its Area, Project, labels, priority, estimate, Owner and sub-task titles are the template each next task copies; the next is made `leadDays` before its slot and named by `occurrenceLabel` (`quarter`, `month`, `date`). A task already in a series is a 409.",
|
|
39957
|
+
body: makeTaskSeriesInputSchema,
|
|
39958
|
+
status: 201,
|
|
39959
|
+
response: { description: "The series, with its strip." },
|
|
39960
|
+
errors: { 400: true, 404: true, 409: true }
|
|
39961
|
+
},
|
|
39962
|
+
list: {
|
|
39963
|
+
method: "get",
|
|
39964
|
+
path: "/task-series",
|
|
39965
|
+
scopes: ["tasks:read"],
|
|
39966
|
+
tag: "Tasks",
|
|
39967
|
+
summary: "List task series",
|
|
39968
|
+
description: "The Space's task series, stopped ones too: each with its rhythm in words, its settings, how many tasks it made and its strip (up to two past occurrences, the latest, and the next with the day it's made).",
|
|
39969
|
+
status: 200,
|
|
39970
|
+
response: { description: "The series." }
|
|
39971
|
+
},
|
|
39972
|
+
get: {
|
|
39973
|
+
method: "get",
|
|
39974
|
+
path: "/task-series/{id}",
|
|
39975
|
+
scopes: ["tasks:read"],
|
|
39976
|
+
tag: "Tasks",
|
|
39977
|
+
summary: "Show a task series",
|
|
39978
|
+
description: "One series with its settings and its strip.",
|
|
39979
|
+
status: 200,
|
|
39980
|
+
response: { description: "The series." },
|
|
39981
|
+
errors: { 404: true }
|
|
39982
|
+
},
|
|
39983
|
+
update: {
|
|
39984
|
+
method: "patch",
|
|
39985
|
+
path: "/task-series/{id}",
|
|
39986
|
+
scopes: ["tasks:write"],
|
|
39987
|
+
tag: "Tasks",
|
|
39988
|
+
summary: "Update a task series",
|
|
39989
|
+
description: "Rename it, change its rhythm, naming or lead days, or stop (`active: false`) and resume it.",
|
|
39990
|
+
body: updateTaskSeriesInputSchema,
|
|
39991
|
+
status: 200,
|
|
39992
|
+
response: { description: "The series." },
|
|
39993
|
+
errors: { 400: true, 404: true }
|
|
39994
|
+
},
|
|
39995
|
+
stop: {
|
|
39996
|
+
method: "delete",
|
|
39997
|
+
path: "/task-series/{id}",
|
|
39998
|
+
scopes: ["tasks:write"],
|
|
39999
|
+
tag: "Tasks",
|
|
40000
|
+
summary: "Stop a task series",
|
|
40001
|
+
description: "No more tasks are made; every task it made stays.",
|
|
40002
|
+
status: 204,
|
|
40003
|
+
response: { description: "Stopped." },
|
|
40004
|
+
errors: { 404: true }
|
|
40005
|
+
}
|
|
40006
|
+
});
|
|
40007
|
+
|
|
39531
40008
|
// ../../packages/schema/src/operations/tasks.ts
|
|
39532
40009
|
var taskListQuery = taskListQuerySchema.extend(paginationQuerySchema.shape).extend({ filter: taskListFilterSchema.optional().default("all") });
|
|
39533
40010
|
var taskOperations = defineOperations({
|
|
@@ -39825,6 +40302,67 @@ var todayOperations = defineOperations({
|
|
|
39825
40302
|
}
|
|
39826
40303
|
});
|
|
39827
40304
|
|
|
40305
|
+
// ../../packages/schema/src/contracts/waste-calendar.ts
|
|
40306
|
+
var WASTE_KIND_VALUES = ["rest", "pmd", "paper", "garden", "glass", "other"];
|
|
40307
|
+
var wasteKindSchema = _enum2(WASTE_KIND_VALUES);
|
|
40308
|
+
var civilDate3 = exports_iso.date();
|
|
40309
|
+
var wasteCollectionSchema = object({
|
|
40310
|
+
date: civilDate3,
|
|
40311
|
+
kinds: array(wasteKindSchema).min(1),
|
|
40312
|
+
movedBy: string2().min(1).optional()
|
|
40313
|
+
});
|
|
40314
|
+
var wasteCalendarConnectInputSchema = object({
|
|
40315
|
+
feedUrl: url({ protocol: /^https?$/ }).max(2000)
|
|
40316
|
+
}).strict();
|
|
40317
|
+
var wasteCalendarNextQuerySchema = object({
|
|
40318
|
+
limit: exports_coerce.number().int().min(1).max(12).default(4)
|
|
40319
|
+
});
|
|
40320
|
+
var wasteCalendarNextItemSchema = wasteCollectionSchema.extend({
|
|
40321
|
+
kindsWords: string2().min(1),
|
|
40322
|
+
binsOutAt: string2().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/)
|
|
40323
|
+
});
|
|
40324
|
+
var wasteCalendarNextSchema = object({
|
|
40325
|
+
connected: boolean2(),
|
|
40326
|
+
collections: array(wasteCalendarNextItemSchema),
|
|
40327
|
+
lastSyncAt: exports_coerce.date().nullable(),
|
|
40328
|
+
lastSyncLine: string2().nullable(),
|
|
40329
|
+
lastError: string2().nullable()
|
|
40330
|
+
});
|
|
40331
|
+
|
|
40332
|
+
// ../../packages/schema/src/operations/waste-calendar.ts
|
|
40333
|
+
var CONNECT3 = ["connections:write"];
|
|
40334
|
+
var AFTER3 = "`*` and `private:*` never grant `connections:write`: a key holds it by name, and a guest's key never does.";
|
|
40335
|
+
var wasteCalendarOperations = defineOperations({
|
|
40336
|
+
connect: {
|
|
40337
|
+
method: "post",
|
|
40338
|
+
path: "/integrations/waste-calendar",
|
|
40339
|
+
scopes: CONNECT3,
|
|
40340
|
+
afterScopes: AFTER3,
|
|
40341
|
+
tag: "Connections",
|
|
40342
|
+
summary: "Connect the waste calendar",
|
|
40343
|
+
description: "Keep the household's waste-collection iCal `feedUrl` (the municipality's or the waste app's), sealed, and read it now; it's read again every morning at 06:00. Connecting again replaces the URL. The URL is never returned. Answers what's next, as `next` does: a feed that can't be read still connects, with `lastError` saying why.",
|
|
40344
|
+
body: wasteCalendarConnectInputSchema,
|
|
40345
|
+
status: 200,
|
|
40346
|
+
response: { description: "The next collections." },
|
|
40347
|
+
errors: { 400: true, 403: true },
|
|
40348
|
+
throttle: "strict",
|
|
40349
|
+
mcp: false
|
|
40350
|
+
},
|
|
40351
|
+
next: {
|
|
40352
|
+
method: "get",
|
|
40353
|
+
path: "/integrations/waste-calendar/next",
|
|
40354
|
+
scopes: ["settings:read"],
|
|
40355
|
+
tag: "Connections",
|
|
40356
|
+
summary: "The next waste collections",
|
|
40357
|
+
description: 'The next `limit` (1\u201312, 4 by default) collection days from today in the connecting member\'s zone: each its `date`, `kinds` (`rest`, `pmd`, `paper`, `garden`, `glass`, `other`), `kindsWords` ("Rest and PMD", "Garden, moved by the holiday"), `movedBy` when the feed says why it moved, and `binsOutAt` (the evening before at 20:00, local). With `lastSyncAt`, `lastSyncLine` ("Today 06:00, every morning") and the last read\'s `lastError`; `connected: false` with none set up.',
|
|
40358
|
+
query: [{ name: "limit", description: "How many collection days (1\u201312, default 4)." }],
|
|
40359
|
+
querySchema: wasteCalendarNextQuerySchema,
|
|
40360
|
+
status: 200,
|
|
40361
|
+
response: { description: "The next collections." },
|
|
40362
|
+
errors: { 403: true }
|
|
40363
|
+
}
|
|
40364
|
+
});
|
|
40365
|
+
|
|
39828
40366
|
// ../../packages/schema/src/db/webhooks.ts
|
|
39829
40367
|
var webhookSubscriptions = pgTable("webhook_subscriptions", {
|
|
39830
40368
|
id: text("id").primaryKey(),
|
|
@@ -40805,6 +41343,14 @@ function addMonths(d, months) {
|
|
|
40805
41343
|
var TAG_RE = /(?:^|\s)#([\p{L}\p{N}][\p{L}\p{N}_-]*)/gu;
|
|
40806
41344
|
var STAR_RE = /(?:^|\s)!(star|flag|s|f)?(?=\s|$)/giu;
|
|
40807
41345
|
var AREA_RE = /(?:^|\s)~([\p{L}\p{N}][\p{L}\p{N}_-]*)/gu;
|
|
41346
|
+
var PRIORITY_RE = /(?:^|\s)(?:p([123])|!(high|medium|low))(?=\s|$)/giu;
|
|
41347
|
+
var PRIORITY_OF_P = { "1": "high", "2": "medium", "3": "low" };
|
|
41348
|
+
function capturePriority(token) {
|
|
41349
|
+
const m = /^(?:p([123])|!(high|medium|low))$/iu.exec(token.trim());
|
|
41350
|
+
if (!m)
|
|
41351
|
+
return null;
|
|
41352
|
+
return m[1] ? PRIORITY_OF_P[m[1]] ?? null : m[2]?.toLowerCase();
|
|
41353
|
+
}
|
|
40808
41354
|
function parseCapture(input, now) {
|
|
40809
41355
|
const tags = [];
|
|
40810
41356
|
for (const match of input.matchAll(TAG_RE)) {
|
|
@@ -40825,7 +41371,12 @@ function parseCapture(input, now) {
|
|
|
40825
41371
|
break;
|
|
40826
41372
|
}
|
|
40827
41373
|
}
|
|
40828
|
-
let
|
|
41374
|
+
let priority;
|
|
41375
|
+
for (const match of input.matchAll(PRIORITY_RE)) {
|
|
41376
|
+
priority = capturePriority(match[0]) ?? undefined;
|
|
41377
|
+
break;
|
|
41378
|
+
}
|
|
41379
|
+
let stripped = blankMatches(input, [TAG_RE, STAR_RE, AREA_RE, PRIORITY_RE]);
|
|
40829
41380
|
const when = now ? parseWhen(stripped, now) : undefined;
|
|
40830
41381
|
if (when) {
|
|
40831
41382
|
for (const span of [...when.spans].sort((a, b) => b.start - a.start)) {
|
|
@@ -40838,6 +41389,7 @@ function parseCapture(input, now) {
|
|
|
40838
41389
|
tags,
|
|
40839
41390
|
...starred ? { starred } : {},
|
|
40840
41391
|
...area ? { area } : {},
|
|
41392
|
+
...priority ? { priority } : {},
|
|
40841
41393
|
...when?.scheduledAt ? { scheduledAt: when.scheduledAt, allDay: when.allDay } : {},
|
|
40842
41394
|
...when?.dueAt ? { dueAt: when.dueAt, ...when.scheduledAt ? {} : { allDay: when.dueAllDay } } : {},
|
|
40843
41395
|
...when?.durationMinutes ? { durationMinutes: when.durationMinutes } : {},
|
|
@@ -41532,7 +42084,7 @@ function renderHabit(data, mode) {
|
|
|
41532
42084
|
const pct = typeof streak?.completionRate === "number" ? ` \xB7 ${Math.round(streak.completionRate * 100)}% lately` : "";
|
|
41533
42085
|
const recovering = streak?.state === "recovering" ? paint(mode, "yellow", " \xB7 recovering") : "";
|
|
41534
42086
|
const counts = streak ? ` \xB7 streak ${streak.current ?? 0} (best ${streak.longest ?? 0})` : "";
|
|
41535
|
-
const rest = h.pause ? ` \xB7 ${h.pause.by === "away" ? "away" : "paused"}
|
|
42087
|
+
const rest = h.pause ? ` \xB7 ${h.pause.by === "away" ? "away" : "paused"}${h.pause.until ? `, back ${h.pause.until}` : " until resumed"}` : streak?.skippedToday ? " \xB7 rest day" : "";
|
|
41536
42088
|
const last = h.lastCheckIn?.id ? `
|
|
41537
42089
|
${paint(mode, "dim", `last check-in ${h.lastCheckIn.id}`)}${h.lastCheckIn.note ? ` "${h.lastCheckIn.note}"` : ""}` : "";
|
|
41538
42090
|
return `${h.title ?? "(habit)"} \u2014 ${flame}${pct}${counts}${recovering}${rest}
|
|
@@ -41574,10 +42126,11 @@ function renderKeys(data, mode) {
|
|
|
41574
42126
|
return paint(mode, "dim", "(no keys)");
|
|
41575
42127
|
return keys.map((key) => {
|
|
41576
42128
|
const scopes = key.scopes ?? [];
|
|
41577
|
-
const reach = sayFullAccess(scopes)
|
|
42129
|
+
const reach = key.scopeLine || sayFullAccess(scopes) || scopes.join(", ") || "(no scopes)";
|
|
42130
|
+
const expires = key.expiresAt ? paint(mode, "dim", ` \xB7 expires ${key.expiresAt.slice(0, 10)}`) : "";
|
|
41578
42131
|
const changed = key.changes?.[0] ? paint(mode, "dim", ` \xB7 changed ${key.changes[0].at.slice(0, 10)}`) : "";
|
|
41579
42132
|
return `${key.name} ${paint(mode, "dim", key.id)}
|
|
41580
|
-
${reach}${changed}`;
|
|
42133
|
+
${reach}${expires}${changed}`;
|
|
41581
42134
|
}).join(`
|
|
41582
42135
|
`);
|
|
41583
42136
|
}
|
|
@@ -44978,6 +45531,32 @@ var CONNECTION_COMMAND_SPECS = [
|
|
|
44978
45531
|
returns: "MoneyKrakenLinkStatus",
|
|
44979
45532
|
notes: `${routeOf(krakenLinkOperations.remove)}. Gated behind --allow-destructive.`
|
|
44980
45533
|
},
|
|
45534
|
+
{
|
|
45535
|
+
path: ["connections", "waste", "next"],
|
|
45536
|
+
summary: "The next waste collections: each day's kinds in words and when the bins go out (the evening before, at 20:00).",
|
|
45537
|
+
args: [],
|
|
45538
|
+
flags: [
|
|
45539
|
+
{ name: "limit", type: "string", description: "How many collection days (1\u201312, default 4)." }
|
|
45540
|
+
],
|
|
45541
|
+
scopes: scopesOf(wasteCalendarOperations.next),
|
|
45542
|
+
returns: "WasteCalendarNext",
|
|
45543
|
+
notes: `${routeOf(wasteCalendarOperations.next)}. A pickup that moved says why ("Garden, moved by the holiday").`
|
|
45544
|
+
},
|
|
45545
|
+
{
|
|
45546
|
+
path: ["connections", "waste", "connect"],
|
|
45547
|
+
summary: "Connect the household's waste calendar: its iCal feed URL, read from a file, then read it.",
|
|
45548
|
+
args: [],
|
|
45549
|
+
flags: [
|
|
45550
|
+
{
|
|
45551
|
+
name: "feed-file",
|
|
45552
|
+
type: "string",
|
|
45553
|
+
description: "Path to a file holding the feed's URL (one line), or - to read it from stdin."
|
|
45554
|
+
}
|
|
45555
|
+
],
|
|
45556
|
+
scopes: scopesOf(wasteCalendarOperations.connect),
|
|
45557
|
+
returns: "WasteCalendarNext",
|
|
45558
|
+
notes: `${routeOf(wasteCalendarOperations.connect)}. The URL can carry a private token, so it's read from the file and sealed; it is never a flag, never printed and never read back. It's read again every morning at 06:00; connecting again replaces it.`
|
|
45559
|
+
},
|
|
44981
45560
|
{
|
|
44982
45561
|
path: ["doccle", "connect"],
|
|
44983
45562
|
summary: "Give do your Doccle login so the nightly import can run. do checks it with Doccle first and keeps it sealed.",
|
|
@@ -45110,6 +45689,24 @@ var CONNECTION_HANDLERS = {
|
|
|
45110
45689
|
data: await guarded([], () => requireClient().get(pathOf(krakenLinkOperations.status))),
|
|
45111
45690
|
render: renderKraken
|
|
45112
45691
|
}),
|
|
45692
|
+
"connections waste next": async ({ flags, requireClient }) => {
|
|
45693
|
+
const limit = flagString(flags, "limit");
|
|
45694
|
+
if (limit !== undefined && !/^\d+$/.test(limit))
|
|
45695
|
+
throw usageError("--limit takes a number (1\u201312)");
|
|
45696
|
+
const path = `${pathOf(wasteCalendarOperations.next)}${limit ? `?limit=${limit}` : ""}`;
|
|
45697
|
+
return {
|
|
45698
|
+
data: await guarded([], () => requireClient().get(path)),
|
|
45699
|
+
render: renderWaste
|
|
45700
|
+
};
|
|
45701
|
+
},
|
|
45702
|
+
"connections waste connect": async ({ io, flags, requireClient }) => {
|
|
45703
|
+
refuseSecretFlags(flags, ["url", "feed", "feed-url"], "--feed-file <path>");
|
|
45704
|
+
const feedUrl = (await readSecret(io, flags, "feed-file", "the feed's URL", "line")).trim();
|
|
45705
|
+
return {
|
|
45706
|
+
data: await guarded([feedUrl], () => requireClient().post(pathOf(wasteCalendarOperations.connect), { feedUrl })),
|
|
45707
|
+
render: renderWaste
|
|
45708
|
+
};
|
|
45709
|
+
},
|
|
45113
45710
|
"money kraken-link set": async ({ io, flags, requireClient }) => {
|
|
45114
45711
|
refuseSecretFlags(flags, ["key", "api-key", "secret", "api-secret", "private-key"], "--key-file <path>");
|
|
45115
45712
|
const text = await readSecret(io, flags, "key-file", "the Kraken key", "all");
|
|
@@ -45196,6 +45793,30 @@ function refuseSecretFlags(flags, names, instead) {
|
|
|
45196
45793
|
}
|
|
45197
45794
|
}
|
|
45198
45795
|
}
|
|
45796
|
+
function renderWaste(data, mode) {
|
|
45797
|
+
const next = data;
|
|
45798
|
+
if (!next || !Array.isArray(next.collections))
|
|
45799
|
+
return renderRecord(data, mode);
|
|
45800
|
+
if (!next.connected)
|
|
45801
|
+
return "No waste calendar connected. Run do connections waste connect.";
|
|
45802
|
+
const day = (iso) => new Date(`${iso.slice(0, 10)}T12:00:00Z`).toLocaleDateString("en-GB", {
|
|
45803
|
+
weekday: "short",
|
|
45804
|
+
day: "numeric",
|
|
45805
|
+
month: "short",
|
|
45806
|
+
timeZone: "UTC"
|
|
45807
|
+
});
|
|
45808
|
+
const weekday = (iso) => new Date(`${iso.slice(0, 10)}T12:00:00Z`).toLocaleDateString("en-GB", {
|
|
45809
|
+
weekday: "short",
|
|
45810
|
+
timeZone: "UTC"
|
|
45811
|
+
});
|
|
45812
|
+
const lines = next.collections.map((c) => `${day(c.date)} \xB7 ${c.kindsWords} \xB7 bins out ${weekday(c.binsOutAt)} ${c.binsOutAt.slice(11)}`);
|
|
45813
|
+
return [
|
|
45814
|
+
...lines.length > 0 ? lines : ["No collections coming up."],
|
|
45815
|
+
...next.lastSyncLine ? [`Read: ${next.lastSyncLine}`] : [],
|
|
45816
|
+
...next.lastError ? [`Last read failed: ${next.lastError}`] : []
|
|
45817
|
+
].join(`
|
|
45818
|
+
`);
|
|
45819
|
+
}
|
|
45199
45820
|
async function readSecret(io, flags, flag, what, take) {
|
|
45200
45821
|
const path = flagString(flags, flag);
|
|
45201
45822
|
if (path === undefined || path.length === 0) {
|
|
@@ -46580,6 +47201,45 @@ var commands16 = [
|
|
|
46580
47201
|
}
|
|
46581
47202
|
];
|
|
46582
47203
|
|
|
47204
|
+
// ../../packages/domain/src/date.ts
|
|
47205
|
+
var formatters = new Map;
|
|
47206
|
+
function dateTimeFormat(locale, options) {
|
|
47207
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
47208
|
+
let formatter = formatters.get(key);
|
|
47209
|
+
if (!formatter) {
|
|
47210
|
+
formatter = new Intl.DateTimeFormat(locale, options);
|
|
47211
|
+
formatters.set(key, formatter);
|
|
47212
|
+
}
|
|
47213
|
+
return formatter;
|
|
47214
|
+
}
|
|
47215
|
+
var STYLES = ["dateStyle", "timeStyle"];
|
|
47216
|
+
var DATE_FIELDS = ["weekday", "year", "month", "day", ...STYLES];
|
|
47217
|
+
var CLOCK_PARTS = new Set(["hour", "minute", "second", "fractionalSecond", "dayPeriod"]);
|
|
47218
|
+
function dayInZone(now, zone) {
|
|
47219
|
+
return dateTimeFormat("en-CA", {
|
|
47220
|
+
timeZone: zone,
|
|
47221
|
+
year: "numeric",
|
|
47222
|
+
month: "2-digit",
|
|
47223
|
+
day: "2-digit"
|
|
47224
|
+
}).format(now);
|
|
47225
|
+
}
|
|
47226
|
+
function dayOfUtc(t) {
|
|
47227
|
+
const d = new Date(t);
|
|
47228
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
47229
|
+
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
|
|
47230
|
+
}
|
|
47231
|
+
function utcOfDay(day) {
|
|
47232
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
47233
|
+
return Date.UTC(y, m - 1, d);
|
|
47234
|
+
}
|
|
47235
|
+
function shiftDay2(day, n) {
|
|
47236
|
+
return dayOfUtc(utcOfDay(day) + n * 86400000);
|
|
47237
|
+
}
|
|
47238
|
+
function mondayOfDay(day) {
|
|
47239
|
+
const weekday = (new Date(utcOfDay(day)).getUTCDay() + 6) % 7;
|
|
47240
|
+
return shiftDay2(day, -weekday);
|
|
47241
|
+
}
|
|
47242
|
+
|
|
46583
47243
|
// src/commands/food.ts
|
|
46584
47244
|
var commands17 = [
|
|
46585
47245
|
{
|
|
@@ -46739,6 +47399,86 @@ var commands17 = [
|
|
|
46739
47399
|
};
|
|
46740
47400
|
}
|
|
46741
47401
|
},
|
|
47402
|
+
{
|
|
47403
|
+
spec: {
|
|
47404
|
+
path: ["food", "menu"],
|
|
47405
|
+
summary: "Show the week menu: each day's dinner and lunch with who cooks and its state, the counts, the leftovers and next week's draft.",
|
|
47406
|
+
args: [],
|
|
47407
|
+
flags: [
|
|
47408
|
+
{
|
|
47409
|
+
name: "start",
|
|
47410
|
+
type: "string",
|
|
47411
|
+
description: "Any day of the week (YYYY-MM-DD); this week by default."
|
|
47412
|
+
}
|
|
47413
|
+
],
|
|
47414
|
+
scopes: [...mealPlanOperations.week.scopes],
|
|
47415
|
+
returns: "MealWeek",
|
|
47416
|
+
notes: `${routeOf(mealPlanOperations.week)}. A draft dinner's \`why\` is its plan's notes; write drafts with \`do meal-plans create\` and state "draft".`
|
|
47417
|
+
},
|
|
47418
|
+
run: async ({ flags, requireClient }) => {
|
|
47419
|
+
const start = flagString(flags, "start");
|
|
47420
|
+
return {
|
|
47421
|
+
data: await requireClient().get(pathOf(mealPlanOperations.week), {
|
|
47422
|
+
query: { start }
|
|
47423
|
+
}),
|
|
47424
|
+
render: renderRecord
|
|
47425
|
+
};
|
|
47426
|
+
}
|
|
47427
|
+
},
|
|
47428
|
+
{
|
|
47429
|
+
spec: {
|
|
47430
|
+
path: ["food", "plans"],
|
|
47431
|
+
summary: "List meal plans, by recipe and state: --recipe <id> --state cooked is who cooked it when.",
|
|
47432
|
+
args: [],
|
|
47433
|
+
flags: [
|
|
47434
|
+
{ name: "recipe", type: "string", description: "Only this recipe's plans (its ULID)." },
|
|
47435
|
+
{
|
|
47436
|
+
name: "state",
|
|
47437
|
+
type: "string",
|
|
47438
|
+
description: "Only this state: planned, cooked, out, unsure or draft."
|
|
47439
|
+
}
|
|
47440
|
+
],
|
|
47441
|
+
scopes: [...mealPlanOperations.list.scopes],
|
|
47442
|
+
returns: "MealPlan",
|
|
47443
|
+
notes: `${routeOf(mealPlanOperations.list)}. \`cookUserId\` is who cooked it; \`notes\` is the plan's note.`
|
|
47444
|
+
},
|
|
47445
|
+
run: async ({ flags, requireClient }) => {
|
|
47446
|
+
const query = parseInput(mealPlanListQuerySchema, {
|
|
47447
|
+
recipeId: flagString(flags, "recipe"),
|
|
47448
|
+
state: flagString(flags, "state")
|
|
47449
|
+
});
|
|
47450
|
+
return {
|
|
47451
|
+
data: await requireClient().get(pathOf(mealPlanOperations.list), { query }),
|
|
47452
|
+
render: renderRecord
|
|
47453
|
+
};
|
|
47454
|
+
}
|
|
47455
|
+
},
|
|
47456
|
+
{
|
|
47457
|
+
spec: {
|
|
47458
|
+
path: ["food", "draft", "accept"],
|
|
47459
|
+
summary: "Accept a week's draft menu: its draft dinners become planned ones.",
|
|
47460
|
+
args: [],
|
|
47461
|
+
flags: [
|
|
47462
|
+
{
|
|
47463
|
+
name: "start",
|
|
47464
|
+
type: "string",
|
|
47465
|
+
description: "Any day of the week (YYYY-MM-DD); next week by default."
|
|
47466
|
+
}
|
|
47467
|
+
],
|
|
47468
|
+
scopes: [...mealPlanOperations.acceptDraft.scopes],
|
|
47469
|
+
returns: "MealWeek",
|
|
47470
|
+
notes: `${routeOf(mealPlanOperations.acceptDraft)}. Idempotent: a week with no drafts left is unchanged.`
|
|
47471
|
+
},
|
|
47472
|
+
run: async ({ flags, requireClient }) => {
|
|
47473
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
47474
|
+
const start = flagString(flags, "start") ?? shiftDay2(mondayOfDay(dayInZone(new Date, zone)), 7);
|
|
47475
|
+
const body = parseInput(acceptMealDraftInputSchema, { start });
|
|
47476
|
+
return {
|
|
47477
|
+
data: await requireClient().post(pathOf(mealPlanOperations.acceptDraft), body),
|
|
47478
|
+
render: renderRecord
|
|
47479
|
+
};
|
|
47480
|
+
}
|
|
47481
|
+
},
|
|
46742
47482
|
{
|
|
46743
47483
|
spec: {
|
|
46744
47484
|
path: ["food", "week-read"],
|
|
@@ -47185,9 +47925,19 @@ var SCHEDULE_FLAGS = [
|
|
|
47185
47925
|
type: "string",
|
|
47186
47926
|
description: "Due monthly on this day of the month: 1\u201331 (a 31 falls on a shorter month's last day), or last."
|
|
47187
47927
|
},
|
|
47928
|
+
{
|
|
47929
|
+
name: "time",
|
|
47930
|
+
type: "string",
|
|
47931
|
+
description: "When in the day: a 24-hour time (16:30) or a part of day (morning, midday, evening); none clears it."
|
|
47932
|
+
},
|
|
47933
|
+
{
|
|
47934
|
+
name: "for",
|
|
47935
|
+
type: "string",
|
|
47936
|
+
description: "How long it takes (15m, 1h, 1h30m; 1\u2013600 minutes); none clears it back to 15m."
|
|
47937
|
+
},
|
|
47188
47938
|
{ name: "data", type: "string", description: "JSON for any other field of the contract." }
|
|
47189
47939
|
];
|
|
47190
|
-
var SCHEDULE_NOTES = "One of --every-day, --on mo,we,fr, --every 3d|2w [--from YYYY-MM-DD], --monthly 1|last sets its schedule; days it isn't due never break the streak. Without one, it keeps `period` + `frequencyCount` (3\xD7 a week). The reply's `nextDue` lists its next 4 due days.";
|
|
47940
|
+
var SCHEDULE_NOTES = "One of --every-day, --on mo,we,fr, --every 3d|2w [--from YYYY-MM-DD], --monthly 1|last sets its schedule; days it isn't due never break the streak. Without one, it keeps `period` + `frequencyCount` (3\xD7 a week). The reply's `nextDue` lists its next 4 due days. --time 16:30|morning|midday|evening and --for 20m put it on your do calendar at that time on each due day (a part of day at 08:00, 12:30 or 19:00; 15 minutes without --for).";
|
|
47191
47941
|
function localToday() {
|
|
47192
47942
|
const now = new Date;
|
|
47193
47943
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -47227,11 +47977,31 @@ function scheduleFromFlags(flags, today = localToday()) {
|
|
|
47227
47977
|
}
|
|
47228
47978
|
return;
|
|
47229
47979
|
}
|
|
47980
|
+
function minutesFromFlag(raw) {
|
|
47981
|
+
const text = raw.trim().toLowerCase();
|
|
47982
|
+
const m = /^(?:(\d+)\s*h)?\s*(?:(\d+)\s*m?)?$/.exec(text);
|
|
47983
|
+
if (!text || !m || m[1] === undefined && m[2] === undefined)
|
|
47984
|
+
throw usageError("--for takes a length like 15m, 1h or 1h30m");
|
|
47985
|
+
return Number(m[1] ?? 0) * 60 + Number(m[2] ?? 0);
|
|
47986
|
+
}
|
|
47987
|
+
function timingFromFlags(flags) {
|
|
47988
|
+
const time = flagString(flags, "time");
|
|
47989
|
+
const length = flagString(flags, "for");
|
|
47990
|
+
const out = {};
|
|
47991
|
+
if (time !== undefined) {
|
|
47992
|
+
const t = time.trim().toLowerCase();
|
|
47993
|
+
out.timeOfDay = t === "none" ? null : /^\d:\d\d$/.test(t) ? `0${t}` : t;
|
|
47994
|
+
}
|
|
47995
|
+
if (length !== undefined) {
|
|
47996
|
+
out.durationMinutes = length.trim().toLowerCase() === "none" ? null : minutesFromFlag(length);
|
|
47997
|
+
}
|
|
47998
|
+
return out;
|
|
47999
|
+
}
|
|
47230
48000
|
var commands20 = [
|
|
47231
48001
|
{
|
|
47232
48002
|
spec: {
|
|
47233
48003
|
path: ["habits", "create"],
|
|
47234
|
-
summary: "Create a habit, with an optional schedule (every day, weekdays, every N, monthly).",
|
|
48004
|
+
summary: "Create a habit, with an optional schedule (every day, weekdays, every N, monthly), time and length.",
|
|
47235
48005
|
args: [{ name: "title", required: false, description: "The habit's title." }],
|
|
47236
48006
|
flags: SCHEDULE_FLAGS,
|
|
47237
48007
|
scopes: [...habitOperations.create.scopes],
|
|
@@ -47246,7 +48016,8 @@ var commands20 = [
|
|
|
47246
48016
|
measurementType: "binary",
|
|
47247
48017
|
...title ? { title } : {},
|
|
47248
48018
|
...readDataFlag(flags),
|
|
47249
|
-
...schedule ? { schedule } : {}
|
|
48019
|
+
...schedule ? { schedule } : {},
|
|
48020
|
+
...timingFromFlags(flags)
|
|
47250
48021
|
});
|
|
47251
48022
|
if (flagBool(flags, "dry-run")) {
|
|
47252
48023
|
return {
|
|
@@ -47263,7 +48034,7 @@ var commands20 = [
|
|
|
47263
48034
|
{
|
|
47264
48035
|
spec: {
|
|
47265
48036
|
path: ["habits", "update"],
|
|
47266
|
-
summary: "Change a habit: its schedule (--every-day, --on, --every, --monthly) or --data.",
|
|
48037
|
+
summary: "Change a habit: its schedule (--every-day, --on, --every, --monthly), --time, --for or --data.",
|
|
47267
48038
|
args: [{ name: "id", required: true, description: "Habit ULID." }],
|
|
47268
48039
|
flags: SCHEDULE_FLAGS,
|
|
47269
48040
|
scopes: [...habitOperations.update.scopes],
|
|
@@ -47275,10 +48046,11 @@ var commands20 = [
|
|
|
47275
48046
|
const schedule = scheduleFromFlags(flags);
|
|
47276
48047
|
const body = parseInput(updateHabitInputSchema, {
|
|
47277
48048
|
...readDataFlag(flags),
|
|
47278
|
-
...schedule ? { schedule } : {}
|
|
48049
|
+
...schedule ? { schedule } : {},
|
|
48050
|
+
...timingFromFlags(flags)
|
|
47279
48051
|
});
|
|
47280
48052
|
if (Object.keys(body).length === 0)
|
|
47281
|
-
throw usageError("nothing to change \u2014 pass a schedule flag such as --on mo,we, or --data");
|
|
48053
|
+
throw usageError("nothing to change \u2014 pass a schedule flag such as --on mo,we, --time, --for, or --data");
|
|
47282
48054
|
const client = requireClient();
|
|
47283
48055
|
if (flagBool(flags, "dry-run")) {
|
|
47284
48056
|
const before = await client.get(pathOf(habitOperations.get, { id }));
|
|
@@ -47459,6 +48231,35 @@ var commands20 = [
|
|
|
47459
48231
|
},
|
|
47460
48232
|
run: readOperation(habitOperations.today, renderHabitsToday)
|
|
47461
48233
|
},
|
|
48234
|
+
{
|
|
48235
|
+
spec: {
|
|
48236
|
+
path: ["habits", "due"],
|
|
48237
|
+
summary: "Each habit's due days in a range, by its schedule (Plan's Month and agendas).",
|
|
48238
|
+
args: [],
|
|
48239
|
+
flags: [
|
|
48240
|
+
{ name: "from", type: "string", description: "The first day (YYYY-MM-DD). Required." },
|
|
48241
|
+
{
|
|
48242
|
+
name: "to",
|
|
48243
|
+
type: "string",
|
|
48244
|
+
description: "The last day (YYYY-MM-DD), at most 62 days on. Required."
|
|
48245
|
+
}
|
|
48246
|
+
],
|
|
48247
|
+
scopes: [...habitOperations.due.scopes],
|
|
48248
|
+
returns: "HabitDueDate[]",
|
|
48249
|
+
notes: `${routeOf(habitOperations.due)}?from=&to=. Your saved time zone's days, both ends included. One row per habit per due day: \`time\` (HH:MM or null), \`scheduleLabel\`, \`daily\` (due every day) and \`streakLabel\` (\`N days\` daily, \`N in a row\` otherwise). Paused days, quit and event-scheduled habits are left out.`
|
|
48250
|
+
},
|
|
48251
|
+
run: async ({ flags, requireClient }) => {
|
|
48252
|
+
const from = flagString(flags, "from");
|
|
48253
|
+
const to = flagString(flags, "to");
|
|
48254
|
+
if (from === undefined || to === undefined) {
|
|
48255
|
+
throw usageError("habits due needs --from and --to");
|
|
48256
|
+
}
|
|
48257
|
+
return {
|
|
48258
|
+
data: await requireClient().get(pathOf(habitOperations.due), { query: { from, to } }),
|
|
48259
|
+
render: renderList
|
|
48260
|
+
};
|
|
48261
|
+
}
|
|
48262
|
+
},
|
|
47462
48263
|
{
|
|
47463
48264
|
spec: {
|
|
47464
48265
|
path: ["habit", "check-ins"],
|
|
@@ -47640,25 +48441,23 @@ var commands20 = [
|
|
|
47640
48441
|
{
|
|
47641
48442
|
spec: {
|
|
47642
48443
|
path: ["habit", "pause"],
|
|
47643
|
-
summary: "Pause a habit until the day it's back: off Now, no push, the plant waits.",
|
|
48444
|
+
summary: "Pause a habit until the day it's back, or until resumed: off Now, no push, the plant waits.",
|
|
47644
48445
|
args: [{ name: "id", required: true, description: "Habit ULID." }],
|
|
47645
48446
|
flags: [
|
|
47646
48447
|
{
|
|
47647
48448
|
name: "until",
|
|
47648
48449
|
type: "string",
|
|
47649
|
-
description: "The day it's back (YYYY-MM-DD), after today.
|
|
48450
|
+
description: "The day it's back (YYYY-MM-DD), after today. Leave it out to pause until `habit resume`."
|
|
47650
48451
|
}
|
|
47651
48452
|
],
|
|
47652
48453
|
scopes: [...habitOperations.pause.scopes],
|
|
47653
48454
|
returns: "HabitSummary",
|
|
47654
|
-
notes: `${routeOf(habitOperations.pause)}. Pausing a paused habit moves the day it's back. Paused days are neutral: the streak is frozen, never broken.`
|
|
48455
|
+
notes: `${routeOf(habitOperations.pause)}. Without --until the pause is open-ended: it lasts until \`habit resume\`. Pausing a paused habit moves the day it's back. Paused days are neutral: the streak is frozen, never broken.`
|
|
47655
48456
|
},
|
|
47656
48457
|
run: async ({ args, flags, requireClient }) => {
|
|
47657
48458
|
const id = requireArg(args, 0, "habit id");
|
|
47658
48459
|
const until = flagString(flags, "until");
|
|
47659
|
-
|
|
47660
|
-
throw usageError("habit pause needs --until <YYYY-MM-DD>, the day it's back");
|
|
47661
|
-
const input = parseInput(habitPauseInputSchema, { until });
|
|
48460
|
+
const input = parseInput(habitPauseInputSchema, until ? { until } : {});
|
|
47662
48461
|
return {
|
|
47663
48462
|
data: await requireClient().post(pathOf(habitOperations.pause, { id }), input),
|
|
47664
48463
|
render: renderHabit
|
|
@@ -49755,6 +50554,17 @@ var commands25 = [
|
|
|
49755
50554
|
];
|
|
49756
50555
|
|
|
49757
50556
|
// src/commands/keys.ts
|
|
50557
|
+
function renderCreatedKey(data, mode) {
|
|
50558
|
+
const key = data.data ?? data;
|
|
50559
|
+
if (!key?.key)
|
|
50560
|
+
return renderRecord(data, mode);
|
|
50561
|
+
return [
|
|
50562
|
+
`${key.name}: ${key.scopeLine || "(no scopes)"}`,
|
|
50563
|
+
key.expiresAt ? `Expires ${key.expiresAt.slice(0, 10)}.` : "Never expires.",
|
|
50564
|
+
`Key (shown once): ${key.key}`
|
|
50565
|
+
].join(`
|
|
50566
|
+
`);
|
|
50567
|
+
}
|
|
49758
50568
|
var commands26 = [
|
|
49759
50569
|
{
|
|
49760
50570
|
spec: {
|
|
@@ -49768,6 +50578,57 @@ var commands26 = [
|
|
|
49768
50578
|
},
|
|
49769
50579
|
run: readOperation(keyOperations.list, renderKeys, {}, { paged: true })
|
|
49770
50580
|
},
|
|
50581
|
+
{
|
|
50582
|
+
spec: {
|
|
50583
|
+
path: ["keys", "create"],
|
|
50584
|
+
summary: "Mint an API key by section, each read or write, with an optional expiry. Prints the key once.",
|
|
50585
|
+
args: [],
|
|
50586
|
+
flags: [
|
|
50587
|
+
{ name: "name", type: "string", description: "What the key is for (e.g. 'CI deploy')." },
|
|
50588
|
+
{
|
|
50589
|
+
name: "section",
|
|
50590
|
+
type: "string",
|
|
50591
|
+
description: "A section and its access, <section>:<read|write>, repeated: today, plan, work, habits, journal, home, food, collection, money, health, people, animals, agents, settings."
|
|
50592
|
+
},
|
|
50593
|
+
{
|
|
50594
|
+
name: "expires",
|
|
50595
|
+
type: "string",
|
|
50596
|
+
description: "When it stops working, a date (YYYY-MM-DD). Left out: never."
|
|
50597
|
+
}
|
|
50598
|
+
],
|
|
50599
|
+
scopes: [...keyOperations.create.scopes],
|
|
50600
|
+
returns: "ApiKeyCreated",
|
|
50601
|
+
notes: "The key is for the member behind this key, never more than this key holds (exit 3), never an Agent's. The secret is printed once, then never again; past its expiry the key gets 401 'This token expired on <date>'."
|
|
50602
|
+
},
|
|
50603
|
+
run: async ({ flags, requireClient }) => {
|
|
50604
|
+
const name = flagString(flags, "name");
|
|
50605
|
+
if (!name)
|
|
50606
|
+
throw usageError("keys create needs --name <name>");
|
|
50607
|
+
const sections = flagStrings(flags, "section").map((raw) => {
|
|
50608
|
+
const [section, access] = raw.split(":").map((part) => part.trim());
|
|
50609
|
+
if (!section || access !== "read" && access !== "write") {
|
|
50610
|
+
throw usageError(`--section takes <section>:<read|write>, not '${raw}'`);
|
|
50611
|
+
}
|
|
50612
|
+
return { section, access };
|
|
50613
|
+
});
|
|
50614
|
+
if (sections.length === 0) {
|
|
50615
|
+
throw usageError("keys create needs at least one --section <section>:<read|write>");
|
|
50616
|
+
}
|
|
50617
|
+
const expires = flagString(flags, "expires");
|
|
50618
|
+
if (expires !== undefined && !/^\d{4}-\d{2}-\d{2}$/.test(expires)) {
|
|
50619
|
+
throw usageError("--expires takes a date, YYYY-MM-DD");
|
|
50620
|
+
}
|
|
50621
|
+
const body = {
|
|
50622
|
+
name,
|
|
50623
|
+
sections,
|
|
50624
|
+
...expires ? { expiresAt: `${expires}T00:00:00.000Z` } : {}
|
|
50625
|
+
};
|
|
50626
|
+
return {
|
|
50627
|
+
data: await requireClient().post(pathOf(keyOperations.create), body),
|
|
50628
|
+
render: renderCreatedKey
|
|
50629
|
+
};
|
|
50630
|
+
}
|
|
50631
|
+
},
|
|
49771
50632
|
{
|
|
49772
50633
|
spec: {
|
|
49773
50634
|
path: ["keys", "update"],
|
|
@@ -49788,7 +50649,7 @@ var commands26 = [
|
|
|
49788
50649
|
],
|
|
49789
50650
|
scopes: [...keyOperations.update.scopes],
|
|
49790
50651
|
returns: "ApiKey",
|
|
49791
|
-
notes: "A key never grants more than it holds: it can't widen its own scopes or change a key that reaches further (exit 3). Every change is recorded on the key.
|
|
50652
|
+
notes: "A key never grants more than it holds: it can't widen its own scopes or change a key that reaches further (exit 3). Every change is recorded on the key. Revoking stays in the app."
|
|
49792
50653
|
},
|
|
49793
50654
|
run: async ({ args, flags, requireClient }) => {
|
|
49794
50655
|
const id = requireArg(args, 0, "key id");
|
|
@@ -53781,7 +54642,9 @@ function renderRoutinesToday(data, mode) {
|
|
|
53781
54642
|
const steps = r.steps.map((s) => {
|
|
53782
54643
|
const meta = s.meta ? paint(mode, "dim", ` \xB7 ${s.meta}`) : "";
|
|
53783
54644
|
const kind = s.kind === "step" ? "" : ` [${s.kind}]`;
|
|
53784
|
-
|
|
54645
|
+
const optional = s.optional ? " (optional)" : "";
|
|
54646
|
+
const indent = " ".repeat(s.depth ?? 0);
|
|
54647
|
+
return ` ${indent}${s.done ? "\u2713" : "\u25CB"} ${s.n}. ${s.title}${kind}${optional}${meta}`;
|
|
53785
54648
|
});
|
|
53786
54649
|
return [head, ...steps].join(`
|
|
53787
54650
|
`);
|
|
@@ -53793,7 +54656,7 @@ function renderRoutineTaskAdded(data) {
|
|
|
53793
54656
|
const last = run.steps?.at(-1)?.title ?? "the task";
|
|
53794
54657
|
return `${run.routineTitle ?? "(routine)"}: ${last} is the last step of today's run.`;
|
|
53795
54658
|
}
|
|
53796
|
-
var STEP_GRAMMAR = "A step is `habit:<habit id>` (a habit step, titled after the habit), `task:<task id>` (a Work task step, titled after the task) or any other text (a plain step with that title).";
|
|
54659
|
+
var STEP_GRAMMAR = "A step is `habit:<habit id>` (a habit step, titled after the habit), `task:<task id>` (a Work task step, titled after the task), `routine:<routine id>` (a nested routine, titled after it: a run walks its steps in this place) or any other text (a plain step with that title).";
|
|
53797
54660
|
var ROUTINE_FLAGS = [
|
|
53798
54661
|
{ name: "kind", type: "string", description: "sequence, time_blocked or checklist." },
|
|
53799
54662
|
{ name: "area", type: "string", description: "The Area's ULID (`none` unfiles it)." },
|
|
@@ -53816,8 +54679,13 @@ function routineFields(flags) {
|
|
|
53816
54679
|
out.notes = notes;
|
|
53817
54680
|
return out;
|
|
53818
54681
|
}
|
|
54682
|
+
var LINK_PATH = {
|
|
54683
|
+
habit: habitOperations.get,
|
|
54684
|
+
task: taskOperations.get,
|
|
54685
|
+
routine: routineOperations.get
|
|
54686
|
+
};
|
|
53819
54687
|
async function stepFrom(client, item, title) {
|
|
53820
|
-
const link = /^(habit|task):(\S+)$/.exec(item.trim());
|
|
54688
|
+
const link = /^(habit|task|routine):(\S+)$/.exec(item.trim());
|
|
53821
54689
|
if (!link) {
|
|
53822
54690
|
const text = (title ?? item).trim();
|
|
53823
54691
|
if (!text)
|
|
@@ -53826,8 +54694,12 @@ async function stepFrom(client, item, title) {
|
|
|
53826
54694
|
}
|
|
53827
54695
|
const kind = link[1];
|
|
53828
54696
|
const id = link[2];
|
|
53829
|
-
const named = title ?? (await client.get(
|
|
53830
|
-
|
|
54697
|
+
const named = title ?? (await client.get(pathOf(LINK_PATH[kind], { id }))).title ?? kind;
|
|
54698
|
+
if (kind === "habit")
|
|
54699
|
+
return { id: ulid3(), title: named, refKind: "habit", habitId: id };
|
|
54700
|
+
if (kind === "task")
|
|
54701
|
+
return { id: ulid3(), title: named, refKind: "task", taskId: id };
|
|
54702
|
+
return { id: ulid3(), title: named, refKind: "routine", childRoutineId: id };
|
|
53831
54703
|
}
|
|
53832
54704
|
function stepInput(step, sortOrder) {
|
|
53833
54705
|
return {
|
|
@@ -53836,6 +54708,8 @@ function stepInput(step, sortOrder) {
|
|
|
53836
54708
|
refKind: step.refKind,
|
|
53837
54709
|
habitId: step.refKind === "habit" ? step.habitId : null,
|
|
53838
54710
|
taskId: step.refKind === "task" ? step.taskId : null,
|
|
54711
|
+
childRoutineId: step.refKind === "routine" ? step.childRoutineId : null,
|
|
54712
|
+
optional: step.optional,
|
|
53839
54713
|
sortOrder,
|
|
53840
54714
|
durationMinutes: step.durationMinutes,
|
|
53841
54715
|
notes: step.notes
|
|
@@ -53861,7 +54735,8 @@ function renderRoutine(data) {
|
|
|
53861
54735
|
const r = data;
|
|
53862
54736
|
const steps = (r.steps ?? []).map((s, i) => {
|
|
53863
54737
|
const kind = s.refKind === "free" ? "" : ` [${s.refKind}]`;
|
|
53864
|
-
|
|
54738
|
+
const optional = s.optional ? " (optional)" : "";
|
|
54739
|
+
return ` ${i + 1}. ${s.title}${kind}${optional} ${s.id}`;
|
|
53865
54740
|
});
|
|
53866
54741
|
return [
|
|
53867
54742
|
`${r.title ?? "(routine)"} ${r.id}`,
|
|
@@ -53875,6 +54750,30 @@ function putSteps(client, id, steps) {
|
|
|
53875
54750
|
});
|
|
53876
54751
|
return client.patch(pathOf(routineOperations.update, { id }), body);
|
|
53877
54752
|
}
|
|
54753
|
+
function ruleAt(recurrence, at, timezone) {
|
|
54754
|
+
if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(at))
|
|
54755
|
+
throw usageError("--at takes a 24-hour time: 07:30");
|
|
54756
|
+
if (!recurrence)
|
|
54757
|
+
return { mode: "rigid", freq: "daily", interval: 1, timezone, timeOfDay: at };
|
|
54758
|
+
const keep = {
|
|
54759
|
+
mode: recurrence.mode,
|
|
54760
|
+
timezone: recurrence.timezone,
|
|
54761
|
+
interval: recurrence.interval,
|
|
54762
|
+
freq: recurrence.freq,
|
|
54763
|
+
byDay: recurrence.byDay,
|
|
54764
|
+
until: recurrence.until,
|
|
54765
|
+
count: recurrence.count,
|
|
54766
|
+
unit: recurrence.unit,
|
|
54767
|
+
anchor: recurrence.anchor
|
|
54768
|
+
};
|
|
54769
|
+
return {
|
|
54770
|
+
...Object.fromEntries(Object.entries(keep).filter(([, v]) => v !== null && v !== undefined)),
|
|
54771
|
+
timeOfDay: at
|
|
54772
|
+
};
|
|
54773
|
+
}
|
|
54774
|
+
function localZone() {
|
|
54775
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
54776
|
+
}
|
|
53878
54777
|
function readRoutine(client, id) {
|
|
53879
54778
|
return client.get(pathOf(routineOperations.get, { id }));
|
|
53880
54779
|
}
|
|
@@ -53920,20 +54819,54 @@ var routineCommands = [
|
|
|
53920
54819
|
{
|
|
53921
54820
|
spec: {
|
|
53922
54821
|
path: ["routines", "update"],
|
|
53923
|
-
summary: "Change a routine: its title, kind, Area, notes, or --data.",
|
|
54822
|
+
summary: "Change a routine: its title, kind, Area, notes, start time (--at), or --data.",
|
|
53924
54823
|
args: [{ name: "id", required: true, description: "Routine ULID." }],
|
|
53925
|
-
flags: [
|
|
54824
|
+
flags: [
|
|
54825
|
+
{ name: "title", type: "string", description: "A new title." },
|
|
54826
|
+
{
|
|
54827
|
+
name: "at",
|
|
54828
|
+
type: "string",
|
|
54829
|
+
description: "Its start time, 24-hour (07:30); one that doesn't repeat starts daily."
|
|
54830
|
+
},
|
|
54831
|
+
...ROUTINE_FLAGS
|
|
54832
|
+
],
|
|
53926
54833
|
scopes: [...routineOperations.update.scopes],
|
|
53927
54834
|
returns: "RoutineView",
|
|
53928
|
-
notes: `${routeOf(routineOperations.update)}. Only what you pass changes; its steps change with \`do routines steps
|
|
54835
|
+
notes: `${routeOf(routineOperations.update)}; --at is ${routeOf(routineOperations.attachRecurrence)} with the routine's rule as it is and its time moved (a routine that doesn't repeat yet becomes daily at that time, in this machine's zone). Only what you pass changes; its steps change with \`do routines steps\`. A routine at a time lands on your do calendar, each child routine and own step as its own event.`
|
|
53929
54836
|
},
|
|
53930
54837
|
run: async ({ args, flags, requireClient }) => {
|
|
53931
54838
|
const id = requireArg(args, 0, "routine id");
|
|
54839
|
+
const at = flagString(flags, "at");
|
|
53932
54840
|
const body = parseInput(updateRoutineInputSchema, routineFields(flags));
|
|
53933
|
-
if (Object.keys(body).length === 0) {
|
|
53934
|
-
throw usageError("nothing to change \u2014 pass --title, --kind, --area, --notes or --data");
|
|
54841
|
+
if (Object.keys(body).length === 0 && at === undefined) {
|
|
54842
|
+
throw usageError("nothing to change \u2014 pass --title, --kind, --area, --notes, --at or --data");
|
|
53935
54843
|
}
|
|
53936
54844
|
const client = requireClient();
|
|
54845
|
+
if (at !== undefined) {
|
|
54846
|
+
const before = await readRoutine(client, id);
|
|
54847
|
+
const rule = parseInput(updateRecurrenceRuleInputSchema, ruleAt(before.recurrence, at.trim(), localZone()));
|
|
54848
|
+
if (flagBool(flags, "dry-run")) {
|
|
54849
|
+
return {
|
|
54850
|
+
data: {
|
|
54851
|
+
dryRun: true,
|
|
54852
|
+
op: "update",
|
|
54853
|
+
entity: "routines",
|
|
54854
|
+
id,
|
|
54855
|
+
before,
|
|
54856
|
+
recurrence: rule,
|
|
54857
|
+
changes: body
|
|
54858
|
+
},
|
|
54859
|
+
render: renderRecord
|
|
54860
|
+
};
|
|
54861
|
+
}
|
|
54862
|
+
const timed = await client.put(pathOf(routineOperations.attachRecurrence, { id }), rule);
|
|
54863
|
+
if (Object.keys(body).length === 0)
|
|
54864
|
+
return { data: timed, render: renderRoutine };
|
|
54865
|
+
return {
|
|
54866
|
+
data: await client.patch(pathOf(routineOperations.update, { id }), body),
|
|
54867
|
+
render: renderRoutine
|
|
54868
|
+
};
|
|
54869
|
+
}
|
|
53937
54870
|
if (flagBool(flags, "dry-run")) {
|
|
53938
54871
|
const before = await readRoutine(client, id);
|
|
53939
54872
|
return {
|
|
@@ -53988,31 +54921,52 @@ var routineCommands = [
|
|
|
53988
54921
|
{
|
|
53989
54922
|
spec: {
|
|
53990
54923
|
path: ["routines", "steps", "add"],
|
|
53991
|
-
summary: "Add a step to a routine, at the end or --at a position.",
|
|
54924
|
+
summary: "Add a step to a routine (or a nested routine), at the end or --at a position.",
|
|
53992
54925
|
args: [
|
|
53993
54926
|
{ name: "id", required: true, description: "Routine ULID." },
|
|
53994
|
-
{
|
|
54927
|
+
{
|
|
54928
|
+
name: "step",
|
|
54929
|
+
required: false,
|
|
54930
|
+
description: "The step. See grammar. Leave it out with --routine."
|
|
54931
|
+
}
|
|
53995
54932
|
],
|
|
53996
54933
|
flags: [
|
|
53997
54934
|
{ name: "at", type: "string", description: "Its position, 1-based (default: last)." },
|
|
53998
|
-
{ name: "title", type: "string", description: "Its title (instead of the linked name)." }
|
|
54935
|
+
{ name: "title", type: "string", description: "Its title (instead of the linked name)." },
|
|
54936
|
+
{
|
|
54937
|
+
name: "routine",
|
|
54938
|
+
type: "string",
|
|
54939
|
+
description: "Nest this routine (its ULID) as the step; same as `routine:<id>`."
|
|
54940
|
+
},
|
|
54941
|
+
{
|
|
54942
|
+
name: "optional",
|
|
54943
|
+
type: "boolean",
|
|
54944
|
+
description: "An optional step: it never keeps the routine from being done."
|
|
54945
|
+
}
|
|
53999
54946
|
],
|
|
54000
54947
|
scopes: [...routineOperations.get.scopes, ...routineOperations.update.scopes],
|
|
54001
54948
|
returns: "RoutineView",
|
|
54002
54949
|
grammar: { step: STEP_GRAMMAR },
|
|
54003
|
-
notes: `${routeOf(routineOperations.get)}, then ${routeOf(routineOperations.update)} with every step (the others keep their ids)
|
|
54950
|
+
notes: `${routeOf(routineOperations.get)}, then ${routeOf(routineOperations.update)} with every step (the others keep their ids). A nested routine (\`--routine <id>\`) walks its steps in this place when the routine runs, to any depth; one that would contain itself is a 400. e.g. \`do routines steps add <morning> --routine <teeth> --optional\`.`
|
|
54004
54951
|
},
|
|
54005
54952
|
run: async ({ args, flags, requireClient }) => {
|
|
54006
54953
|
const id = requireArg(args, 0, "routine id");
|
|
54007
|
-
const
|
|
54954
|
+
const child = flagString(flags, "routine");
|
|
54955
|
+
const words = args.slice(1).join(" ").trim();
|
|
54956
|
+
if (child && words)
|
|
54957
|
+
throw usageError("--routine is the step; pass no other step with it");
|
|
54958
|
+
const item = child ? `routine:${child}` : words;
|
|
54008
54959
|
if (!item)
|
|
54009
|
-
throw usageError("routines steps add needs the step: `habit:<id>`, `task:<id>` or a title");
|
|
54960
|
+
throw usageError("routines steps add needs the step: `habit:<id>`, `task:<id>`, `routine:<id>` (or --routine <id>) or a title");
|
|
54010
54961
|
const client = requireClient();
|
|
54011
54962
|
const routine = await readRoutine(client, id);
|
|
54012
54963
|
const steps = routine.steps.map((s, i) => stepInput(s, i));
|
|
54013
54964
|
const at = flagString(flags, "at");
|
|
54014
54965
|
const index = at === undefined ? steps.length : positionOf(at, steps.length + 1, "--at");
|
|
54015
|
-
|
|
54966
|
+
const step = await stepFrom(client, item, flagString(flags, "title"));
|
|
54967
|
+
if (flagBool(flags, "optional"))
|
|
54968
|
+
step.optional = true;
|
|
54969
|
+
steps.splice(index, 0, step);
|
|
54016
54970
|
return { data: await putSteps(client, id, steps), render: renderRoutine };
|
|
54017
54971
|
}
|
|
54018
54972
|
},
|
|
@@ -54122,7 +55076,7 @@ var commands42 = [
|
|
|
54122
55076
|
flags: [],
|
|
54123
55077
|
scopes: [...routineOperations.today.scopes],
|
|
54124
55078
|
returns: "RoutinesTodayResult",
|
|
54125
|
-
notes: `${routeOf(routineOperations.today)}. Today is your saved time zone's day. A routine is on it when its rule has it due, it has a slot today, or it ran today. Each step's \`kind\` is step, habit
|
|
55079
|
+
notes: `${routeOf(routineOperations.today)}. Today is your saved time zone's day. A routine is on it when its rule has it due, it has a slot today, or it ran today. Each step's \`kind\` is step, habit, task (a Work task) or routine (a nested routine's heading, its steps one \`depth\` deeper; V3-121); \`rhythm\` is its last 14 days, oldest first (all, some, none, off).`
|
|
54126
55080
|
},
|
|
54127
55081
|
run: async ({ requireClient }) => ({
|
|
54128
55082
|
data: await requireClient().get(pathOf(routineOperations.today)),
|
|
@@ -55534,7 +56488,8 @@ var commands50 = [
|
|
|
55534
56488
|
args: [],
|
|
55535
56489
|
flags: [{ name: "data", type: "string", description: "JSON of fields to change." }],
|
|
55536
56490
|
scopes: [...settingsOperations.update.scopes],
|
|
55537
|
-
returns: "UserSettings"
|
|
56491
|
+
returns: "UserSettings",
|
|
56492
|
+
notes: `Appearance keys: glass (liquid|frosted|solid), followSky, nightUntilWake, largerText, reduceMotion, showStreakNumbers, themePack (do|skyrim|pokemon), themePackScope (section|everywhere). E.g. --data '{"showStreakNumbers":false}'.`
|
|
55538
56493
|
},
|
|
55539
56494
|
run: async ({ flags, requireClient }) => {
|
|
55540
56495
|
const body = parseInput(updateUserSettingsInputSchema, readDataFlag(flags));
|
|
@@ -55743,6 +56698,119 @@ var commands53 = [
|
|
|
55743
56698
|
})
|
|
55744
56699
|
];
|
|
55745
56700
|
|
|
56701
|
+
// ../../packages/domain/src/task-series.ts
|
|
56702
|
+
function rhythmFromWord(word) {
|
|
56703
|
+
const map = {
|
|
56704
|
+
week: { freq: "weekly", interval: 1 },
|
|
56705
|
+
weekly: { freq: "weekly", interval: 1 },
|
|
56706
|
+
fortnight: { freq: "weekly", interval: 2 },
|
|
56707
|
+
month: { freq: "monthly", interval: 1 },
|
|
56708
|
+
monthly: { freq: "monthly", interval: 1 },
|
|
56709
|
+
quarter: { freq: "monthly", interval: 3 },
|
|
56710
|
+
quarterly: { freq: "monthly", interval: 3 },
|
|
56711
|
+
"half-year": { freq: "monthly", interval: 6 },
|
|
56712
|
+
year: { freq: "yearly", interval: 1 },
|
|
56713
|
+
yearly: { freq: "yearly", interval: 1 }
|
|
56714
|
+
};
|
|
56715
|
+
return map[word.trim().toLowerCase()] ?? null;
|
|
56716
|
+
}
|
|
56717
|
+
|
|
56718
|
+
// src/commands/task-series.ts
|
|
56719
|
+
function renderSeries(data, mode) {
|
|
56720
|
+
const page = data;
|
|
56721
|
+
const list = Array.isArray(page) ? page : ("data" in page) ? page.data : [page];
|
|
56722
|
+
if (list.length === 0)
|
|
56723
|
+
return paint(mode, "dim", "(no task series)");
|
|
56724
|
+
return list.map((s) => {
|
|
56725
|
+
const next = s.active ? s.nextMadeOn ? `next made ${s.nextMadeOn}` : "next made" : "stopped";
|
|
56726
|
+
const items = s.items.map((i) => i.taskId ? `${i.label} (${i.status ?? "?"})` : `${i.label} (to come)`).join(", ");
|
|
56727
|
+
return ` ${s.title} ${paint(mode, "dim", s.rhythm)} ${next} ${paint(mode, "dim", s.id)}
|
|
56728
|
+
${items}`;
|
|
56729
|
+
}).join(`
|
|
56730
|
+
`);
|
|
56731
|
+
}
|
|
56732
|
+
var commands54 = [
|
|
56733
|
+
defineCommand({
|
|
56734
|
+
operation: taskSeriesOperations.make,
|
|
56735
|
+
path: ["tasks", "make-series"],
|
|
56736
|
+
summary: "Make a task the first of a series: each next one is its own task.",
|
|
56737
|
+
args: [{ name: "task", required: true, description: "The task's ref (DO-150) or id." }],
|
|
56738
|
+
flags: [
|
|
56739
|
+
{
|
|
56740
|
+
name: "every",
|
|
56741
|
+
type: "string",
|
|
56742
|
+
description: "week, fortnight, month, quarter, half-year or year."
|
|
56743
|
+
},
|
|
56744
|
+
{
|
|
56745
|
+
name: "label",
|
|
56746
|
+
type: "string",
|
|
56747
|
+
description: "How each is named: quarter (Q1\u2026), month (October) or date (Mon 26 Oct)."
|
|
56748
|
+
},
|
|
56749
|
+
{
|
|
56750
|
+
name: "lead",
|
|
56751
|
+
type: "string",
|
|
56752
|
+
description: "Make each next task this many days before it's due (default 14)."
|
|
56753
|
+
}
|
|
56754
|
+
],
|
|
56755
|
+
returns: "TaskSeries",
|
|
56756
|
+
run: async ({ args, flags, requireClient }) => {
|
|
56757
|
+
const task = requireArg(args, 0, "task");
|
|
56758
|
+
const every = flagString(flags, "every");
|
|
56759
|
+
const rule = every ? rhythmFromWord(every) : null;
|
|
56760
|
+
if (!rule) {
|
|
56761
|
+
throw usageError("tasks make-series needs --every week|fortnight|month|quarter|half-year|year");
|
|
56762
|
+
}
|
|
56763
|
+
const label = flagString(flags, "label");
|
|
56764
|
+
const lead = flagString(flags, "lead");
|
|
56765
|
+
const body = parseInput(makeTaskSeriesInputSchema, {
|
|
56766
|
+
rule,
|
|
56767
|
+
...label ? { occurrenceLabel: label.trim().toLowerCase() } : {},
|
|
56768
|
+
...lead !== undefined ? { leadDays: Number(lead) } : {}
|
|
56769
|
+
});
|
|
56770
|
+
return {
|
|
56771
|
+
data: await requireClient().post(pathOf(taskSeriesOperations.make, { id: task }), body),
|
|
56772
|
+
render: renderSeries
|
|
56773
|
+
};
|
|
56774
|
+
}
|
|
56775
|
+
}),
|
|
56776
|
+
defineCommand({
|
|
56777
|
+
operation: taskSeriesOperations.list,
|
|
56778
|
+
path: ["series", "list"],
|
|
56779
|
+
summary: "List the task series, with their rhythm and what's next.",
|
|
56780
|
+
returns: "TaskSeries[]",
|
|
56781
|
+
run: async ({ requireClient }) => ({
|
|
56782
|
+
data: await requireClient().get(pathOf(taskSeriesOperations.list)),
|
|
56783
|
+
render: renderSeries
|
|
56784
|
+
})
|
|
56785
|
+
}),
|
|
56786
|
+
defineCommand({
|
|
56787
|
+
operation: taskSeriesOperations.get,
|
|
56788
|
+
path: ["series", "show"],
|
|
56789
|
+
summary: "Show a task series: its strip of tasks and the next to come.",
|
|
56790
|
+
args: [{ name: "series", required: true, description: "The series' id." }],
|
|
56791
|
+
returns: "TaskSeries",
|
|
56792
|
+
run: async ({ args, requireClient }) => ({
|
|
56793
|
+
data: await requireClient().get(pathOf(taskSeriesOperations.get, { id: requireArg(args, 0, "series") })),
|
|
56794
|
+
render: renderSeries
|
|
56795
|
+
})
|
|
56796
|
+
}),
|
|
56797
|
+
defineCommand({
|
|
56798
|
+
operation: taskSeriesOperations.stop,
|
|
56799
|
+
path: ["series", "stop"],
|
|
56800
|
+
summary: "Stop a task series: no more tasks are made; the ones made stay.",
|
|
56801
|
+
args: [{ name: "series", required: true, description: "The series' id." }],
|
|
56802
|
+
returns: "{ ok, stopped }",
|
|
56803
|
+
run: async ({ args, requireClient }) => {
|
|
56804
|
+
const id = requireArg(args, 0, "series");
|
|
56805
|
+
await requireClient().delete(pathOf(taskSeriesOperations.stop, { id }));
|
|
56806
|
+
return {
|
|
56807
|
+
data: { ok: true, stopped: id },
|
|
56808
|
+
render: () => "Stopped the series. Its tasks stay."
|
|
56809
|
+
};
|
|
56810
|
+
}
|
|
56811
|
+
})
|
|
56812
|
+
];
|
|
56813
|
+
|
|
55746
56814
|
// src/capture.ts
|
|
55747
56815
|
function parseCapture2(input, now) {
|
|
55748
56816
|
const parsed = parseCapture(input, now);
|
|
@@ -55816,7 +56884,7 @@ function repeatFromFlags(flags) {
|
|
|
55816
56884
|
out.anchor = "from_completion";
|
|
55817
56885
|
return out;
|
|
55818
56886
|
}
|
|
55819
|
-
var
|
|
56887
|
+
var commands55 = [
|
|
55820
56888
|
{
|
|
55821
56889
|
spec: {
|
|
55822
56890
|
path: ["capture"],
|
|
@@ -55873,6 +56941,7 @@ var commands54 = [
|
|
|
55873
56941
|
title: parsed.title,
|
|
55874
56942
|
tags: parsed.tags,
|
|
55875
56943
|
...parsed.starred ? { starred: parsed.starred } : {},
|
|
56944
|
+
...parsed.priority ? { priority: parsed.priority } : {},
|
|
55876
56945
|
...parsed.area ? { areaName: parsed.area } : {},
|
|
55877
56946
|
...scheduledAt ? { scheduledAt } : {},
|
|
55878
56947
|
...dueAt ? { dueAt } : {},
|
|
@@ -56273,7 +57342,7 @@ var commands54 = [
|
|
|
56273
57342
|
];
|
|
56274
57343
|
|
|
56275
57344
|
// src/commands/time-blocks.ts
|
|
56276
|
-
var
|
|
57345
|
+
var commands56 = [
|
|
56277
57346
|
{
|
|
56278
57347
|
spec: {
|
|
56279
57348
|
path: ["time-blocks", "record"],
|
|
@@ -56454,7 +57523,7 @@ function renderLandedCounts(data, mode) {
|
|
|
56454
57523
|
return out.join(`
|
|
56455
57524
|
`);
|
|
56456
57525
|
}
|
|
56457
|
-
var
|
|
57526
|
+
var commands57 = [
|
|
56458
57527
|
{
|
|
56459
57528
|
spec: {
|
|
56460
57529
|
path: ["today"],
|
|
@@ -56826,7 +57895,7 @@ function renderViews(data, mode) {
|
|
|
56826
57895
|
function renderView(data, mode) {
|
|
56827
57896
|
return renderViews([data], mode);
|
|
56828
57897
|
}
|
|
56829
|
-
var
|
|
57898
|
+
var commands58 = [
|
|
56830
57899
|
defineCommand({
|
|
56831
57900
|
operation: workViewOperations.list,
|
|
56832
57901
|
path: ["views", "list"],
|
|
@@ -56945,7 +58014,7 @@ var commands57 = [
|
|
|
56945
58014
|
];
|
|
56946
58015
|
|
|
56947
58016
|
// src/commands/worktree-links.ts
|
|
56948
|
-
var
|
|
58017
|
+
var commands59 = [
|
|
56949
58018
|
{
|
|
56950
58019
|
spec: {
|
|
56951
58020
|
path: ["worktree-links", "list"],
|
|
@@ -57109,7 +58178,8 @@ var FEATURE_COMMANDS = [
|
|
|
57109
58178
|
...commands55,
|
|
57110
58179
|
...commands56,
|
|
57111
58180
|
...commands57,
|
|
57112
|
-
...commands58
|
|
58181
|
+
...commands58,
|
|
58182
|
+
...commands59
|
|
57113
58183
|
];
|
|
57114
58184
|
|
|
57115
58185
|
// src/registry.ts
|