@beauraines/toggl-cli 0.10.5

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/.eslintrc.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true,
5
+ "node": true
6
+ },
7
+ "extends": "standard",
8
+ "overrides": [
9
+ ],
10
+ "parserOptions": {
11
+ "ecmaVersion": "latest",
12
+ "sourceType": "module"
13
+ },
14
+ "rules": {
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "npm" # See documentation for possible values
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,27 @@
1
+ name: Node.js CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+
10
+ jobs:
11
+ build:
12
+
13
+ runs-on: ubuntu-latest
14
+
15
+ strategy:
16
+ matrix:
17
+ node-version: [16.x]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v3
21
+ - name: Use Node.js ${{ matrix.node-version }}
22
+ uses: actions/setup-node@v3
23
+ with:
24
+ node-version: ${{ matrix.node-version }}
25
+ - run: npm ci
26
+ - run: npm run build --if-present
27
+ - run: npm test
@@ -0,0 +1,54 @@
1
+ name: 'Publish to NPM'
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ['Node.js CI']
6
+ types: [completed]
7
+ branches: [master,main]
8
+
9
+ jobs:
10
+ publish-new-version:
11
+ runs-on: ubuntu-latest
12
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ with:
16
+ fetch-depth: '0'
17
+ - name: git setup
18
+ run: |
19
+ git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
20
+ git config --local user.name "github-actions[bot]"
21
+ - name: setup node
22
+ uses: actions/setup-node@v3
23
+ with:
24
+ node-version: 16.x
25
+ registry-url: 'https://registry.npmjs.org'
26
+ - name: npm install
27
+ run: npm ci
28
+
29
+ - name: Should release
30
+ id: should_release
31
+ continue-on-error: true
32
+ run: npm run should-release -- -v
33
+
34
+ - name: No release
35
+ if: steps.should_release.outcome != 'success'
36
+ run: echo "No release required. Skipping publishing."
37
+
38
+ - name: Version bump
39
+ if: steps.should_release.outcome == 'success'
40
+ run: npm run release
41
+
42
+ - name: Publish to NPM
43
+ if: steps.should_release.outcome == 'success'
44
+ run: npm publish
45
+ env:
46
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
47
+
48
+ - name: Push commits to GitHub
49
+ if: steps.should_release.outcome == 'success'
50
+ uses: ad-m/github-push-action@master
51
+ with:
52
+ github_token: ${{ secrets.GITHUB_TOKEN }}
53
+ branch: ${{ github.ref }}
54
+ tags: true
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
+
5
+ ### [0.10.5](https://github.com/beauraines/toggl-cli-node/compare/v0.10.4...v0.10.5) (2023-03-04)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * makes weekly report work in v9 API ([#42](https://github.com/beauraines/toggl-cli-node/issues/42)) ([5de62c8](https://github.com/beauraines/toggl-cli-node/commit/5de62c833bb05840efadd3065ca60ce4c1bb6d1b))
11
+
12
+ ### [0.10.4](https://github.com/beauraines/toggl-cli-node/compare/v0.10.3...v0.10.4) (2023-03-02)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Cannot edit time entries ([ec6611a](https://github.com/beauraines/toggl-cli-node/commit/ec6611aee147ff0355af664ee7bf05aa6b2c4f38)), closes [#40](https://github.com/beauraines/toggl-cli-node/issues/40)
18
+
19
+ ### [0.10.3](https://github.com/beauraines/toggl-cli-node/compare/v0.10.2...v0.10.3) (2023-03-02)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * converts project id, workspace id to number ([8cefe8c](https://github.com/beauraines/toggl-cli-node/commit/8cefe8cce9d8aa7651171e3a35d69ee9ba278950)), closes [#38](https://github.com/beauraines/toggl-cli-node/issues/38)
25
+
26
+ ### 0.10.2 (2023-02-28)
27
+
28
+
29
+ ### Features
30
+
31
+ * adds Me command ([#1](https://github.com/beauraines/toggl-cli-node/issues/1)) ([6e656e8](https://github.com/beauraines/toggl-cli-node/commit/6e656e8468f181f43f27880372d18ae67e7e677f))
32
+
33
+ ### 0.10.1 (2023-02-28)
34
+
35
+
36
+ ### Features
37
+
38
+ * adds Me command ([#1](https://github.com/beauraines/toggl-cli-node/issues/1)) ([6e656e8](https://github.com/beauraines/toggl-cli-node/commit/6e656e8468f181f43f27880372d18ae67e7e677f))
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # toggl-cli-node
2
+
3
+ **UPDATED TO USE Toggl v9 API**
4
+
5
+ A command line interface for [toggl](https://toggl.com) written in node, based on the python [TogglCli](https://github.com/AuHau/toggl-cli) project. Attempting to use similar syntax.
6
+
7
+ This was made possible because [saintedlama](https://github.com/saintedlama) had already built a node API for toggl.
8
+
9
+ ## Configuration
10
+
11
+ 1. Configure your environment, with environment variables or a `.env` file in the project root. Eventually, these will be read from a config file as an alternative
12
+ 1. `TOGGL_API_TOKEN` (required)
13
+ 2. `TOGGL_DEFAULT_WORKSPACE_ID` (required)
14
+ 3. `TOGGL_DEFAULT_PROJECT_ID` (optional)
15
+ 4. `TOGGL_TIMEZONE=America/Los_Angeles` (defaults to `America/New_York`)
16
+ ## Dependencies
17
+
18
+ 1. toggl-client - Used from [saintedlama/toggl-client](https://github.com/saintedlama/toggl-client) repository as the latest code hasn't been released to npm
19
+ 3. dotenv
20
+ 4. yargs
21
+
22
+
23
+
24
+ ## Features
25
+
26
+ | Feature | Available | Comments |
27
+ | ------------------------------------ | --------- | ----------------------------------------------------------- |
28
+ | Start time entry | ✅ | |
29
+ | Start time entry with description | ✅ | |
30
+ | Start time entry with project | ✅ | |
31
+ | stop time entry | ✅ | |
32
+ | Continue named time entry | ✅ | |
33
+ | Report today by project | ✅ | |
34
+ | Report this week by project by day | ✅ | |
35
+ | Edit time entry | ✅ | |
36
+ | Use config from file | | |
37
+ | Save config to file | | |
38
+ | Refactor: Display and format modules | | |
39
+ | Client: reset PAT | | |
40
+ | Client: other user feature? | | |
41
+ | Client: specify client name | | |
42
+ | Colorize output | | |
43
+ | Better table output | | |
44
+ | List recent time entries | ✅ | |
45
+ | Command line completion | ✅ | [#6](https://github.com/beauraines/toggl-cli-node/issues/6) |
46
+
47
+ ## Development Road Map
48
+
49
+ Priority order... I think.
50
+
51
+ 1. ~today - improve the output~
52
+ 2. ~weekly - improve the output format~
53
+ 3. ~now - format the display time entry output~
54
+ 4. ~toggl continue~
55
+ 5. ~duration to display helper function?~
56
+ 6. read configuration (token, default workspace) from files (client and utils)
57
+ 7. now - update running description, project, start time
58
+ 8. now - update start time with overlap detection and adjust prior time entry
59
+ 9. project list - format the output, group by client
60
+ 10. Add ability to lookup project by name not just id
61
+ 11. start - add project name to output, add time started
62
+ 12. toggl workspace add - this command is not yet supported.
63
+ 13. toggl workspace list - improve output
64
+ 14. colorized output
65
+ 15. config - some way to save /me information to a file
66
+ 1. default workspace?
67
+ 2. API key?
68
+ 3. display formats?
69
+ 16. toggl project add - this command is not yet supported.
70
+
71
+
72
+
73
+ ## Planned Limitations
74
+
75
+ There are several features that I do not use from Toggl, so including them is a priority for me. I'm not opposed to them being in there and would welcome collaboration to include them.
76
+
77
+ 1. tags
78
+ 2. multiple workspaces
79
+ 3. billable
80
+
81
+ ## How to Contribute
82
+
83
+ I need to clean up and refactor the code and establish a few more helper and utility functions, but I'm open to contributions!
84
+
85
+
package/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from 'yargs'
4
+ import { hideBin } from 'yargs/helpers'
5
+ import { commands } from './cmds/index.mjs'
6
+
7
+ yargs(hideBin(process.argv))
8
+ .scriptName('toggl')
9
+ .command(commands)
10
+ .completion('completion', 'Outputs bash/zsh-completion shortcuts for commands and options to add to .bashrc or .bash_profile')
11
+ .demandCommand()
12
+ .help()
13
+ .parse()
package/client.js ADDED
@@ -0,0 +1,29 @@
1
+ import dotenv from 'dotenv'
2
+ import togglClient from 'toggl-client'
3
+ dotenv.config()
4
+
5
+ export default function () {
6
+ if (!process.env.TOGGL_API_TOKEN) {
7
+ console.log('TOGGL_API_TOKEN environment variable is not set.')
8
+ console.log('For development, it can be set in the .env file in the project root')
9
+ }
10
+
11
+ // TODO Try to read rc file
12
+
13
+ // FIXME apiToken is not needed
14
+ const apiToken = process.env.TOGGL_API_TOKEN
15
+ const client = togglClient()
16
+ // const client = togglClient({ apiToken });
17
+ // ? Why doesn't a try/catch block work?
18
+ // try {
19
+ // const client = togglClient({ apiToken });
20
+ // } catch (error) {
21
+ // console.error(error);
22
+ // }
23
+ if (!client) {
24
+ console.error('There was a problem')
25
+ process.exit(1)
26
+ }
27
+
28
+ return client
29
+ }
@@ -0,0 +1,55 @@
1
+ import Client from '../client.js'
2
+ import { createTimeEntry, getProjectById } from '../utils.js'
3
+ import dayjs from 'dayjs'
4
+
5
+ export const command = 'continue [description]'
6
+
7
+ export const desc = 'Continues an existing time entry. If description is included it will search for the most' +
8
+ 'recent entry including that description. If no description is provided, the most recent entry will be restarted.'
9
+
10
+ export const builder = {
11
+ description: {
12
+ describe: 'The time entry to continue',
13
+ type: 'string'
14
+ }
15
+ // -s, --start DATETIME Sets a start time.
16
+ }
17
+
18
+ export const handler = async function (argv) {
19
+ const client = Client()
20
+ const timeEntries = await client.timeEntries.list(
21
+ {
22
+ start_date: dayjs().subtract(14, 'days').toISOString(),
23
+ end_date: dayjs().toISOString()
24
+ }
25
+ ) // Gets time entries for last 14 days, up to 1000 entries
26
+
27
+ timeEntries.sort((a, b) => dayjs(a.start).toDate() - dayjs(b.start).toDate())
28
+
29
+ let matchingTimeEntry
30
+ switch (argv.description) {
31
+ case undefined:
32
+ matchingTimeEntry = timeEntries.slice(-1)[0]
33
+ break
34
+ default:
35
+ { const searchName = argv.description.toLowerCase()
36
+ matchingTimeEntry = timeEntries.find(x => x.description.toLowerCase().includes(searchName)) }
37
+ break
38
+ }
39
+
40
+ const params = {
41
+ projectId: matchingTimeEntry?.pid,
42
+ workspaceId: matchingTimeEntry?.wid,
43
+ description: matchingTimeEntry?.description || 'no description',
44
+ billable: matchingTimeEntry?.billable,
45
+ dur: matchingTimeEntry?.dur
46
+ }
47
+
48
+ if (matchingTimeEntry) {
49
+ const timeEntry = await createTimeEntry(params)
50
+ const project = await getProjectById(timeEntry.wid, timeEntry.pid)
51
+ console.info(`Continued ${timeEntry?.description} for project ${project?.name}`)
52
+ } else {
53
+ console.info('No matching time entry found!')
54
+ }
55
+ }
@@ -0,0 +1,16 @@
1
+ import Client from '../client.js'
2
+ import { displayTimeEntry } from '../utils.js'
3
+
4
+ export const command = 'now'
5
+ export const desc = 'Displays the current running time entry'
6
+ export const builder = {}
7
+
8
+ export const handler = async function (argv) {
9
+ const client = new Client()
10
+ const currentTimeEntry = await client.timeEntries.current()
11
+ if (currentTimeEntry) {
12
+ await displayTimeEntry(currentTimeEntry)
13
+ } else {
14
+ console.log('There is no time entry running!')
15
+ }
16
+ }
package/cmds/edit.mjs ADDED
@@ -0,0 +1,84 @@
1
+ import Client from '../client.js'
2
+ import { defaultWorkspaceId, getProjectByName, getProjectById, appName, displayTimeEntry } from '../utils.js'
3
+ import dayjs from 'dayjs'
4
+ import utc from 'dayjs/plugin/utc.js'
5
+ import timezone from 'dayjs/plugin/timezone.js'
6
+ import yargs from 'yargs'
7
+ dayjs.extend(utc)
8
+ dayjs.extend(timezone)
9
+
10
+ export const command = 'edit'
11
+ // FIXME editing not working
12
+ export const desc = 'SOMETHING IS NOT RIGHT Edits the current running time entry'
13
+
14
+ export const builder = {
15
+ d: { alias: ['description'], describe: 'Time entry name', type: 'string:' },
16
+ p: { alias: ['projectId', 'project'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false },
17
+ s: { alias: ['start', 'startTime'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false },
18
+ e: { alias: ['end', 'endTime'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false }
19
+ }
20
+
21
+ export const handler = async function (argv) {
22
+ if (!(argv.d || argv.p || argv.s || argv.e)) {
23
+ console.error('At least one option must be provided, description, project, start or end')
24
+ yargs().help()
25
+ yargs().exit(1, new Error('At least one option must be provided, description, project, start or end'))
26
+ }
27
+ const client = new Client()
28
+ const currentTimeEntry = await client.timeEntries.current()
29
+
30
+ const params = {}
31
+
32
+ params.workspace_id = +defaultWorkspaceId
33
+ let project
34
+ if (argv.projectId) {
35
+ if (isNaN(argv.projectId)) {
36
+ project = await getProjectByName(params.workspace_id, argv.projectId)
37
+ } else {
38
+ project = await getProjectById(params.workspace_id, argv.projectId)
39
+ }
40
+ }
41
+
42
+ let startTime, endTime
43
+ if (dayjs(argv.startTime).isValid()) {
44
+ startTime = argv.startTime
45
+ } else {
46
+ // Parse the time and set it based upon the current time
47
+ startTime = parseTime(argv.startTime)
48
+ }
49
+
50
+ if (dayjs(argv.endTime).isValid()) {
51
+ endTime = argv.endTime
52
+ } else {
53
+ // Parse the time and set it based upon the current time
54
+ endTime = parseTime(argv.endTime)
55
+ }
56
+
57
+ params.created_with = appName
58
+ params.at = dayjs().toISOString()
59
+ project ? params.project_id = +project.id : undefined
60
+ startTime ? params.start = startTime.toISOString() : undefined
61
+ endTime ? params.stop = endTime.toISOString() : undefined
62
+ endTime ? params.duration = endTime.diff(startTime, 'seconds') : undefined
63
+ argv.description ? params.description = argv.description : undefined
64
+ const timeEntry = await client.timeEntries.update(currentTimeEntry.id, params)
65
+ await displayTimeEntry(timeEntry)
66
+ }
67
+
68
+ /**
69
+ * Parses a timelike string into a dayjs object of the current date and that time
70
+ * @param {string} timeString timelike string e.g. 4:50PM '12:00 AM' etc.
71
+ * @returns {object} dayjs object
72
+ */
73
+ function parseTime (timeString) {
74
+ let h, m
75
+ // Assumes time in format 4:50 PM
76
+ const time = timeString.split(':', 2)
77
+ h = time[0]
78
+ m = time[1].match(/[0-9]+/)[0]
79
+ if (timeString.match(/PM/i) && h <= 12) {
80
+ // + in front of string converts to a number, cool!
81
+ h = +h + 12
82
+ }
83
+ return dayjs().hour(h).minute(m).second(0)
84
+ }
package/cmds/index.mjs ADDED
@@ -0,0 +1,26 @@
1
+ import * as ls from './ls.mjs'
2
+ import * as me from './me.mjs'
3
+ import * as continueEntry from './continue.mjs'
4
+ import * as current from './currentTimeEntry.mjs'
5
+ import * as workspace from './workspace.mjs'
6
+ import * as projects from './projects.mjs'
7
+ import * as edit from './edit.mjs'
8
+ import * as web from './web.mjs'
9
+ import * as startTimeEntry from './startTimeEntry.mjs'
10
+ import * as stopTimeEntry from './stopTimeEntry.mjs'
11
+ import * as today from './today.mjs'
12
+ import * as weekly from './weekly.mjs'
13
+ export const commands = [
14
+ continueEntry,
15
+ current,
16
+ edit,
17
+ ls,
18
+ me,
19
+ projects,
20
+ startTimeEntry,
21
+ stopTimeEntry,
22
+ today,
23
+ web,
24
+ weekly,
25
+ workspace
26
+ ]
package/cmds/ls.mjs ADDED
@@ -0,0 +1,30 @@
1
+ import Client from '../client.js'
2
+ import { convertUtcTime, formatDuration } from '../utils.js'
3
+ import dayjs from 'dayjs'
4
+
5
+ export const command = 'ls'
6
+ export const desc = 'Lists time entries'
7
+
8
+ export const builder = {
9
+
10
+ }
11
+
12
+ export const handler = async function (argv) {
13
+ const client = Client()
14
+ // TODO update these dates
15
+ const timeEntries = await client.timeEntries.list({start_date:dayjs().subtract(14,'days').toISOString(),end_date:dayjs().toISOString()});
16
+
17
+ const report = []
18
+ timeEntries.forEach(element => {
19
+ report.push(
20
+ {
21
+ description: element.description,
22
+ start: convertUtcTime(element.start),
23
+ stop: convertUtcTime(element.stop),
24
+ duration: formatDuration(element.duration * 1000)
25
+ }
26
+ )
27
+ })
28
+
29
+ console.table(report, ['description', 'start', 'stop', 'duration'])
30
+ }
@@ -0,0 +1,6 @@
1
+ describe('ls command',() => {
2
+ it.todo('should get tasks for the last 14 days')
3
+ // get time entries
4
+ // sort them
5
+ // check that the earliest is within 14 days
6
+ })
package/cmds/me.mjs ADDED
@@ -0,0 +1,44 @@
1
+ import dayjs from 'dayjs'
2
+ import Client from '../client.js'
3
+
4
+ export const command = 'me'
5
+ export const desc = 'Displays the current user'
6
+ export const builder = {}
7
+
8
+ export const handler = async function (argv) {
9
+ const client = Client()
10
+ const currentUser = await client.user.current()
11
+ // console.log(currentUser);
12
+
13
+ console.log(`Default workspace: ${currentUser.default_workspace_id}`)
14
+ console.log(`API Token: ${currentUser.api_token}`)
15
+ console.log(`Fullname: ${currentUser.fullname}`)
16
+ console.log(`Timezone: ${currentUser.timezone}`)
17
+ // TODO These look like they come from the `/preferences` endpoint
18
+ // console.log(`Date format: ${currentUser.date_format}`);
19
+ // console.log(`jquery date format: ${currentUser.jquery_date_format}`);
20
+ // console.log(`jquery_timeofday_format: ${currentUser.jquery_timeofday_format}`);
21
+
22
+ const weekStart = dayjs().day(currentUser.beginning_of_week).format('dddd')
23
+ console.log(`beginning_of_week: ${weekStart}`)
24
+
25
+ // Api token:
26
+ // Beginning of week: Sunday
27
+ // Date format: MM/DD/YYYY
28
+ // Default workspace: beau.raines's workspace (#403916)
29
+ // Fullname: Beau Raines
30
+ // Image url: https://assets.track.toggl.com/avatars/913dfa76c01d730711eb00a1c3e6d57c.jpg
31
+ // Language: en_US
32
+ // Send timer notifications: True
33
+ // Timeofday format: 12-hour
34
+ // Timezone: America/Los_Angeles
35
+ // Workspace: beau.raines's workspace (#403916)
36
+
37
+ const workspaces = (await client.workspaces.list()).map(w => w.name).join(', ')
38
+ console.log('')
39
+ console.log(`Workspaces: ${workspaces}`)
40
+ // FIXME since is no longer in the response
41
+ const since = dayjs.unix(currentUser.since)
42
+ console.log('')
43
+ console.log(`Toggl user since ${since}`)
44
+ }
@@ -0,0 +1,7 @@
1
+ export const command = 'add'
2
+ export const desc = 'Creates a new project.'
3
+ export const builder = {}
4
+
5
+ export const handler = async function (argv) {
6
+ console.info(`${argv.$0} ${argv._.join(' ')} - this command is not yet supported.`)
7
+ }
@@ -0,0 +1,3 @@
1
+ import * as add from './add.mjs'
2
+ import * as list from './list.mjs'
3
+ export const projects = [add, list]
@@ -0,0 +1,25 @@
1
+ import Client from '../../client.js'
2
+
3
+ export const command = 'list'
4
+ export const desc = 'Lists active projects from the current workspace'
5
+
6
+ export const builder = {
7
+
8
+ }
9
+
10
+ export const handler = async function (argv) {
11
+ const client = Client()
12
+ const workspaces = await client.workspaces.list()
13
+
14
+ const workspace = workspaces[0]
15
+
16
+ console.info('Workspace: ' + workspace.name)
17
+ console.info('id: ' + workspace.id)
18
+
19
+ const projects = await client.workspaces.projects(workspace.id)
20
+
21
+ const activeProjects = projects.filter(x => x.active)
22
+ console.info(`Found ${activeProjects.length} projects`)
23
+ activeProjects.map(p => { console.log(p.name + ' ' + p.id) })
24
+
25
+ }
@@ -0,0 +1,11 @@
1
+ import { projects } from './projects/index.mjs'
2
+
3
+
4
+ export const command = 'project <command>'
5
+ export const desc = 'Manage projects'
6
+
7
+ export const builder = function (yargs) {
8
+ return yargs.command(projects)
9
+ }
10
+
11
+ export const handler = function (argv) {}
@@ -0,0 +1,38 @@
1
+ import { defaultWorkspaceId,getProjectByName, createTimeEntry, getProjectById, defaultProjectId } from '../utils.js'
2
+
3
+ export const command = 'start'
4
+ export const desc = 'Starts a time entry'
5
+
6
+ export const builder = {
7
+ description: {
8
+ describe: 'Time entry name',
9
+ type: 'string:'
10
+ },
11
+ p: { alias: ['projectId', 'project'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false },
12
+ // TODO default to default workspace
13
+ w: { alias: ['workspaceId', 'workspace'], describe: 'The case insensitive workspace name or workspace id.', type: 'number', demandOption: false }
14
+ }
15
+
16
+ export const handler = async function (argv) {
17
+ // console.info(`${argv.$0} ${argv._.join(' ')} - this command is not yet supported.`);
18
+ // console.debug(argv);
19
+ // TODO validate options
20
+ // TODO check that description was provided or provide a default
21
+ const params = {}
22
+ params.description = argv.description || argv._.slice(1).join(' ') || 'no description'
23
+ // TODO lookup workspace
24
+ params.workspaceId = defaultWorkspaceId
25
+ let project
26
+ if (argv.projectId) {
27
+ if (isNaN(argv.projectId)) {
28
+ project = await getProjectByName(params.workspaceId, argv.projectId)
29
+ } else {
30
+ project = await getProjectById(params.workspaceId, argv.projectId)
31
+ }
32
+ }
33
+
34
+ params.projectId = project?.id || defaultProjectId || null
35
+ // TODO check for invalid projectId or catch the error when creating fails
36
+ const timeEntry = await createTimeEntry(params)
37
+ console.info(`Started ${timeEntry?.description} ${project?.name ? `for project ${project.name}` : 'without a project'}`)
38
+ }
@@ -0,0 +1,18 @@
1
+ import Client from '../client.js'
2
+ import dayjs from 'dayjs'
3
+
4
+ export const command = 'stop'
5
+ export const desc = 'Stops the current running time entry'
6
+ export const builder = {}
7
+
8
+ export const handler = async function (argv) {
9
+ const client = Client()
10
+ const currentTimeEntry = await client.timeEntries.current()
11
+ if (currentTimeEntry) {
12
+ const stopped = await client.timeEntries.stop(currentTimeEntry)
13
+ const duration = dayjs.duration(stopped.duration * 1000).format('H[h] m[m]')
14
+ console.log(`Stopped ${stopped.description} after ${duration}`)
15
+ } else {
16
+ console.log('There is no time entry running!')
17
+ }
18
+ }