@opentabs-dev/opentabs-plugin-ynab 0.0.86 → 0.0.87
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/adapter.iife.js +447 -24
- package/dist/adapter.iife.js.map +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/create-category-group.d.ts +11 -0
- package/dist/tools/create-category-group.d.ts.map +1 -0
- package/dist/tools/create-category-group.js +37 -0
- package/dist/tools/create-category-group.js.map +1 -0
- package/dist/tools/create-category.d.ts +59 -0
- package/dist/tools/create-category.d.ts.map +1 -0
- package/dist/tools/create-category.js +63 -0
- package/dist/tools/create-category.js.map +1 -0
- package/dist/tools/delete-category-group.d.ts +8 -0
- package/dist/tools/delete-category-group.d.ts.map +1 -0
- package/dist/tools/delete-category-group.js +33 -0
- package/dist/tools/delete-category-group.js.map +1 -0
- package/dist/tools/delete-category.d.ts +7 -0
- package/dist/tools/delete-category.d.ts.map +1 -0
- package/dist/tools/delete-category.js +28 -0
- package/dist/tools/delete-category.js.map +1 -0
- package/dist/tools/move-category-budget.d.ts.map +1 -1
- package/dist/tools/move-category-budget.js +13 -18
- package/dist/tools/move-category-budget.js.map +1 -1
- package/dist/tools/schemas.d.ts +80 -1
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +238 -4
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/snooze-category-goal.d.ts +10 -0
- package/dist/tools/snooze-category-goal.d.ts.map +1 -0
- package/dist/tools/snooze-category-goal.js +53 -0
- package/dist/tools/snooze-category-goal.js.map +1 -0
- package/dist/tools/update-category-budget.d.ts.map +1 -1
- package/dist/tools/update-category-budget.js +3 -6
- package/dist/tools/update-category-budget.js.map +1 -1
- package/dist/tools/update-category.d.ts +61 -0
- package/dist/tools/update-category.d.ts.map +1 -0
- package/dist/tools/update-category.js +46 -0
- package/dist/tools/update-category.js.map +1 -0
- package/dist/tools.json +774 -1
- package/package.json +3 -3
package/dist/adapter.iife.js
CHANGED
|
@@ -14270,6 +14270,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14270
14270
|
};
|
|
14271
14271
|
var toMilliunits = (amount) => Math.round(amount * 1e3);
|
|
14272
14272
|
var notTombstone = (x) => !x.is_tombstone;
|
|
14273
|
+
var findCategory = (entities, id) => {
|
|
14274
|
+
const c = (entities?.be_subcategories ?? []).find((s) => s.id === id && notTombstone(s));
|
|
14275
|
+
if (!c) throw ToolError.notFound(`Category not found: ${id}`);
|
|
14276
|
+
return c;
|
|
14277
|
+
};
|
|
14278
|
+
var findCategoryGroup = (entities, id) => {
|
|
14279
|
+
const g = (entities?.be_master_categories ?? []).find((m) => m.id === id && notTombstone(m));
|
|
14280
|
+
if (!g) throw ToolError.notFound(`Category group not found: ${id}`);
|
|
14281
|
+
return g;
|
|
14282
|
+
};
|
|
14283
|
+
var assertCategoryGroupDeletable = (group) => {
|
|
14284
|
+
if (group.deletable !== true) {
|
|
14285
|
+
throw ToolError.validation(`Category group "${group.name}" is not deletable.`);
|
|
14286
|
+
}
|
|
14287
|
+
};
|
|
14288
|
+
var assertCategoryDeletable = (category) => {
|
|
14289
|
+
if (category.entities_account_id != null || category.type !== CATEGORY_TYPE_DEFAULT) {
|
|
14290
|
+
throw ToolError.validation(`Category "${category.name}" is system-managed and cannot be deleted.`);
|
|
14291
|
+
}
|
|
14292
|
+
};
|
|
14293
|
+
var nextTopSortableIndex = (rows, step = 10) => {
|
|
14294
|
+
let min = 0;
|
|
14295
|
+
for (const r of rows) {
|
|
14296
|
+
if (typeof r.sortable_index === "number" && r.sortable_index < min) min = r.sortable_index;
|
|
14297
|
+
}
|
|
14298
|
+
return min - step;
|
|
14299
|
+
};
|
|
14273
14300
|
var userSchema = external_exports.object({
|
|
14274
14301
|
id: external_exports.string().describe("User ID"),
|
|
14275
14302
|
first_name: external_exports.string().describe("First name"),
|
|
@@ -14390,6 +14417,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14390
14417
|
/** Category-to-category transfer. */
|
|
14391
14418
|
MOVEMENT: "manual_movement"
|
|
14392
14419
|
};
|
|
14420
|
+
var GOAL_TYPE = {
|
|
14421
|
+
/** "Set aside" or "Refill" — `goal_needs_whole_amount` differentiates. */
|
|
14422
|
+
NEED: "NEED",
|
|
14423
|
+
/** "Have a balance of" — no date, no cadence. */
|
|
14424
|
+
TARGET_BALANCE: "TB",
|
|
14425
|
+
/** "Have a balance of by date" — one-shot with `goal_target_date`. */
|
|
14426
|
+
TARGET_BY_DATE: "TBD",
|
|
14427
|
+
/** Debt payment goals on debt-account categories. */
|
|
14428
|
+
DEBT: "DEBT",
|
|
14429
|
+
/** Legacy "Monthly Funding". The modern UI no longer creates these but they
|
|
14430
|
+
still exist on older categories and the API still honors them. */
|
|
14431
|
+
MONTHLY_FUNDING: "MF"
|
|
14432
|
+
};
|
|
14433
|
+
var CATEGORY_TYPE_DEFAULT = "DFT";
|
|
14393
14434
|
var SUBCATEGORY_BUDGET_PREFIX = "mcb";
|
|
14394
14435
|
var MONTHLY_BUDGET_PREFIX = "mb";
|
|
14395
14436
|
var toMonthKey = (month) => month.substring(0, 7);
|
|
@@ -14412,6 +14453,128 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14412
14453
|
blue: "Blue",
|
|
14413
14454
|
purple: "Purple"
|
|
14414
14455
|
};
|
|
14456
|
+
var cadenceWireValue = {
|
|
14457
|
+
weekly: 2,
|
|
14458
|
+
monthly: 1,
|
|
14459
|
+
yearly: 13
|
|
14460
|
+
};
|
|
14461
|
+
var isValidCalendarDate = (s) => {
|
|
14462
|
+
const parts = s.split("-").map(Number);
|
|
14463
|
+
const year = parts[0] ?? 0;
|
|
14464
|
+
const month = parts[1] ?? 0;
|
|
14465
|
+
const day = parts[2] ?? 0;
|
|
14466
|
+
if (month < 1 || month > 12) return false;
|
|
14467
|
+
const maxDay = new Date(year, month, 0).getDate();
|
|
14468
|
+
return day >= 1 && day <= maxDay;
|
|
14469
|
+
};
|
|
14470
|
+
var needGoalShape = {
|
|
14471
|
+
target: external_exports.number().positive().describe("Goal amount in currency units (e.g. 50 for $50)"),
|
|
14472
|
+
cadence: external_exports.enum(["weekly", "monthly", "yearly"]).optional().describe("How often the goal recurs. Defaults to monthly."),
|
|
14473
|
+
every: external_exports.number().int().min(1).optional().describe("Multiplier on cadence (e.g. cadence=monthly + every=5 means every 5 months). Defaults to 1."),
|
|
14474
|
+
day: external_exports.number().int().min(0).max(31).optional().describe("Day-of-week (0=Sunday, 6=Saturday) for weekly cadence, or day-of-month (1-31) for monthly cadence."),
|
|
14475
|
+
start_date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD").refine(isValidCalendarDate, "Date must be a valid calendar date").optional().describe("First occurrence date (YYYY-MM-DD). Required for yearly cadence; optional for others.")
|
|
14476
|
+
};
|
|
14477
|
+
var yearlyNeedRefine = (data, ctx) => {
|
|
14478
|
+
if (data.cadence === "yearly" && !data.start_date) {
|
|
14479
|
+
ctx.addIssue({
|
|
14480
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14481
|
+
message: 'start_date is required when cadence is "yearly"',
|
|
14482
|
+
path: ["start_date"]
|
|
14483
|
+
});
|
|
14484
|
+
}
|
|
14485
|
+
};
|
|
14486
|
+
var needCadenceDayRefine = (data, ctx) => {
|
|
14487
|
+
if (data.day === void 0) return;
|
|
14488
|
+
const cadence = data.cadence ?? "monthly";
|
|
14489
|
+
if (cadence === "weekly" && (data.day < 0 || data.day > 6)) {
|
|
14490
|
+
ctx.addIssue({
|
|
14491
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14492
|
+
message: "For weekly cadence, day must be 0\u20136 (0=Sunday, 6=Saturday)",
|
|
14493
|
+
path: ["day"]
|
|
14494
|
+
});
|
|
14495
|
+
} else if (cadence === "monthly" && (data.day < 1 || data.day > 31)) {
|
|
14496
|
+
ctx.addIssue({
|
|
14497
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14498
|
+
message: "For monthly cadence, day must be 1\u201331",
|
|
14499
|
+
path: ["day"]
|
|
14500
|
+
});
|
|
14501
|
+
} else if (cadence === "yearly") {
|
|
14502
|
+
ctx.addIssue({
|
|
14503
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14504
|
+
message: "For yearly cadence, use start_date to set the recurrence anchor instead of day",
|
|
14505
|
+
path: ["day"]
|
|
14506
|
+
});
|
|
14507
|
+
}
|
|
14508
|
+
};
|
|
14509
|
+
var goalSpecSchema = external_exports.discriminatedUnion("type", [
|
|
14510
|
+
external_exports.object({ type: external_exports.literal("set_aside"), ...needGoalShape }).strict().superRefine(yearlyNeedRefine).superRefine(needCadenceDayRefine),
|
|
14511
|
+
external_exports.object({ type: external_exports.literal("refill"), ...needGoalShape }).strict().superRefine(yearlyNeedRefine).superRefine(needCadenceDayRefine),
|
|
14512
|
+
external_exports.object({
|
|
14513
|
+
type: external_exports.literal("target_balance"),
|
|
14514
|
+
target: external_exports.number().positive().describe("Balance to maintain in currency units")
|
|
14515
|
+
}).strict(),
|
|
14516
|
+
external_exports.object({
|
|
14517
|
+
type: external_exports.literal("target_by_date"),
|
|
14518
|
+
target: external_exports.number().positive().describe("Target balance to have by the given date in currency units"),
|
|
14519
|
+
date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD").refine(isValidCalendarDate, "Date must be a valid calendar date").describe("Target date YYYY-MM-DD")
|
|
14520
|
+
}).strict(),
|
|
14521
|
+
external_exports.object({
|
|
14522
|
+
type: external_exports.literal("debt"),
|
|
14523
|
+
target: external_exports.number().positive().describe("Monthly payment amount in currency units"),
|
|
14524
|
+
day: external_exports.number().int().min(1).max(31).optional().describe("Day of month the payment is due (1-31). Defaults to 1.")
|
|
14525
|
+
}).strict(),
|
|
14526
|
+
external_exports.object({ type: external_exports.literal("none") }).strict()
|
|
14527
|
+
]);
|
|
14528
|
+
var NO_GOAL_FIELDS = {
|
|
14529
|
+
goal_type: null,
|
|
14530
|
+
goal_created_on: null,
|
|
14531
|
+
goal_needs_whole_amount: null,
|
|
14532
|
+
goal_target_amount: 0,
|
|
14533
|
+
goal_target_date: null,
|
|
14534
|
+
goal_cadence: null,
|
|
14535
|
+
goal_cadence_frequency: null,
|
|
14536
|
+
goal_day: null
|
|
14537
|
+
};
|
|
14538
|
+
var buildGoalFields = (goal) => {
|
|
14539
|
+
if (!goal || goal.type === "none") return { ...NO_GOAL_FIELDS };
|
|
14540
|
+
const now = /* @__PURE__ */ new Date();
|
|
14541
|
+
const createdOn = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-01`;
|
|
14542
|
+
const base = { ...NO_GOAL_FIELDS, goal_created_on: createdOn };
|
|
14543
|
+
switch (goal.type) {
|
|
14544
|
+
case "set_aside":
|
|
14545
|
+
case "refill": {
|
|
14546
|
+
const cadence = goal.cadence ?? "monthly";
|
|
14547
|
+
return {
|
|
14548
|
+
...base,
|
|
14549
|
+
goal_type: GOAL_TYPE.NEED,
|
|
14550
|
+
goal_needs_whole_amount: goal.type === "set_aside",
|
|
14551
|
+
goal_target_amount: toMilliunits(goal.target),
|
|
14552
|
+
goal_cadence: cadenceWireValue[cadence],
|
|
14553
|
+
goal_cadence_frequency: goal.every ?? 1,
|
|
14554
|
+
goal_day: cadence === "yearly" ? null : goal.day ?? null,
|
|
14555
|
+
goal_target_date: goal.start_date ?? null
|
|
14556
|
+
};
|
|
14557
|
+
}
|
|
14558
|
+
case "target_balance":
|
|
14559
|
+
return { ...base, goal_type: GOAL_TYPE.TARGET_BALANCE, goal_target_amount: toMilliunits(goal.target) };
|
|
14560
|
+
case "target_by_date":
|
|
14561
|
+
return {
|
|
14562
|
+
...base,
|
|
14563
|
+
goal_type: GOAL_TYPE.TARGET_BY_DATE,
|
|
14564
|
+
goal_target_amount: toMilliunits(goal.target),
|
|
14565
|
+
goal_target_date: goal.date
|
|
14566
|
+
};
|
|
14567
|
+
case "debt":
|
|
14568
|
+
return {
|
|
14569
|
+
...base,
|
|
14570
|
+
goal_type: GOAL_TYPE.DEBT,
|
|
14571
|
+
goal_target_amount: toMilliunits(goal.target),
|
|
14572
|
+
goal_cadence: cadenceWireValue.monthly,
|
|
14573
|
+
goal_cadence_frequency: 1,
|
|
14574
|
+
goal_day: goal.day ?? 1
|
|
14575
|
+
};
|
|
14576
|
+
}
|
|
14577
|
+
};
|
|
14415
14578
|
var resolvePayee = (existingPayees, payeeName) => {
|
|
14416
14579
|
const target = payeeName.toLowerCase();
|
|
14417
14580
|
const match = existingPayees.find((p) => notTombstone(p) && p.name?.toLowerCase() === target);
|
|
@@ -14434,7 +14597,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14434
14597
|
};
|
|
14435
14598
|
return { payeeId, newPayee };
|
|
14436
14599
|
};
|
|
14437
|
-
var buildAccountCalcMap = (entities) => new Map(
|
|
14600
|
+
var buildAccountCalcMap = (entities) => new Map(
|
|
14601
|
+
(entities.be_account_calculations ?? []).filter((c) => c.entities_account_id).map((c) => [c.entities_account_id, c])
|
|
14602
|
+
);
|
|
14438
14603
|
var buildMonthlyBudgetCalcMap = (calcs) => {
|
|
14439
14604
|
const map2 = /* @__PURE__ */ new Map();
|
|
14440
14605
|
for (const calc of calcs) {
|
|
@@ -14481,7 +14646,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14481
14646
|
budgeted: budget?.budgeted ?? c.budgeted,
|
|
14482
14647
|
activity: (calc?.cash_outflows ?? 0) + (calc?.credit_outflows ?? 0),
|
|
14483
14648
|
balance: calc?.balance ?? c.balance,
|
|
14484
|
-
goal_target: calc?.goal_target ?? c.goal_target,
|
|
14485
14649
|
goal_percentage_complete: calc?.goal_percentage_complete ?? c.goal_percentage_complete
|
|
14486
14650
|
});
|
|
14487
14651
|
};
|
|
@@ -14561,7 +14725,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14561
14725
|
activity_milliunits: c.activity ?? 0,
|
|
14562
14726
|
balance_milliunits: c.balance ?? 0,
|
|
14563
14727
|
goal_type: c.goal_type ?? "",
|
|
14564
|
-
|
|
14728
|
+
// The "goal target" the user configured is stored on the category itself:
|
|
14729
|
+
// - MF (legacy Monthly Funding): the static target lives in `monthly_funding`
|
|
14730
|
+
// - All modern goal types (NEED, TB, TBD, DEBT): use `goal_target_amount`
|
|
14731
|
+
// The calc's `goal_target` is YNAB's dynamically-computed "needed this month",
|
|
14732
|
+
// which for refill goals returns max(0, target - current_balance) — surprising
|
|
14733
|
+
// and not what users mean when they ask for the goal target.
|
|
14734
|
+
goal_target: formatMilliunits(
|
|
14735
|
+
(c.goal_type === GOAL_TYPE.MONTHLY_FUNDING ? c.monthly_funding : c.goal_target_amount) ?? 0
|
|
14736
|
+
),
|
|
14565
14737
|
goal_percentage_complete: c.goal_percentage_complete ?? 0
|
|
14566
14738
|
});
|
|
14567
14739
|
var mapPayee = (p) => ({
|
|
@@ -14638,6 +14810,105 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14638
14810
|
deleted: s.is_tombstone === true
|
|
14639
14811
|
});
|
|
14640
14812
|
|
|
14813
|
+
// src/tools/create-category.ts
|
|
14814
|
+
var createCategory = defineTool({
|
|
14815
|
+
name: "create_category",
|
|
14816
|
+
displayName: "Create Category",
|
|
14817
|
+
description: 'Create a new category in an existing category group. Optionally set an initial goal: "set_aside" (set aside X per cadence), "refill" (refill the balance up to X per cadence), "target_balance" (have a balance of X), or "target_by_date" (have a balance of X by a specific date). NEED-style goals (set_aside, refill) accept weekly/monthly/yearly cadence. Debt goals are not supported for new categories \u2014 they only apply to existing debt-account categories.',
|
|
14818
|
+
summary: "Create a new budget category",
|
|
14819
|
+
icon: "plus",
|
|
14820
|
+
group: "Categories",
|
|
14821
|
+
input: external_exports.object({
|
|
14822
|
+
group_id: external_exports.string().min(1).describe("Category group ID to create the category in"),
|
|
14823
|
+
name: external_exports.string().min(1).describe("Name of the new category"),
|
|
14824
|
+
goal: goalSpecSchema.optional().describe("Optional initial goal for the category"),
|
|
14825
|
+
note: external_exports.string().optional().describe("Optional note for the category")
|
|
14826
|
+
}),
|
|
14827
|
+
output: external_exports.object({
|
|
14828
|
+
category: categorySchema
|
|
14829
|
+
}),
|
|
14830
|
+
handle: async (params) => {
|
|
14831
|
+
if (params.goal?.type === "debt") {
|
|
14832
|
+
throw ToolError.validation("Debt goals can only be set on debt-account categories.");
|
|
14833
|
+
}
|
|
14834
|
+
const planId = getPlanId();
|
|
14835
|
+
const categoryId = crypto.randomUUID();
|
|
14836
|
+
const budget = await syncBudget(planId);
|
|
14837
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
14838
|
+
assertCategoryGroupDeletable(findCategoryGroup(budget.changed_entities, params.group_id));
|
|
14839
|
+
const childCategories = (budget.changed_entities?.be_subcategories ?? []).filter(
|
|
14840
|
+
(c) => c.entities_master_category_id === params.group_id
|
|
14841
|
+
);
|
|
14842
|
+
const monthKey = currentMonthKey();
|
|
14843
|
+
const categoryEntry = {
|
|
14844
|
+
id: categoryId,
|
|
14845
|
+
is_tombstone: false,
|
|
14846
|
+
entities_master_category_id: params.group_id,
|
|
14847
|
+
entities_account_id: null,
|
|
14848
|
+
internal_name: null,
|
|
14849
|
+
sortable_index: nextTopSortableIndex(childCategories, 5),
|
|
14850
|
+
name: params.name,
|
|
14851
|
+
type: CATEGORY_TYPE_DEFAULT,
|
|
14852
|
+
note: params.note ?? null,
|
|
14853
|
+
monthly_funding: 0,
|
|
14854
|
+
is_hidden: false,
|
|
14855
|
+
pinned_index: null,
|
|
14856
|
+
pinned_goal_index: null,
|
|
14857
|
+
...buildGoalFields(params.goal)
|
|
14858
|
+
};
|
|
14859
|
+
const budgetEntry = {
|
|
14860
|
+
id: formatSubcategoryBudgetId(monthKey, categoryId),
|
|
14861
|
+
is_tombstone: false,
|
|
14862
|
+
entities_monthly_budget_id: formatMonthlyBudgetId(monthKey, planId),
|
|
14863
|
+
entities_subcategory_id: categoryId,
|
|
14864
|
+
budgeted: 0
|
|
14865
|
+
};
|
|
14866
|
+
await syncWrite(
|
|
14867
|
+
planId,
|
|
14868
|
+
{
|
|
14869
|
+
be_subcategories: [categoryEntry],
|
|
14870
|
+
be_monthly_subcategory_budgets: [budgetEntry]
|
|
14871
|
+
},
|
|
14872
|
+
serverKnowledge
|
|
14873
|
+
);
|
|
14874
|
+
return { category: mapCategory(categoryEntry) };
|
|
14875
|
+
}
|
|
14876
|
+
});
|
|
14877
|
+
|
|
14878
|
+
// src/tools/create-category-group.ts
|
|
14879
|
+
var createCategoryGroup = defineTool({
|
|
14880
|
+
name: "create_category_group",
|
|
14881
|
+
displayName: "Create Category Group",
|
|
14882
|
+
description: "Create a new category group in the active YNAB plan.",
|
|
14883
|
+
summary: "Create a category group",
|
|
14884
|
+
icon: "folder-plus",
|
|
14885
|
+
group: "Categories",
|
|
14886
|
+
input: external_exports.object({
|
|
14887
|
+
name: external_exports.string().min(1).describe("Name of the new category group")
|
|
14888
|
+
}),
|
|
14889
|
+
output: external_exports.object({
|
|
14890
|
+
group: categoryGroupSchema
|
|
14891
|
+
}),
|
|
14892
|
+
handle: async (params) => {
|
|
14893
|
+
const planId = getPlanId();
|
|
14894
|
+
const groupId = crypto.randomUUID();
|
|
14895
|
+
const budget = await syncBudget(planId);
|
|
14896
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
14897
|
+
const groupEntry = {
|
|
14898
|
+
id: groupId,
|
|
14899
|
+
is_tombstone: false,
|
|
14900
|
+
internal_name: "",
|
|
14901
|
+
deletable: true,
|
|
14902
|
+
sortable_index: nextTopSortableIndex(budget.changed_entities?.be_master_categories ?? []),
|
|
14903
|
+
name: params.name,
|
|
14904
|
+
note: "",
|
|
14905
|
+
is_hidden: false
|
|
14906
|
+
};
|
|
14907
|
+
await syncWrite(planId, { be_master_categories: [groupEntry] }, serverKnowledge);
|
|
14908
|
+
return { group: mapCategoryGroup(groupEntry) };
|
|
14909
|
+
}
|
|
14910
|
+
});
|
|
14911
|
+
|
|
14641
14912
|
// src/tools/create-transaction.ts
|
|
14642
14913
|
var createTransaction = defineTool({
|
|
14643
14914
|
name: "create_transaction",
|
|
@@ -14733,6 +15004,71 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14733
15004
|
}
|
|
14734
15005
|
});
|
|
14735
15006
|
|
|
15007
|
+
// src/tools/delete-category.ts
|
|
15008
|
+
var deleteCategory = defineTool({
|
|
15009
|
+
name: "delete_category",
|
|
15010
|
+
displayName: "Delete Category",
|
|
15011
|
+
description: "Delete a category from the active YNAB plan. This is a soft delete (tombstone). Existing transactions assigned to this category remain in place but the category will no longer appear in budget views.",
|
|
15012
|
+
summary: "Delete a category",
|
|
15013
|
+
icon: "trash-2",
|
|
15014
|
+
group: "Categories",
|
|
15015
|
+
input: external_exports.object({
|
|
15016
|
+
category_id: external_exports.string().min(1).describe("Category ID to delete")
|
|
15017
|
+
}),
|
|
15018
|
+
output: external_exports.object({
|
|
15019
|
+
success: external_exports.boolean()
|
|
15020
|
+
}),
|
|
15021
|
+
handle: async (params) => {
|
|
15022
|
+
const planId = getPlanId();
|
|
15023
|
+
const budget = await syncBudget(planId);
|
|
15024
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15025
|
+
const existing2 = findCategory(budget.changed_entities, params.category_id);
|
|
15026
|
+
assertCategoryDeletable(existing2);
|
|
15027
|
+
await syncWrite(
|
|
15028
|
+
planId,
|
|
15029
|
+
{ be_subcategories: [{ ...existing2, is_tombstone: true }] },
|
|
15030
|
+
serverKnowledge
|
|
15031
|
+
);
|
|
15032
|
+
return { success: true };
|
|
15033
|
+
}
|
|
15034
|
+
});
|
|
15035
|
+
|
|
15036
|
+
// src/tools/delete-category-group.ts
|
|
15037
|
+
var deleteCategoryGroup = defineTool({
|
|
15038
|
+
name: "delete_category_group",
|
|
15039
|
+
displayName: "Delete Category Group",
|
|
15040
|
+
description: "Delete a category group and all of its child categories from the active YNAB plan. This is a soft delete (tombstone). Internal/non-deletable groups (Credit Card Payments, Hidden Categories, Internal Master Category) cannot be deleted.",
|
|
15041
|
+
summary: "Delete a category group and its children",
|
|
15042
|
+
icon: "folder-x",
|
|
15043
|
+
group: "Categories",
|
|
15044
|
+
input: external_exports.object({
|
|
15045
|
+
group_id: external_exports.string().min(1).describe("Category group ID to delete")
|
|
15046
|
+
}),
|
|
15047
|
+
output: external_exports.object({
|
|
15048
|
+
success: external_exports.boolean(),
|
|
15049
|
+
deleted_category_count: external_exports.number().describe("Number of child categories that were also tombstoned")
|
|
15050
|
+
}),
|
|
15051
|
+
handle: async (params) => {
|
|
15052
|
+
const planId = getPlanId();
|
|
15053
|
+
const budget = await syncBudget(planId);
|
|
15054
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15055
|
+
const group = findCategoryGroup(budget.changed_entities, params.group_id);
|
|
15056
|
+
assertCategoryGroupDeletable(group);
|
|
15057
|
+
const childCategories = (budget.changed_entities?.be_subcategories ?? []).filter(
|
|
15058
|
+
(c) => c.entities_master_category_id === params.group_id && notTombstone(c)
|
|
15059
|
+
);
|
|
15060
|
+
await syncWrite(
|
|
15061
|
+
planId,
|
|
15062
|
+
{
|
|
15063
|
+
be_master_categories: [{ ...group, is_tombstone: true }],
|
|
15064
|
+
be_subcategories: childCategories.map((c) => ({ ...c, is_tombstone: true }))
|
|
15065
|
+
},
|
|
15066
|
+
serverKnowledge
|
|
15067
|
+
);
|
|
15068
|
+
return { success: true, deleted_category_count: childCategories.length };
|
|
15069
|
+
}
|
|
15070
|
+
});
|
|
15071
|
+
|
|
14736
15072
|
// src/tools/delete-transaction.ts
|
|
14737
15073
|
var deleteTransaction = defineTool({
|
|
14738
15074
|
name: "delete_transaction",
|
|
@@ -15138,17 +15474,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15138
15474
|
const monthlyBudgetId = formatMonthlyBudgetId(monthKey, planId);
|
|
15139
15475
|
const budget = await syncBudget(planId);
|
|
15140
15476
|
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15141
|
-
const subcategories = budget.changed_entities?.be_subcategories ?? [];
|
|
15142
15477
|
const existingBudgets = budget.changed_entities?.be_monthly_subcategory_budgets ?? [];
|
|
15143
|
-
const
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
const
|
|
15149
|
-
const toCategory = params.to_category_id ? findCategory(params.to_category_id) : null;
|
|
15150
|
-
const fromBudgetId = fromCategory ? formatSubcategoryBudgetId(monthKey, fromCategory.id) : null;
|
|
15151
|
-
const toBudgetId = toCategory ? formatSubcategoryBudgetId(monthKey, toCategory.id) : null;
|
|
15478
|
+
const fromCategoryId = params.from_category_id;
|
|
15479
|
+
const toCategoryId = params.to_category_id;
|
|
15480
|
+
const fromCategory = fromCategoryId ? findCategory(budget.changed_entities, fromCategoryId) : null;
|
|
15481
|
+
const toCategory = toCategoryId ? findCategory(budget.changed_entities, toCategoryId) : null;
|
|
15482
|
+
const fromBudgetId = fromCategoryId ? formatSubcategoryBudgetId(monthKey, fromCategoryId) : null;
|
|
15483
|
+
const toBudgetId = toCategoryId ? formatSubcategoryBudgetId(monthKey, toCategoryId) : null;
|
|
15152
15484
|
const buildEntry = (categoryId, budgetId, signedDelta) => {
|
|
15153
15485
|
const current = existingBudgets.find((b) => b.id === budgetId && notTombstone(b))?.budgeted ?? 0;
|
|
15154
15486
|
return {
|
|
@@ -15160,9 +15492,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15160
15492
|
};
|
|
15161
15493
|
};
|
|
15162
15494
|
const budgetEntries = [];
|
|
15163
|
-
if (
|
|
15164
|
-
if (
|
|
15165
|
-
const source =
|
|
15495
|
+
if (fromCategoryId && fromBudgetId) budgetEntries.push(buildEntry(fromCategoryId, fromBudgetId, -milliunits));
|
|
15496
|
+
if (toCategoryId && toBudgetId) budgetEntries.push(buildEntry(toCategoryId, toBudgetId, milliunits));
|
|
15497
|
+
const source = fromCategoryId && toCategoryId ? MONEY_MOVEMENT_SOURCE.MOVEMENT : MONEY_MOVEMENT_SOURCE.ASSIGN;
|
|
15166
15498
|
const result = await syncWrite(
|
|
15167
15499
|
planId,
|
|
15168
15500
|
{
|
|
@@ -15198,6 +15530,96 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15198
15530
|
}
|
|
15199
15531
|
});
|
|
15200
15532
|
|
|
15533
|
+
// src/tools/snooze-category-goal.ts
|
|
15534
|
+
var snoozeCategoryGoal = defineTool({
|
|
15535
|
+
name: "snooze_category_goal",
|
|
15536
|
+
displayName: "Snooze Category Goal",
|
|
15537
|
+
description: "Snooze a category goal for a specific month so it does not appear as needing funding for that month. Pass snooze=false to un-snooze.",
|
|
15538
|
+
summary: "Snooze a category goal for a month",
|
|
15539
|
+
icon: "bell-off",
|
|
15540
|
+
group: "Categories",
|
|
15541
|
+
input: external_exports.object({
|
|
15542
|
+
category_id: external_exports.string().min(1).describe("Category ID whose goal to snooze"),
|
|
15543
|
+
month: external_exports.string().regex(/^\d{4}-\d{2}(-\d{2})?$/, "Month must be YYYY-MM or YYYY-MM-DD").describe("Month in YYYY-MM format (e.g. 2026-04)"),
|
|
15544
|
+
snooze: external_exports.boolean().optional().describe("true to snooze (default), false to un-snooze")
|
|
15545
|
+
}),
|
|
15546
|
+
output: external_exports.object({
|
|
15547
|
+
success: external_exports.boolean(),
|
|
15548
|
+
snoozed_at: external_exports.string().nullable().describe("ISO timestamp the goal was snoozed at, or null if un-snoozed")
|
|
15549
|
+
}),
|
|
15550
|
+
handle: async (params) => {
|
|
15551
|
+
const planId = getPlanId();
|
|
15552
|
+
const monthKey = toMonthKey(params.month);
|
|
15553
|
+
const budgetId = formatSubcategoryBudgetId(monthKey, params.category_id);
|
|
15554
|
+
const monthlyBudgetId = formatMonthlyBudgetId(monthKey, planId);
|
|
15555
|
+
const shouldSnooze = params.snooze ?? true;
|
|
15556
|
+
const budget = await syncBudget(planId);
|
|
15557
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15558
|
+
const category = findCategory(budget.changed_entities, params.category_id);
|
|
15559
|
+
if (!category.goal_type) {
|
|
15560
|
+
throw ToolError.validation(`Category "${category.name}" has no goal to snooze.`);
|
|
15561
|
+
}
|
|
15562
|
+
const existing2 = (budget.changed_entities?.be_monthly_subcategory_budgets ?? []).find(
|
|
15563
|
+
(b) => b.id === budgetId && notTombstone(b)
|
|
15564
|
+
);
|
|
15565
|
+
const snoozedAt = shouldSnooze ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
15566
|
+
const budgetEntry = {
|
|
15567
|
+
...existing2 ?? {},
|
|
15568
|
+
id: budgetId,
|
|
15569
|
+
is_tombstone: false,
|
|
15570
|
+
entities_monthly_budget_id: monthlyBudgetId,
|
|
15571
|
+
entities_subcategory_id: params.category_id,
|
|
15572
|
+
budgeted: existing2?.budgeted ?? 0,
|
|
15573
|
+
goal_snoozed_at: snoozedAt
|
|
15574
|
+
};
|
|
15575
|
+
await syncWrite(planId, { be_monthly_subcategory_budgets: [budgetEntry] }, serverKnowledge);
|
|
15576
|
+
return { success: true, snoozed_at: snoozedAt };
|
|
15577
|
+
}
|
|
15578
|
+
});
|
|
15579
|
+
|
|
15580
|
+
// src/tools/update-category.ts
|
|
15581
|
+
var updateCategory = defineTool({
|
|
15582
|
+
name: "update_category",
|
|
15583
|
+
displayName: "Update Category",
|
|
15584
|
+
description: 'Rename a category, change its group, set/clear its goal, or hide/unhide it. Only specified fields change; omitted fields remain unchanged. Goal types: "set_aside" / "refill" (recurring NEED with optional cadence), "target_balance" (have $X), "target_by_date" (have $X by date), "debt" (recurring debt payment), or "none" to clear an existing goal.',
|
|
15585
|
+
summary: "Update a category",
|
|
15586
|
+
icon: "pencil",
|
|
15587
|
+
group: "Categories",
|
|
15588
|
+
input: external_exports.object({
|
|
15589
|
+
category_id: external_exports.string().min(1).describe("Category ID to update"),
|
|
15590
|
+
name: external_exports.string().min(1).optional().describe("New name"),
|
|
15591
|
+
group_id: external_exports.string().min(1).optional().describe("New parent category group ID (to move the category)"),
|
|
15592
|
+
goal: goalSpecSchema.optional().describe('New goal definition. Pass { type: "none" } to clear the goal.'),
|
|
15593
|
+
hidden: external_exports.boolean().optional().describe("Hide or unhide the category"),
|
|
15594
|
+
note: external_exports.string().optional().describe("New note (pass empty string to clear)")
|
|
15595
|
+
}),
|
|
15596
|
+
output: external_exports.object({
|
|
15597
|
+
category: categorySchema
|
|
15598
|
+
}),
|
|
15599
|
+
handle: async (params) => {
|
|
15600
|
+
const planId = getPlanId();
|
|
15601
|
+
const budget = await syncBudget(planId);
|
|
15602
|
+
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15603
|
+
const existing2 = findCategory(budget.changed_entities, params.category_id);
|
|
15604
|
+
if (params.group_id) {
|
|
15605
|
+
assertCategoryGroupDeletable(findCategoryGroup(budget.changed_entities, params.group_id));
|
|
15606
|
+
}
|
|
15607
|
+
if (params.goal?.type === "debt" && existing2.entities_account_id == null) {
|
|
15608
|
+
throw ToolError.validation("Debt goals can only be set on debt-account categories.");
|
|
15609
|
+
}
|
|
15610
|
+
const updated = {
|
|
15611
|
+
...existing2,
|
|
15612
|
+
name: params.name ?? existing2.name,
|
|
15613
|
+
entities_master_category_id: params.group_id ?? existing2.entities_master_category_id,
|
|
15614
|
+
is_hidden: params.hidden ?? existing2.is_hidden,
|
|
15615
|
+
note: params.note ?? existing2.note,
|
|
15616
|
+
...params.goal !== void 0 ? buildGoalFields(params.goal) : {}
|
|
15617
|
+
};
|
|
15618
|
+
await syncWrite(planId, { be_subcategories: [updated] }, serverKnowledge);
|
|
15619
|
+
return { category: mapCategory(updated) };
|
|
15620
|
+
}
|
|
15621
|
+
});
|
|
15622
|
+
|
|
15201
15623
|
// src/tools/update-category-budget.ts
|
|
15202
15624
|
var updateCategoryBudget = defineTool({
|
|
15203
15625
|
name: "update_category_budget",
|
|
@@ -15223,12 +15645,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15223
15645
|
const monthlyBudgetId = formatMonthlyBudgetId(monthKey, planId);
|
|
15224
15646
|
const budget = await syncBudget(planId);
|
|
15225
15647
|
const serverKnowledge = budget.current_server_knowledge ?? 0;
|
|
15226
|
-
const category = (budget.changed_entities
|
|
15227
|
-
(c) => c.id === params.category_id && notTombstone(c)
|
|
15228
|
-
);
|
|
15229
|
-
if (!category) {
|
|
15230
|
-
throw ToolError.notFound(`Category not found: ${params.category_id}`);
|
|
15231
|
-
}
|
|
15648
|
+
const category = findCategory(budget.changed_entities, params.category_id);
|
|
15232
15649
|
const existingBudget = (budget.changed_entities?.be_monthly_subcategory_budgets ?? []).find(
|
|
15233
15650
|
(b) => b.id === budgetId && notTombstone(b)
|
|
15234
15651
|
);
|
|
@@ -15394,8 +15811,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15394
15811
|
getAccount,
|
|
15395
15812
|
// Categories
|
|
15396
15813
|
listCategories,
|
|
15814
|
+
createCategory,
|
|
15815
|
+
updateCategory,
|
|
15816
|
+
deleteCategory,
|
|
15817
|
+
createCategoryGroup,
|
|
15818
|
+
deleteCategoryGroup,
|
|
15397
15819
|
updateCategoryBudget,
|
|
15398
15820
|
moveCategoryBudget,
|
|
15821
|
+
snoozeCategoryGoal,
|
|
15399
15822
|
// Payees
|
|
15400
15823
|
listPayees,
|
|
15401
15824
|
// Transactions
|
|
@@ -15416,7 +15839,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15416
15839
|
};
|
|
15417
15840
|
var src_default = new YnabPlugin();
|
|
15418
15841
|
|
|
15419
|
-
// dist/
|
|
15842
|
+
// dist/_adapter_entry_0fc84288-9dca-44bb-92da-639aeea53a88.ts
|
|
15420
15843
|
if (!globalThis.__openTabs) {
|
|
15421
15844
|
globalThis.__openTabs = {};
|
|
15422
15845
|
} else {
|
|
@@ -15632,5 +16055,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15632
16055
|
};
|
|
15633
16056
|
delete src_default.onDeactivate;
|
|
15634
16057
|
}
|
|
15635
|
-
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["ynab"]){var a=o.adapters["ynab"];a.__adapterHash="
|
|
16058
|
+
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["ynab"]){var a=o.adapters["ynab"];a.__adapterHash="3a2d11d4883f6b89713b9ed3995d0c919e24e6ae5699ae84dc071e130223def6";if(a.tools&&Array.isArray(a.tools)){for(var i=0;i<a.tools.length;i++){Object.freeze(a.tools[i]);}Object.freeze(a.tools);}Object.freeze(a);Object.defineProperty(o.adapters,"ynab",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
|
|
15636
16059
|
//# sourceMappingURL=adapter.iife.js.map
|