@knpkv/jira-clockify 0.2.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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/dist/package.json +47 -0
  4. package/dist/src/bin.js +28 -0
  5. package/dist/src/bin.js.map +1 -0
  6. package/dist/src/cli/auth.js +169 -0
  7. package/dist/src/cli/auth.js.map +1 -0
  8. package/dist/src/cli/config.js +107 -0
  9. package/dist/src/cli/config.js.map +1 -0
  10. package/dist/src/cli/fuzzySelect.js +149 -0
  11. package/dist/src/cli/fuzzySelect.js.map +1 -0
  12. package/dist/src/cli/layers.js +59 -0
  13. package/dist/src/cli/layers.js.map +1 -0
  14. package/dist/src/cli/list.js +27 -0
  15. package/dist/src/cli/list.js.map +1 -0
  16. package/dist/src/cli/setup.js +134 -0
  17. package/dist/src/cli/setup.js.map +1 -0
  18. package/dist/src/cli/timer/discard.js +33 -0
  19. package/dist/src/cli/timer/discard.js.map +1 -0
  20. package/dist/src/cli/timer/edit.js +139 -0
  21. package/dist/src/cli/timer/edit.js.map +1 -0
  22. package/dist/src/cli/timer/index.js +12 -0
  23. package/dist/src/cli/timer/index.js.map +1 -0
  24. package/dist/src/cli/timer/log.js +96 -0
  25. package/dist/src/cli/timer/log.js.map +1 -0
  26. package/dist/src/cli/timer/start.js +156 -0
  27. package/dist/src/cli/timer/start.js.map +1 -0
  28. package/dist/src/cli/timer/status.js +80 -0
  29. package/dist/src/cli/timer/status.js.map +1 -0
  30. package/dist/src/cli/timer/stop.js +96 -0
  31. package/dist/src/cli/timer/stop.js.map +1 -0
  32. package/dist/src/cli/timer.js +7 -0
  33. package/dist/src/cli/timer.js.map +1 -0
  34. package/dist/src/main.js +24 -0
  35. package/dist/src/main.js.map +1 -0
  36. package/dist/src/services/ClockifyAuth.js +77 -0
  37. package/dist/src/services/ClockifyAuth.js.map +1 -0
  38. package/dist/src/services/ConfigService.js +66 -0
  39. package/dist/src/services/ConfigService.js.map +1 -0
  40. package/dist/src/services/StateWriter.js +69 -0
  41. package/dist/src/services/StateWriter.js.map +1 -0
  42. package/dist/src/services/TicketService.js +106 -0
  43. package/dist/src/services/TicketService.js.map +1 -0
  44. package/dist/src/services/TimerService.js +271 -0
  45. package/dist/src/services/TimerService.js.map +1 -0
  46. package/dist/src/tui/App.js +204 -0
  47. package/dist/src/tui/App.js.map +1 -0
  48. package/dist/src/tui/atoms/runtime.js +9 -0
  49. package/dist/src/tui/atoms/runtime.js.map +1 -0
  50. package/dist/src/tui/atoms/tickets.js +17 -0
  51. package/dist/src/tui/atoms/tickets.js.map +1 -0
  52. package/dist/src/tui/atoms/timer.js +31 -0
  53. package/dist/src/tui/atoms/timer.js.map +1 -0
  54. package/dist/src/tui/atoms/ui.js +10 -0
  55. package/dist/src/tui/atoms/ui.js.map +1 -0
  56. package/dist/src/tui/components/BigTimer.js +60 -0
  57. package/dist/src/tui/components/BigTimer.js.map +1 -0
  58. package/dist/src/tui/components/Footer.js +16 -0
  59. package/dist/src/tui/components/Footer.js.map +1 -0
  60. package/dist/src/tui/components/Header.js +16 -0
  61. package/dist/src/tui/components/Header.js.map +1 -0
  62. package/dist/src/tui/components/PopupInput.js +30 -0
  63. package/dist/src/tui/components/PopupInput.js.map +1 -0
  64. package/dist/src/tui/components/PopupMessage.js +47 -0
  65. package/dist/src/tui/components/PopupMessage.js.map +1 -0
  66. package/dist/src/tui/components/TicketList.js +40 -0
  67. package/dist/src/tui/components/TicketList.js.map +1 -0
  68. package/dist/src/tui/components/TicketRow.js +48 -0
  69. package/dist/src/tui/components/TicketRow.js.map +1 -0
  70. package/dist/src/tui/components/TimerDisplay.js +30 -0
  71. package/dist/src/tui/components/TimerDisplay.js.map +1 -0
  72. package/dist/src/tui/components/index.js +12 -0
  73. package/dist/src/tui/components/index.js.map +1 -0
  74. package/dist/src/tui/context/theme.js +15 -0
  75. package/dist/src/tui/context/theme.js.map +1 -0
  76. package/dist/src/tui/hooks/useElapsedTimer.js +35 -0
  77. package/dist/src/tui/hooks/useElapsedTimer.js.map +1 -0
  78. package/dist/src/tui/hooks/useTerminalSize.js +32 -0
  79. package/dist/src/tui/hooks/useTerminalSize.js.map +1 -0
  80. package/dist/src/utils/time.js +23 -0
  81. package/dist/src/utils/time.js.map +1 -0
  82. package/dist/test/TimerService.test.js +355 -0
  83. package/dist/test/TimerService.test.js.map +1 -0
  84. package/dist/tsconfig.tsbuildinfo +1 -0
  85. package/nvim/lua/jcf/branch.lua +26 -0
  86. package/nvim/lua/jcf/float.lua +87 -0
  87. package/nvim/lua/jcf/init.lua +70 -0
  88. package/nvim/lua/jcf/state.lua +56 -0
  89. package/nvim/lua/jcf/statusline.lua +31 -0
  90. package/nvim/lua/jcf/telescope.lua +50 -0
  91. package/nvim/plugin/jcf.vim +2 -0
  92. package/package.json +47 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Timer `log` command — add activity manually.
3
+ *
4
+ * @module
5
+ */
6
+ import { Args, Command, Options } from "@effect/cli";
7
+ import * as HttpClient from "@effect/platform/HttpClient";
8
+ import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
9
+ import { ClockifyApiClient } from "@knpkv/clockify-api-client";
10
+ import { JiraApiClient, toEffect } from "@knpkv/jira-api-client";
11
+ import { JiraAuth } from "@knpkv/jira-cli/JiraAuth";
12
+ import { Console, Effect, Option, Redacted } from "effect";
13
+ import { ClockifyAuth } from "../../services/ClockifyAuth.js";
14
+ export const log = Command.make("log", {
15
+ key: Args.text({ name: "key" }),
16
+ time: Options.text("time").pipe(Options.withAlias("t"), Options.withDescription("Duration (e.g. 1h30m, 2h, 45m)")),
17
+ date: Options.text("date").pipe(Options.withAlias("d"), Options.withDescription("Date (YYYY-MM-DD, default today)"), Options.optional),
18
+ comment: Options.text("comment").pipe(Options.withAlias("c"), Options.withDescription("Worklog comment"), Options.optional)
19
+ }, ({ comment, date, key, time }) => Effect.gen(function* () {
20
+ // Parse duration string like "1h30m", "2h", "45m"
21
+ const durationMatch = time.match(/(?:(\d+)h)?(?:(\d+)m)?/);
22
+ if (!durationMatch || (!durationMatch[1] && !durationMatch[2])) {
23
+ yield* Console.log("Invalid duration. Use format: 1h30m, 2h, 45m");
24
+ return;
25
+ }
26
+ const hours = parseInt(durationMatch[1] ?? "0", 10);
27
+ const minutes = parseInt(durationMatch[2] ?? "0", 10);
28
+ const totalSeconds = Math.max(60, hours * 3600 + minutes * 60);
29
+ // Parse date
30
+ const dateStr = Option.isSome(date) ? date.value : new Date().toISOString().slice(0, 10);
31
+ const started = new Date(`${dateStr}T09:00:00.000Z`);
32
+ if (isNaN(started.getTime())) {
33
+ yield* Console.log("Invalid date. Use format: YYYY-MM-DD");
34
+ return;
35
+ }
36
+ // Validate ticket exists
37
+ const jira = yield* JiraApiClient;
38
+ const issue = yield* toEffect(jira.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
39
+ params: {
40
+ path: { issueIdOrKey: key },
41
+ query: { fields: ["summary", "issuetype", "labels"] }
42
+ }
43
+ })).pipe(Effect.catchAll(() => Effect.succeed(null)));
44
+ if (!issue) {
45
+ yield* Console.log(`Ticket ${key} not found in Jira.`);
46
+ return;
47
+ }
48
+ const fields = issue.fields;
49
+ const summary = typeof fields?.["summary"] === "string" ? fields["summary"] : key;
50
+ yield* Console.log(`Logging: ${key} — ${summary}`);
51
+ yield* Console.log(` Duration: ${hours}h ${minutes}m (${totalSeconds}s)`);
52
+ yield* Console.log(` Date: ${dateStr}`);
53
+ // Create Clockify entry (completed)
54
+ const clockifyAuth = yield* ClockifyAuth;
55
+ const clockifyClient = yield* ClockifyApiClient;
56
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
57
+ let clockifyOk = false;
58
+ if (auth) {
59
+ const end = new Date(started.getTime() + totalSeconds * 1000);
60
+ yield* clockifyClient.createTimeEntry(auth.workspaceId, {
61
+ description: `[${key}] ${summary}`,
62
+ start: started.toISOString(),
63
+ end: end.toISOString()
64
+ }).pipe(Effect.tap(() => Effect.sync(() => {
65
+ clockifyOk = true;
66
+ })), Effect.catchAll(() => Effect.void));
67
+ }
68
+ // Create Jira worklog via raw HTTP
69
+ const jiraAuthSvc = yield* JiraAuth;
70
+ const accessToken = yield* jiraAuthSvc.getAccessToken().pipe(Effect.map((t) => Redacted.value(t)), Effect.catchAll(() => Effect.succeed("")));
71
+ const cloudId = yield* jiraAuthSvc.getCloudId().pipe(Effect.catchAll(() => Effect.succeed("")));
72
+ let jiraOk = false;
73
+ if (accessToken && cloudId) {
74
+ const httpClient = yield* HttpClient.HttpClient;
75
+ const commentText = Option.isSome(comment) ? comment.value : undefined;
76
+ const response = yield* httpClient.execute(HttpClientRequest.post(`https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${key}/worklog`).pipe(HttpClientRequest.setHeader("Authorization", `Bearer ${accessToken}`), HttpClientRequest.setHeader("Content-Type", "application/json"), HttpClientRequest.bodyUnsafeJson({
77
+ started: started.toISOString().replace("Z", "+0000"),
78
+ timeSpentSeconds: totalSeconds,
79
+ ...(commentText ?
80
+ {
81
+ comment: {
82
+ type: "doc",
83
+ version: 1,
84
+ content: [{ type: "paragraph", content: [{ type: "text", text: commentText }] }]
85
+ }
86
+ } :
87
+ {})
88
+ }))).pipe(Effect.catchAll(() => Effect.succeed(null)));
89
+ if (response && response.status >= 200 && response.status < 300) {
90
+ jiraOk = true;
91
+ }
92
+ }
93
+ yield* Console.log(` Clockify: ${clockifyOk ? "✓" : "✗"}`);
94
+ yield* Console.log(` Jira worklog: ${jiraOk ? "✓" : "✗"}`);
95
+ }));
96
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../../../src/cli/timer/log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Timer `start` command.
3
+ *
4
+ * @module
5
+ */
6
+ import { Args, Command, Options, Prompt } from "@effect/cli";
7
+ import { ClockifyApiClient } from "@knpkv/clockify-api-client";
8
+ import { JiraApiClient, toEffect } from "@knpkv/jira-api-client";
9
+ import { Console, Effect, Option, SubscriptionRef } from "effect";
10
+ import { ClockifyAuth } from "../../services/ClockifyAuth.js";
11
+ import { ConfigService } from "../../services/ConfigService.js";
12
+ import { TicketService } from "../../services/TicketService.js";
13
+ import { TimerService } from "../../services/TimerService.js";
14
+ import { fuzzySelect } from "../fuzzySelect.js";
15
+ export const start = Command.make("start", {
16
+ key: Args.text({ name: "key" }).pipe(Args.optional),
17
+ project: Options.text("project").pipe(Options.withAlias("p"), Options.withDescription("Clockify project ID"), Options.optional),
18
+ billable: Options.boolean("billable").pipe(Options.withAlias("b"), Options.withDescription("Mark as billable"), Options.optional),
19
+ saveDefaults: Options.boolean("save-defaults").pipe(Options.withDescription("Save project/billable as defaults"), Options.withDefault(false))
20
+ }, ({ billable, key, project, saveDefaults }) => Effect.gen(function* () {
21
+ const timer = yield* TimerService;
22
+ const ticketService = yield* TicketService;
23
+ const cfg = yield* ConfigService;
24
+ const clockifyAuth = yield* ClockifyAuth;
25
+ const clockifyClient = yield* ClockifyApiClient;
26
+ // Check for running timer
27
+ yield* timer.detectRunning;
28
+ const currentState = yield* SubscriptionRef.get(timer.state);
29
+ if (currentState.active && currentState.ticketKey) {
30
+ const elapsed = currentState.startedAt
31
+ ? Math.floor((Date.now() - currentState.startedAt.getTime()) / 1000)
32
+ : 0;
33
+ const h = Math.floor(elapsed / 3600);
34
+ const m = Math.floor((elapsed % 3600) / 60);
35
+ const s = elapsed % 60;
36
+ yield* Console.log(`Timer running: ${currentState.ticketKey} — ${currentState.summary ?? ""} (${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")})`);
37
+ const action = yield* Prompt.select({
38
+ message: "What to do?",
39
+ choices: [
40
+ { title: "Stop current and start new", value: "replace" },
41
+ { title: "Keep current timer", value: "keep" }
42
+ ]
43
+ });
44
+ if (action === "keep")
45
+ return;
46
+ }
47
+ // Refresh tickets for selection
48
+ yield* ticketService.refresh;
49
+ yield* Effect.sleep("600 millis");
50
+ const { tickets: allTickets } = yield* SubscriptionRef.get(ticketService.state);
51
+ let ticket;
52
+ if (key._tag === "None") {
53
+ if (allTickets.length === 0) {
54
+ yield* Console.log("No tickets found. Usage: jcf start PROJ-123");
55
+ return;
56
+ }
57
+ const selectedKey = yield* fuzzySelect({
58
+ message: "Select ticket (/ to filter):",
59
+ choices: allTickets.map((t) => ({
60
+ title: `${t.key.padEnd(12)} ${t.summary.slice(0, 45).padEnd(45)} [${t.status}]`,
61
+ value: t.key
62
+ }))
63
+ }).pipe(Effect.catchAll(() => Effect.succeed(null)));
64
+ if (!selectedKey)
65
+ return;
66
+ const found = allTickets.find((t) => t.key === selectedKey);
67
+ if (!found)
68
+ return;
69
+ ticket = found;
70
+ }
71
+ else {
72
+ // Key provided — fetch from Jira to validate and get title
73
+ const jira = yield* JiraApiClient;
74
+ const issue = yield* toEffect(jira.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
75
+ params: {
76
+ path: { issueIdOrKey: key.value },
77
+ query: { fields: ["summary", "status", "priority", "assignee", "issuetype", "labels"] }
78
+ }
79
+ })).pipe(Effect.catchAll(() => Effect.succeed(null)));
80
+ if (!issue) {
81
+ yield* Console.log(`Ticket ${key.value} not found in Jira.`);
82
+ return;
83
+ }
84
+ const fields = issue.fields;
85
+ const nested = (k, n) => {
86
+ const v = fields?.[k];
87
+ return v && typeof v === "object" && n in v ? v[n] : null;
88
+ };
89
+ ticket = {
90
+ key: issue.key ?? key.value,
91
+ summary: (typeof fields?.["summary"] === "string" ? fields["summary"] : null) ?? key.value,
92
+ status: (typeof nested("status", "name") === "string" ? nested("status", "name") : null) ??
93
+ "Unknown",
94
+ priority: typeof nested("priority", "name") === "string" ? nested("priority", "name") : null,
95
+ assignee: typeof nested("assignee", "displayName") === "string"
96
+ ? nested("assignee", "displayName")
97
+ : null,
98
+ type: (typeof nested("issuetype", "name") === "string" ? nested("issuetype", "name") : null) ??
99
+ "Task",
100
+ labels: Array.isArray(fields?.["labels"]) ? fields["labels"] : [],
101
+ updated: (typeof fields?.["updated"] === "string" ? fields["updated"] : null) ?? new Date().toISOString()
102
+ };
103
+ yield* Console.log(`${ticket.key}: ${ticket.summary} [${ticket.status}]`);
104
+ }
105
+ // Resolve projectId
106
+ let projectId = Option.isSome(project) ? project.value : undefined;
107
+ if (!projectId) {
108
+ const config = yield* cfg.get;
109
+ if (config.defaultProjectId) {
110
+ projectId = config.defaultProjectId;
111
+ yield* Console.log(`Using default project: ${config.defaultProjectName ?? config.defaultProjectId}`);
112
+ }
113
+ else {
114
+ // Prompt: list projects
115
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
116
+ if (auth) {
117
+ const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
118
+ if (projects.length > 0) {
119
+ const selected = yield* Prompt.select({
120
+ message: "Select Clockify project:",
121
+ choices: [
122
+ ...projects.map((p) => ({ title: p.name, value: p.id })),
123
+ { title: "(skip)", value: "" }
124
+ ]
125
+ });
126
+ if (selected)
127
+ projectId = selected;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ // Resolve billable
133
+ let billableVal = Option.isSome(billable) ? billable.value : undefined;
134
+ if (billableVal === undefined) {
135
+ const config = yield* cfg.get;
136
+ billableVal = config.defaultBillable;
137
+ yield* Console.log(`Billable: ${billableVal ? "yes" : "no"} (default)`);
138
+ }
139
+ // Save defaults if requested
140
+ if (saveDefaults && (projectId || billableVal !== undefined)) {
141
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
142
+ let projectName = null;
143
+ if (projectId && auth) {
144
+ const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
145
+ projectName = projects.find((p) => p.id === projectId)?.name ?? null;
146
+ }
147
+ yield* cfg.set({
148
+ ...(projectId ? { defaultProjectId: projectId, defaultProjectName: projectName } : {}),
149
+ ...(billableVal !== undefined ? { defaultBillable: billableVal } : {})
150
+ });
151
+ yield* Console.log("Defaults saved to ~/.jcf/config.json");
152
+ }
153
+ yield* timer.start(ticket, { projectId, billable: billableVal }).pipe(Effect.catchAll((e) => Console.log(`Error: ${e.message}`)));
154
+ yield* Console.log(`Timer started: ${ticket.key} — ${ticket.summary}`);
155
+ }));
156
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../../src/cli/timer/start.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Timer `status` command.
3
+ *
4
+ * @module
5
+ */
6
+ import { Command } from "@effect/cli";
7
+ import { ClockifyApiClient } from "@knpkv/clockify-api-client";
8
+ import { Console, Effect } from "effect";
9
+ import { ClockifyAuth } from "../../services/ClockifyAuth.js";
10
+ import { StateWriter } from "../../services/StateWriter.js";
11
+ export const statusCmd = Command.make("status", {}, () => Effect.gen(function* () {
12
+ const stateWriter = yield* StateWriter;
13
+ const state = yield* stateWriter.read;
14
+ // Verify against Clockify — timer may have been stopped externally
15
+ if (state.active) {
16
+ const clockifyAuth = yield* ClockifyAuth;
17
+ const clockifyClient = yield* ClockifyApiClient;
18
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
19
+ if (auth) {
20
+ let apiReachable = false;
21
+ const running = yield* clockifyClient.getRunningTimer(auth.workspaceId, auth.userId).pipe(Effect.tap(() => Effect.sync(() => {
22
+ apiReachable = true;
23
+ })), Effect.catchAll(() => Effect.succeed(null)));
24
+ // Only clear if API was reachable and confirmed no running timer
25
+ if (apiReachable && !running) {
26
+ yield* stateWriter.clear;
27
+ yield* Console.log("Timer was stopped externally. State cleared.");
28
+ return;
29
+ }
30
+ // If timer still running, update state file (refreshes mtime for Lua statusline)
31
+ if (running) {
32
+ yield* stateWriter.write({
33
+ ...state,
34
+ elapsed: state.startedAt_unix ? Math.floor(Date.now() / 1000) - state.startedAt_unix : 0
35
+ });
36
+ }
37
+ }
38
+ }
39
+ if (!state.active) {
40
+ yield* Console.log("No active timer");
41
+ return;
42
+ }
43
+ const elapsed = state.startedAt_unix
44
+ ? Math.floor(Date.now() / 1000) - state.startedAt_unix
45
+ : 0;
46
+ const h = Math.floor(elapsed / 3600);
47
+ const m = Math.floor((elapsed % 3600) / 60);
48
+ const s = elapsed % 60;
49
+ yield* Console.log(`● ${state.ticketKey} ${state.summary ?? ""}`);
50
+ yield* Console.log(` Time: ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`);
51
+ yield* Console.log(` Started: ${state.startedAt ?? "?"}`);
52
+ // Show project/billable from Clockify entry
53
+ const clockifyAuth = yield* ClockifyAuth;
54
+ const clockifyClient = yield* ClockifyApiClient;
55
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
56
+ if (auth && state.clockifyEntryId) {
57
+ const entry = yield* clockifyClient.getTimeEntry(auth.workspaceId, state.clockifyEntryId).pipe(Effect.catchAll(() => Effect.succeed(null)));
58
+ if (entry) {
59
+ let projectName = "none";
60
+ if (entry.projectId) {
61
+ const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
62
+ projectName = projects.find((p) => p.id === entry.projectId)?.name ?? entry.projectId;
63
+ }
64
+ yield* Console.log(` Project: ${projectName}`);
65
+ yield* Console.log(` Billable: ${entry.billable ? "yes" : "no"}`);
66
+ // Show tags
67
+ if (entry.tagIds && entry.tagIds.length > 0) {
68
+ const allTags = yield* clockifyClient.getTags(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
69
+ const tagNames = entry.tagIds
70
+ .map((id) => allTags.find((t) => t.id === id)?.name ?? id)
71
+ .join(", ");
72
+ yield* Console.log(` Tags: ${tagNames}`);
73
+ }
74
+ else {
75
+ yield* Console.log(" Tags: none");
76
+ }
77
+ }
78
+ }
79
+ }));
80
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/cli/timer/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Timer `stop` command.
3
+ *
4
+ * @module
5
+ */
6
+ import { Command, Options, Prompt } from "@effect/cli";
7
+ import { ClockifyApiClient } from "@knpkv/clockify-api-client";
8
+ import { Console, Duration, Effect, Option, SubscriptionRef } from "effect";
9
+ import { ClockifyAuth } from "../../services/ClockifyAuth.js";
10
+ import { ConfigService } from "../../services/ConfigService.js";
11
+ import { TimerService } from "../../services/TimerService.js";
12
+ export const stop = Command.make("stop", {
13
+ project: Options.text("project").pipe(Options.withAlias("p"), Options.withDescription("Clockify project ID"), Options.optional),
14
+ billable: Options.boolean("billable").pipe(Options.withAlias("b"), Options.withDescription("Mark as billable"), Options.optional)
15
+ }, ({ billable, project }) => Effect.gen(function* () {
16
+ const timer = yield* TimerService;
17
+ yield* timer.detectRunning;
18
+ // Check if project/billable need to be set
19
+ const currentTimer = yield* SubscriptionRef.get(timer.state);
20
+ const current = { projectId: currentTimer.projectId, billable: currentTimer.billable };
21
+ let stopProjectId = Option.isSome(project) ? project.value : undefined;
22
+ let stopBillable = Option.isSome(billable) ? billable.value : undefined;
23
+ // Prompt for project if not set on start and not provided via flag
24
+ if (!current.projectId && !stopProjectId) {
25
+ const clockifyAuth = yield* ClockifyAuth;
26
+ const clockifyClient = yield* ClockifyApiClient;
27
+ const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
28
+ if (auth) {
29
+ const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
30
+ if (projects.length > 0) {
31
+ const selected = yield* Prompt.select({
32
+ message: "No project set. Select Clockify project:",
33
+ choices: [
34
+ ...projects.map((p) => ({ title: p.name, value: p.id })),
35
+ { title: "(skip)", value: "" }
36
+ ]
37
+ });
38
+ if (selected) {
39
+ stopProjectId = selected;
40
+ const selectedName = projects.find((p) => p.id === selected)?.name ?? selected;
41
+ const saveDefault = yield* Prompt.select({
42
+ message: `Save "${selectedName}" as default project?`,
43
+ choices: [
44
+ { title: "Yes", value: true },
45
+ { title: "No", value: false }
46
+ ]
47
+ });
48
+ if (saveDefault) {
49
+ const cfg = yield* ConfigService;
50
+ yield* cfg.set({ defaultProjectId: stopProjectId, defaultProjectName: selectedName });
51
+ yield* Console.log("Default project saved.");
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ // Prompt for billable if not set
58
+ if (current.billable === null && stopBillable === undefined) {
59
+ stopBillable = yield* Prompt.select({
60
+ message: "Billable?",
61
+ choices: [
62
+ { title: "Yes", value: true },
63
+ { title: "No", value: false }
64
+ ]
65
+ });
66
+ const saveDefault = yield* Prompt.select({
67
+ message: `Save billable=${stopBillable ? "yes" : "no"} as default?`,
68
+ choices: [
69
+ { title: "Yes", value: true },
70
+ { title: "No", value: false }
71
+ ]
72
+ });
73
+ if (saveDefault) {
74
+ const cfg = yield* ConfigService;
75
+ yield* cfg.set({ defaultBillable: stopBillable });
76
+ yield* Console.log("Default billable saved.");
77
+ }
78
+ }
79
+ // Optional comment for Jira worklog
80
+ const comment = yield* Prompt.text({ message: "Comment (empty to skip):" });
81
+ const result = yield* timer.stop({
82
+ projectId: stopProjectId,
83
+ billable: stopBillable,
84
+ comment: comment.trim() || undefined
85
+ }).pipe(Effect.catchAll((e) => Console.log(`Error: ${e.message}`).pipe(Effect.flatMap(() => Effect.succeed(null)))));
86
+ if (result) {
87
+ const secs = Duration.toSeconds(result.duration);
88
+ const h = Math.floor(secs / 3600);
89
+ const m = Math.floor((secs % 3600) / 60);
90
+ const s = Math.floor(secs % 60);
91
+ yield* Console.log(`Timer stopped: ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`);
92
+ yield* Console.log(` Clockify: ${result.clockifyLogged ? "✓" : "✗"}`);
93
+ yield* Console.log(` Jira worklog: ${result.jiraWorklogLogged ? "✓" : "skipped"}`);
94
+ }
95
+ }));
96
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../../src/cli/timer/stop.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAE7D,MAAM,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAC9B,MAAM,EACN;IACE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CACnC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EACtB,OAAO,CAAC,eAAe,CAAC,qBAAqB,CAAC,EAC9C,OAAO,CAAC,QAAQ,CACjB;IACD,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CACxC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EACtB,OAAO,CAAC,eAAe,CAAC,kBAAkB,CAAC,EAC3C,OAAO,CAAC,QAAQ,CACjB;CACF,EACD,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CACxB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,YAAY,CAAA;IACjC,KAAK,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;IAE1B,2CAA2C;IAC3C,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC5D,MAAM,OAAO,GAAG,EAAE,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,QAAQ,EAAE,CAAA;IAEtF,IAAI,aAAa,GAAuB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1F,IAAI,YAAY,GAAwB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IAE5F,mEAAmE;IACnE,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,YAAY,CAAA;QACxC,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAA;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC5F,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CACvE,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAW,CAAC,CAAC,CACnD,CAAA;YACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;oBACpC,OAAO,EAAE,0CAA0C;oBACnD,OAAO,EAAE;wBACP,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wBACxD,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;qBAC/B;iBACF,CAAC,CAAA;gBACF,IAAI,QAAQ,EAAE,CAAC;oBACb,aAAa,GAAG,QAAQ,CAAA;oBACxB,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAA;oBAE9E,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;wBACvC,OAAO,EAAE,SAAS,YAAY,uBAAuB;wBACrD,OAAO,EAAE;4BACP,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;4BAC7B,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;yBAC9B;qBACF,CAAC,CAAA;oBACF,IAAI,WAAW,EAAE,CAAC;wBAChB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,aAAa,CAAA;wBAChC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAC,CAAA;wBACrF,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;oBAC9C,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC5D,YAAY,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;YAClC,OAAO,EAAE,WAAW;YACpB,OAAO,EAAE;gBACP,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;gBAC7B,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;aAC9B;SACF,CAAC,CAAA;QAEF,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;YACvC,OAAO,EAAE,iBAAiB,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,cAAc;YACnE,OAAO,EAAE;gBACP,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;gBAC7B,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;aAC9B;SACF,CAAC,CAAA;QACF,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,aAAa,CAAA;YAChC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,CAAA;YACjD,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAA;IAE3E,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/B,SAAS,EAAE,aAAa;QACxB,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,SAAS;KACrC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,QAAQ,CAAC,CAAC,CAA+B,EAAE,EAAE,CAClD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CACpF,CACF,CAAA;IAED,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAChD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;QACjC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA;QAC/B,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAChB,kBAAkB,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3G,CAAA;QACD,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAA;QACtE,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAA;IACrF,CAAC;AACH,CAAC,CAAC,CACL,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Timer commands: start, stop, status, edit, discard, log.
3
+ *
4
+ * @module
5
+ */
6
+ export { discard, edit, log, start, statusCmd, stop } from "./timer/index.js";
7
+ //# sourceMappingURL=timer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timer.js","sourceRoot":"","sources":["../../../src/cli/timer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ /**
3
+ * Bun-only TUI entry point — renders the React app via `@opentui/react`.
4
+ *
5
+ * @module
6
+ */
7
+ import { createCliRenderer } from "@opentui/core";
8
+ import { createRoot } from "@opentui/react";
9
+ import { Deferred, Effect } from "effect";
10
+ import { App } from "./tui/App.js";
11
+ const program = Effect.gen(function* () {
12
+ const exitSignal = yield* Deferred.make();
13
+ const renderer = yield* Effect.acquireRelease(Effect.promise(() => createCliRenderer({
14
+ exitOnCtrlC: false
15
+ })), (renderer) => Effect.sync(() => renderer.destroy()));
16
+ const onQuit = () => {
17
+ Effect.runSync(Deferred.succeed(exitSignal, void 0));
18
+ };
19
+ const root = createRoot(renderer);
20
+ root.render(_jsx(App, { onQuit: onQuit }));
21
+ yield* Deferred.await(exitSignal);
22
+ }).pipe(Effect.scoped);
23
+ export default program;
24
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAQ,CAAA;IAE/C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC3C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAClB,iBAAiB,CAAC;QAChB,WAAW,EAAE,KAAK;KACnB,CAAC,CACH,EACD,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CACpD,CAAA;IAED,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IACtD,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,CAAC,MAAM,CAAC,KAAC,GAAG,IAAC,MAAM,EAAE,MAAM,GAAI,CAAC,CAAA;IAEpC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACnC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAEtB,eAAe,OAAO,CAAA"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * File-based Clockify API key storage with schema-validated reads.
3
+ *
4
+ * **Mental model**
5
+ *
6
+ * - **Credential file**: Reads/writes `~/.jcf/clockify.json` containing API key, workspace ID,
7
+ * user ID, and base URL. The API key is wrapped in `Redacted` on read.
8
+ * - **Schema-gated**: JSON is parsed then validated via `Schema.decodeUnknown(StoredAuth)` —
9
+ * corrupt data yields {@link ClockifyAuthMissingError} instead of a crash.
10
+ * - **Config-based home dir**: Uses Effect `Config.string("HOME")` for testable env access.
11
+ *
12
+ * @module
13
+ */
14
+ import * as FileSystem from "@effect/platform/FileSystem";
15
+ import * as Path from "@effect/platform/Path";
16
+ import * as Context from "effect/Context";
17
+ import * as Data from "effect/Data";
18
+ import * as Effect from "effect/Effect";
19
+ import * as Layer from "effect/Layer";
20
+ import * as Redacted from "effect/Redacted";
21
+ import * as Schema from "effect/Schema";
22
+ import { homedir } from "node:os";
23
+ export class ClockifyAuthMissingError extends Data.TaggedError("ClockifyAuthMissingError") {
24
+ }
25
+ const StoredAuth = Schema.Struct({
26
+ apiKey: Schema.String,
27
+ workspaceId: Schema.String,
28
+ userId: Schema.String,
29
+ baseUrl: Schema.optional(Schema.String)
30
+ });
31
+ export class ClockifyAuth extends Context.Tag("jcf/ClockifyAuth")() {
32
+ }
33
+ const AUTH_DIR = ".jcf";
34
+ const AUTH_FILE = "clockify.json";
35
+ const DEFAULT_BASE_URL = "https://api.clockify.me/api";
36
+ export const layer = Layer.effect(ClockifyAuth, Effect.gen(function* () {
37
+ const fs = yield* FileSystem.FileSystem;
38
+ const path = yield* Path.Path;
39
+ const home = yield* Effect.sync(() => homedir());
40
+ const dir = path.join(home, AUTH_DIR);
41
+ const filePath = path.join(dir, AUTH_FILE);
42
+ const ensureDir = Effect.gen(function* () {
43
+ const exists = yield* fs.exists(dir);
44
+ if (!exists)
45
+ yield* fs.makeDirectory(dir, { recursive: true });
46
+ });
47
+ const decodeStoredAuth = Schema.decodeUnknown(StoredAuth);
48
+ return {
49
+ getConfig: Effect.gen(function* () {
50
+ const exists = yield* fs.exists(filePath);
51
+ if (!exists) {
52
+ return yield* Effect.fail(new ClockifyAuthMissingError({
53
+ message: "Clockify not configured. Run: jcf auth clockify setup"
54
+ }));
55
+ }
56
+ const content = yield* fs.readFileString(filePath).pipe(Effect.mapError(() => new ClockifyAuthMissingError({ message: "Failed to read Clockify auth file" })));
57
+ const json = yield* Effect.try({
58
+ try: () => JSON.parse(content),
59
+ catch: () => new ClockifyAuthMissingError({ message: "Invalid JSON in Clockify auth file" })
60
+ });
61
+ const stored = yield* decodeStoredAuth(json).pipe(Effect.mapError(() => new ClockifyAuthMissingError({ message: "Invalid Clockify auth file schema" })));
62
+ return {
63
+ apiKey: Redacted.make(stored.apiKey),
64
+ workspaceId: stored.workspaceId,
65
+ userId: stored.userId,
66
+ baseUrl: stored.baseUrl ?? DEFAULT_BASE_URL
67
+ };
68
+ }).pipe(Effect.catchTag("SystemError", () => Effect.fail(new ClockifyAuthMissingError({ message: "Failed to read Clockify auth" }))), Effect.catchTag("BadArgument", () => Effect.fail(new ClockifyAuthMissingError({ message: "Invalid Clockify auth file" })))),
69
+ save: (auth) => Effect.gen(function* () {
70
+ yield* ensureDir;
71
+ yield* fs.writeFileString(filePath, JSON.stringify(auth, null, 2));
72
+ yield* Effect.tryPromise(() => import("node:fs/promises").then((f) => f.chmod(filePath, 0o600))).pipe(Effect.catchAll(() => Effect.void));
73
+ }).pipe(Effect.catchAll(() => Effect.void)),
74
+ isConfigured: fs.exists(filePath).pipe(Effect.catchAll(() => Effect.succeed(false)))
75
+ };
76
+ }));
77
+ //# sourceMappingURL=ClockifyAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClockifyAuth.js","sourceRoot":"","sources":["../../../src/services/ClockifyAuth.ts"],"names":[],"mappings":"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"}