@togglhq/cli 1.8.1 → 1.8.3

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/build/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as CommanderError, i as shutdownCliTelemetry, n as captureCliException, r as initCliTelemetry, t as createProgram } from "./program-CH5epPVt.js";
2
+ import { a as CommanderError, i as shutdownCliTelemetry, n as captureCliException, r as initCliTelemetry, t as createProgram } from "./program-B0eN4WNv.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
package/build/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createProgram } from "./program-CH5epPVt.js";
1
+ import { t as createProgram } from "./program-B0eN4WNv.js";
2
2
  export { createProgram };
@@ -10604,24 +10604,6 @@ async function createTimeEntryWithOrg(httpClient, organization_id, workspace_id,
10604
10604
  json: data
10605
10605
  }).json();
10606
10606
  }
10607
- async function getTimeEntriesRangeWithOrgClassic(httpClient, organization_id, workspace_id, { date_from, date_to, task_id, status_id, type, page, per_page, order_by, archived, time_block_id, include_taskless }, signal) {
10608
- return await httpClient.get(`organizations/${organization_id}/workspaces/${workspace_id}/time-entries`, {
10609
- signal,
10610
- searchParams: buildSearchParams$3({
10611
- date_from,
10612
- date_to,
10613
- task_id,
10614
- status_id,
10615
- type,
10616
- page,
10617
- per_page,
10618
- order_by,
10619
- archived,
10620
- time_block_id,
10621
- include_taskless
10622
- })
10623
- }).json();
10624
- }
10625
10607
  async function createTasklessTimeEntryWithOrg(httpClient, organization_id, workspace_id, data, signal) {
10626
10608
  return await httpClient.post(`organizations/${organization_id}/workspaces/${workspace_id}/time-entries`, {
10627
10609
  signal,
@@ -10972,7 +10954,7 @@ async function deleteMyWorkingHours(httpClient, organization_id, data, signal) {
10972
10954
  return response.json();
10973
10955
  }
10974
10956
  //#endregion
10975
- //#region ../cli-core/build/focus-client-B2l1BZIW.js
10957
+ //#region ../cli-core/build/focus-client-D9oJPY4f.js
10976
10958
  /** Request header the API reads to attribute traffic to MCP or CLI. */
10977
10959
  const TOGGL_CLIENT_HEADER = "X-Toggl-Client";
10978
10960
  /** Headers that attribute a raw `fetch` to MCP or CLI (FocusClient sets these itself). */
@@ -11000,6 +10982,20 @@ function normalizeDateFrom(value) {
11000
10982
  function normalizeDateTo(value) {
11001
10983
  return DATE_ONLY_RE.test(value) ? `${value}T23:59:59Z` : value;
11002
10984
  }
10985
+ function timeEntryProjectId(entry) {
10986
+ if (typeof entry.project_id === "number") return entry.project_id;
10987
+ if (typeof entry.project?.id === "number") return entry.project.id;
10988
+ if (typeof entry.task?.project_id === "number") return entry.task.project_id;
10989
+ }
10990
+ function filterTimeEntriesByProjectId(entries, projectId) {
10991
+ return entries.filter((entry) => timeEntryProjectId(entry) === projectId);
10992
+ }
10993
+ function parseOptionalBoolean(value) {
10994
+ if (value === void 0 || value === "") return void 0;
10995
+ if (typeof value === "boolean") return value;
10996
+ if (value === "true") return true;
10997
+ if (value === "false") return false;
10998
+ }
11003
10999
  const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
11004
11000
  const MIME_BY_EXTENSION = {
11005
11001
  ".png": "image/png",
@@ -11556,16 +11552,53 @@ var FocusClient = class {
11556
11552
  const date_from = normalizeDateFrom(rawFrom);
11557
11553
  const date_to = normalizeDateTo(rawTo);
11558
11554
  const type = params.type;
11559
- const response = await this.run("listTimeEntries", () => getTimeEntriesRangeWithOrgClassic(this.http, this.organizationIdNum, this.workspaceIdNum, {
11555
+ const include_taskless = parseOptionalBoolean(params.include_taskless) ?? true;
11556
+ const projectId = parseOptionalInt(params.project_id);
11557
+ const task_id = parseOptionalInt(params.task_id);
11558
+ const time_block_id = parseOptionalInt(params.time_block_id);
11559
+ const requestedPage = parseOptionalInt(params.page) ?? 1;
11560
+ const requestedPerPage = parseOptionalInt(params.per_page);
11561
+ const fetchPage = (page, per_page) => this.run("listTimeEntries", () => this.http.get(`organizations/${this.organizationIdNum}/workspaces/${this.workspaceIdNum}/time-entries`, { searchParams: buildSearchParams$2({
11560
11562
  date_from,
11561
11563
  date_to,
11562
- task_id: parseOptionalInt(params.task_id),
11563
- time_block_id: parseOptionalInt(params.time_block_id),
11564
+ task_id,
11565
+ time_block_id,
11564
11566
  type,
11565
- page: parseOptionalInt(params.page),
11566
- per_page: parseOptionalInt(params.per_page)
11567
- }));
11568
- return this.softValidateResponse("listTimeEntries", PaginatedTimeEntryWithTasksSchema, response);
11567
+ page,
11568
+ per_page,
11569
+ include_taskless,
11570
+ project_id: projectId
11571
+ }) }).json());
11572
+ if (projectId === void 0) {
11573
+ const response = await fetchPage(requestedPage, requestedPerPage);
11574
+ return this.softValidateResponse("listTimeEntries", PaginatedTimeEntryWithTasksSchema, response);
11575
+ }
11576
+ const perPage = requestedPerPage ?? 50;
11577
+ const needed = requestedPage * perPage;
11578
+ const matched = [];
11579
+ const apiPerPage = 100;
11580
+ const maxApiPages = 50;
11581
+ let truncated = false;
11582
+ for (let apiPage = 1; apiPage <= maxApiPages; apiPage += 1) {
11583
+ const response = await fetchPage(apiPage, apiPerPage);
11584
+ const validated = this.softValidateResponse("listTimeEntries", PaginatedTimeEntryWithTasksSchema, response);
11585
+ if (!validated.ok) return validated;
11586
+ const pageRows = validated.data.data;
11587
+ matched.push(...filterTimeEntriesByProjectId(pageRows, projectId));
11588
+ if (pageRows.length < apiPerPage) break;
11589
+ if (matched.length >= needed) break;
11590
+ if (apiPage === maxApiPages) truncated = true;
11591
+ }
11592
+ const start = (requestedPage - 1) * perPage;
11593
+ return {
11594
+ ok: true,
11595
+ data: {
11596
+ page: requestedPage,
11597
+ per_page: perPage,
11598
+ data: matched.slice(start, start + perPage),
11599
+ ...truncated ? { truncated: true } : {}
11600
+ }
11601
+ };
11569
11602
  }
11570
11603
  async startTracking(payload) {
11571
11604
  const input = validatePayload(TimeEntryStartTrackingPayloadSchema, payload, "startTracking payload");
@@ -12956,7 +12989,7 @@ async function runSkillSetup(options) {
12956
12989
  });
12957
12990
  }
12958
12991
  //#endregion
12959
- //#region ../operations/build/validation-v3-RQf0k.js
12992
+ //#region ../operations/build/validation-CbrghKZi.js
12960
12993
  const optionalNumber = /* @__PURE__ */ optional$1(/* @__PURE__ */ number$2());
12961
12994
  const perPageField = (max) => /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ integer(), /* @__PURE__ */ minValue$1(1), /* @__PURE__ */ maxValue$1(max), /* @__PURE__ */ description$1(`Results per page (1-${max})`)));
12962
12995
  const perPageSchema = perPageField(100);
@@ -12982,9 +13015,11 @@ const timeBlockListSchema = /* @__PURE__ */ object$2({
12982
13015
  const timeEntryListSchema = /* @__PURE__ */ object$2({
12983
13016
  date_from: dateField("Range start — date-only (e.g. '2025-01-01') or RFC3339 datetime. Date-only is expanded to T00:00:00Z."),
12984
13017
  date_to: dateField("Range end — date-only (e.g. '2025-01-07') or RFC3339 datetime. Date-only is expanded to T23:59:59Z."),
13018
+ project_id: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ description$1("Filter to entries on this project (task-linked or taskless)"))),
12985
13019
  task_id: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ description$1("Filter by task ID"))),
12986
13020
  time_block_id: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ description$1("Filter by time block ID"))),
12987
13021
  type: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ picklist$1(["activity", "break"]), /* @__PURE__ */ description$1("Filter by entry type"))),
13022
+ include_taskless: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ boolean$1(), /* @__PURE__ */ description$1("Include entries tracked on a project without a task. Defaults to true when omitted."))),
12988
13023
  page: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ description$1("Page number"))),
12989
13024
  per_page: perPageSchema
12990
13025
  });
@@ -13947,20 +13982,31 @@ const timeBlockOverrides = {
13947
13982
  };
13948
13983
  const timeEntryOverrides = {
13949
13984
  "focus.time-entries.list": {
13950
- agentGuidance: "Returns tracked time entries in a date range. Requires date_from and date_to. Accepts date-only strings (YYYY-MM-DD) or RFC3339 datetimes — date-only is automatically expanded: date_from to T00:00:00Z, date_to to T23:59:59Z. Includes task and project context.",
13951
- examples: [{
13952
- description: "List time entries for a week (date-only, expanded to full-day range)",
13953
- data: {
13954
- date_from: "2025-01-01",
13955
- date_to: "2025-01-07"
13956
- }
13957
- }, {
13958
- description: "List time entries with explicit RFC3339 timestamps",
13959
- data: {
13960
- date_from: "2025-01-01T00:00:00Z",
13961
- date_to: "2025-01-07T23:59:59Z"
13985
+ agentGuidance: "Returns tracked time entries in a date range, including taskless entries (tracked on a project with no task). Requires date_from and date_to. Accepts date-only strings (YYYY-MM-DD) or RFC3339 datetimes — date-only is automatically expanded: date_from to T00:00:00Z, date_to to T23:59:59Z. Optional project_id keeps only entries on that project (sent to the API; client also scans up to 50 list pages of 100, then slices page/per_page; not a full workspace scan). If truncated is true, more unfiltered rows exist than were scanned — narrow date_from/date_to. Pass include_taskless:false to hide taskless rows. Includes task and project context.",
13986
+ examples: [
13987
+ {
13988
+ description: "List time entries for a week (date-only, expanded to full-day range)",
13989
+ data: {
13990
+ date_from: "2025-01-01",
13991
+ date_to: "2025-01-07"
13992
+ }
13993
+ },
13994
+ {
13995
+ description: "List time entries with explicit RFC3339 timestamps",
13996
+ data: {
13997
+ date_from: "2025-01-01T00:00:00Z",
13998
+ date_to: "2025-01-07T23:59:59Z"
13999
+ }
14000
+ },
14001
+ {
14002
+ description: "List time entries for one project, including taskless rows",
14003
+ data: {
14004
+ date_from: "2025-01-01",
14005
+ date_to: "2025-01-07",
14006
+ project_id: 42
14007
+ }
13962
14008
  }
13963
- }]
14009
+ ]
13964
14010
  },
13965
14011
  "focus.time-entries.start": {
13966
14012
  agentGuidance: "type defaults to 'activity' if omitted; pass type:'break' to start a break. Optionally provide task_id, project_id, description, and time_block_id. Only one timer can be active at a time — stops the current one if running.",
@@ -14052,6 +14098,10 @@ const userOverrides = {
14052
14098
  }]
14053
14099
  }
14054
14100
  };
14101
+ const versionOverrides = { "focus.version.service": {
14102
+ description: "MCP package version plus the Focus API service revision.",
14103
+ agentGuidance: "mcp.package is this published MCP package version (e.g. 1.8.0). focus_api is the Focus API process revision (git SHA / build number) — it does not change when you update the npm MCP package."
14104
+ } };
14055
14105
  const dateRangeDay = /* @__PURE__ */ pipe$2(/* @__PURE__ */ string$1(), /* @__PURE__ */ regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD"));
14056
14106
  const TimeOffMeUpdateInputSchema = /* @__PURE__ */ intersect([/* @__PURE__ */ object$2({ timeoff_id: /* @__PURE__ */ number$2() }), PublicMeEntryRequestSchema]);
14057
14107
  const emptySchema = /* @__PURE__ */ object$2({});
@@ -24081,6 +24131,7 @@ const focusOperationOverrides = {
24081
24131
  ...readOnlyPostOverrides,
24082
24132
  ...timeBlockOverrides,
24083
24133
  ...timeEntryOverrides,
24134
+ ...versionOverrides,
24084
24135
  ...statusOverrides,
24085
24136
  ...userOverrides,
24086
24137
  ...tagOverrides,
@@ -25673,4 +25724,4 @@ Common commands:
25673
25724
  //#endregion
25674
25725
  export { CommanderError as a, shutdownCliTelemetry as i, captureCliException as n, initCliTelemetry as r, createProgram as t };
25675
25726
 
25676
- //# sourceMappingURL=program-CH5epPVt.js.map
25727
+ //# sourceMappingURL=program-B0eN4WNv.js.map
package/build/program.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createProgram } from "./program-CH5epPVt.js";
1
+ import { t as createProgram } from "./program-B0eN4WNv.js";
2
2
  export { createProgram };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@togglhq/cli",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "Toggl CLI for Toggl 2.0 API operations.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",