@eeymoo/hum 0.1.23 → 0.1.25
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/package.json +5 -5
- package/src/commands/auth.js +16 -9
- package/src/commands/config.js +6 -1
- package/src/commands/diet.js +19 -158
- package/src/commands/exercise.js +18 -154
- package/src/commands/food.js +1 -1
- package/src/commands/record.js +9 -9
- package/src/commands/sleep.js +42 -163
- package/src/commands/timeline.js +1 -1
- package/src/commands/weight.js +17 -149
- package/src/lib/api.js +14 -3
- package/src/lib/crud-command.js +186 -0
- package/test/e2e-extended.sh +200 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeymoo/hum",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
|
|
3
|
+
"version": "0.1.25",
|
|
4
|
+
"description": "Hum CLI - A command line tool for Hum API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/Eeymoo/hum.git",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"@toon-format/toon": "^2.3.0",
|
|
21
21
|
"commander": "^12.1.0",
|
|
22
22
|
"conf": "^13.0.1",
|
|
23
|
-
"dayjs": "^1.11.13"
|
|
24
|
-
"node-fetch": "^3.3.2"
|
|
23
|
+
"dayjs": "^1.11.13"
|
|
25
24
|
},
|
|
26
25
|
"scripts": {
|
|
27
26
|
"build": "node bin/index.js --version",
|
|
28
|
-
"test:e2e": "bash test/e2e.sh"
|
|
27
|
+
"test:e2e": "bash test/e2e.sh",
|
|
28
|
+
"test:e2e:extended": "bash test/e2e-extended.sh"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "^5.6.0"
|
package/src/commands/auth.js
CHANGED
|
@@ -16,7 +16,7 @@ auth
|
|
|
16
16
|
console.log('Please visit:', deviceData.verificationUriComplete)
|
|
17
17
|
console.log('Code:', deviceData.userCode)
|
|
18
18
|
console.log('Waiting for authorization...')
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
const maxAttempts = 60
|
|
21
21
|
for (let i = 0; i < maxAttempts; i++) {
|
|
22
22
|
await new Promise(resolve => setTimeout(resolve, deviceData.interval * 1000))
|
|
@@ -28,10 +28,10 @@ auth
|
|
|
28
28
|
grantType: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
29
29
|
})
|
|
30
30
|
})
|
|
31
|
-
|
|
32
|
-
if (tokenData.
|
|
33
|
-
config.set('accessToken', tokenData.
|
|
34
|
-
config.set('refreshToken', tokenData.
|
|
31
|
+
|
|
32
|
+
if (tokenData.access_token) {
|
|
33
|
+
config.set('accessToken', tokenData.access_token)
|
|
34
|
+
config.set('refreshToken', tokenData.refresh_token)
|
|
35
35
|
console.log('Successfully logged in!')
|
|
36
36
|
return
|
|
37
37
|
}
|
|
@@ -40,14 +40,21 @@ auth
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
console.error('Authorization timeout')
|
|
43
|
-
} else if (options.apiKey) {
|
|
43
|
+
} else if (options.apiKey || process.env.HUM_API_KEY) {
|
|
44
|
+
const apiKey = options.apiKey || process.env.HUM_API_KEY
|
|
44
45
|
const result = await request('/auth/verify', {
|
|
45
46
|
method: 'POST',
|
|
46
|
-
body: JSON.stringify({ apiKey
|
|
47
|
+
body: JSON.stringify({ apiKey })
|
|
48
|
+
}).catch(err => {
|
|
49
|
+
// verify 端点在 key 无效时返回 401,捕获后返回统一结构
|
|
50
|
+
if (err.message.includes('401')) {
|
|
51
|
+
return { valid: false }
|
|
52
|
+
}
|
|
53
|
+
throw err
|
|
47
54
|
})
|
|
48
55
|
|
|
49
56
|
if (result.valid) {
|
|
50
|
-
config.set('apiKey',
|
|
57
|
+
config.set('apiKey', apiKey)
|
|
51
58
|
const parts = []
|
|
52
59
|
if (result.user) parts.push(result.user)
|
|
53
60
|
if (result.keyName) parts.push(`key: ${result.keyName}`)
|
|
@@ -56,7 +63,7 @@ auth
|
|
|
56
63
|
console.error('Invalid API key')
|
|
57
64
|
}
|
|
58
65
|
} else {
|
|
59
|
-
console.error('Please provide --api-key or --device option')
|
|
66
|
+
console.error('Please provide --api-key or --device option, or set HUM_API_KEY environment variable')
|
|
60
67
|
}
|
|
61
68
|
} catch (error) {
|
|
62
69
|
console.error('Login failed:', error.message)
|
package/src/commands/config.js
CHANGED
|
@@ -45,7 +45,12 @@ configCmd
|
|
|
45
45
|
console.log('Configuration:')
|
|
46
46
|
for (const [key, value] of Object.entries(allConfig)) {
|
|
47
47
|
const displayKey = key === 'apiUrl' ? 'api-url' : key === 'dateFormat' ? 'date-format' : key
|
|
48
|
-
|
|
48
|
+
let displayValue = value
|
|
49
|
+
// 脱敏敏感信息
|
|
50
|
+
if ((key === 'apiKey' || key === 'accessToken' || key === 'refreshToken') && typeof value === 'string' && value.length > 8) {
|
|
51
|
+
displayValue = value.slice(0, 4) + '****' + value.slice(-4)
|
|
52
|
+
}
|
|
53
|
+
console.log(` ${displayKey}: ${displayValue}`)
|
|
49
54
|
}
|
|
50
55
|
})
|
|
51
56
|
|
package/src/commands/diet.js
CHANGED
|
@@ -1,160 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.option('--note <note>', 'Note')
|
|
21
|
-
.option('--date <date>', 'Date (YYYY-MM-DD or ISO 8601 datetime)')
|
|
22
|
-
.option('--file <paths...>', 'File paths to attach')
|
|
23
|
-
.action(async (options) => {
|
|
24
|
-
try {
|
|
25
|
-
const formData = createFormData({
|
|
26
|
-
mealType: options.meal,
|
|
27
|
-
calories: options.calories,
|
|
28
|
-
protein: options.protein,
|
|
29
|
-
carbs: options.carbs,
|
|
30
|
-
fat: options.fat,
|
|
31
|
-
fiber: options.fiber,
|
|
32
|
-
sodium: options.sodium,
|
|
33
|
-
foods: options.foods,
|
|
34
|
-
water: options.water,
|
|
35
|
-
extraData: options.extraData,
|
|
36
|
-
note: options.note,
|
|
37
|
-
date: appendTimezoneOffset(options.date)
|
|
38
|
-
}, options.file || [])
|
|
39
|
-
|
|
40
|
-
const result = await request('/diets', {
|
|
41
|
-
method: 'POST',
|
|
42
|
-
body: formData,
|
|
43
|
-
isFormData: true
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
console.log('Diet record added:', result.id)
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error('Failed to add diet record:', error.message)
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
diet
|
|
53
|
-
.command('list')
|
|
54
|
-
.option('--meal <type>', 'Filter by meal type')
|
|
55
|
-
.option('--last <period>', 'Last N days/weeks/months/years')
|
|
56
|
-
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
57
|
-
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
58
|
-
.option('--page <number>', 'Page number', '1')
|
|
59
|
-
.option('--limit <number>', 'Items per page', '20')
|
|
60
|
-
.option('--include-deleted', 'Include deleted records')
|
|
61
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
62
|
-
.action(async (options) => {
|
|
63
|
-
try {
|
|
64
|
-
const { params, page } = buildQueryParams(options)
|
|
65
|
-
const result = await request(`/diets?${params.toString()}`)
|
|
66
|
-
outputData(result, { format: options.format, type: 'diet-list', page })
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('Failed to list diet records:', error.message)
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
diet
|
|
73
|
-
.command('stats')
|
|
74
|
-
.option('--last <period>', 'Last N days/weeks/months/years')
|
|
75
|
-
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
76
|
-
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
77
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
78
|
-
.action(async (options) => {
|
|
79
|
-
try {
|
|
80
|
-
const { params } = buildQueryParams(options)
|
|
81
|
-
const result = await request(`/diets/stats?${params.toString()}`)
|
|
82
|
-
outputData(result, { format: options.format, type: 'diet-stats' })
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error('Failed to get diet stats:', error.message)
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
diet
|
|
89
|
-
.command('get')
|
|
90
|
-
.requiredOption('--id <id>', 'Diet record ID')
|
|
91
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
92
|
-
.action(async (options) => {
|
|
93
|
-
try {
|
|
94
|
-
const result = await request(`/diets/${options.id}`)
|
|
95
|
-
outputData(result, { format: options.format, type: 'diet-get' })
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Failed to get diet record:', error.message)
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
diet
|
|
102
|
-
.command('update')
|
|
103
|
-
.requiredOption('--id <id>', 'Diet record ID')
|
|
104
|
-
.option('--meal <type>', 'Updated meal type')
|
|
105
|
-
.option('--calories <value>', 'Updated calories')
|
|
106
|
-
.option('--protein <value>', 'Updated protein')
|
|
107
|
-
.option('--carbs <value>', 'Updated carbs')
|
|
108
|
-
.option('--fat <value>', 'Updated fat')
|
|
109
|
-
.option('--fiber <value>', 'Updated fiber')
|
|
110
|
-
.option('--sodium <value>', 'Updated sodium')
|
|
111
|
-
.option('--foods <string>', 'Updated foods')
|
|
112
|
-
.option('--water <value>', 'Updated water')
|
|
113
|
-
.option('--extra-data <json>', 'Updated extra data (JSON string)')
|
|
114
|
-
.option('--note <note>', 'Updated note')
|
|
115
|
-
.option('--date <date>', 'Updated date (YYYY-MM-DD or ISO 8601 datetime)')
|
|
116
|
-
.option('--file <paths...>', 'File paths to attach')
|
|
117
|
-
.option('--replace-attachments', 'Replace existing attachments instead of adding')
|
|
118
|
-
.action(async (options) => {
|
|
119
|
-
try {
|
|
120
|
-
const formData = createFormData({
|
|
121
|
-
mealType: options.meal,
|
|
122
|
-
calories: options.calories,
|
|
123
|
-
protein: options.protein,
|
|
124
|
-
carbs: options.carbs,
|
|
125
|
-
fat: options.fat,
|
|
126
|
-
fiber: options.fiber,
|
|
127
|
-
sodium: options.sodium,
|
|
128
|
-
foods: options.foods,
|
|
129
|
-
water: options.water,
|
|
130
|
-
extraData: options.extraData,
|
|
131
|
-
note: options.note,
|
|
132
|
-
date: appendTimezoneOffset(options.date),
|
|
133
|
-
replaceAttachments: options.replaceAttachments ? 'true' : undefined
|
|
134
|
-
}, options.file || [])
|
|
135
|
-
|
|
136
|
-
const result = await request(`/diets/${options.id}`, {
|
|
137
|
-
method: 'PATCH',
|
|
138
|
-
body: formData,
|
|
139
|
-
isFormData: true
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
console.log('Diet record updated:', result.id)
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error('Failed to update diet record:', error.message)
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
diet
|
|
149
|
-
.command('delete')
|
|
150
|
-
.requiredOption('--id <id>', 'Diet record ID')
|
|
151
|
-
.action(async (options) => {
|
|
152
|
-
try {
|
|
153
|
-
await request(`/diets/${options.id}`, { method: 'DELETE' })
|
|
154
|
-
console.log('Diet record deleted')
|
|
155
|
-
} catch (error) {
|
|
156
|
-
console.error('Failed to delete diet record:', error.message)
|
|
157
|
-
}
|
|
158
|
-
})
|
|
1
|
+
import { createCrudCommand } from '../lib/crud-command.js'
|
|
2
|
+
|
|
3
|
+
const diet = createCrudCommand('diet', {
|
|
4
|
+
endpoint: '/diets',
|
|
5
|
+
fields: [
|
|
6
|
+
{ flag: 'meal', description: 'Meal type (breakfast/lunch/dinner/snack)', formKey: 'mealType', required: true },
|
|
7
|
+
{ flag: 'calories', description: 'Calories' },
|
|
8
|
+
{ flag: 'protein', description: 'Protein (g)' },
|
|
9
|
+
{ flag: 'carbs', description: 'Carbs (g)' },
|
|
10
|
+
{ flag: 'fat', description: 'Fat (g)' },
|
|
11
|
+
{ flag: 'fiber', description: 'Fiber (g)' },
|
|
12
|
+
{ flag: 'sodium', description: 'Sodium (mg)' },
|
|
13
|
+
{ flag: 'foods', description: 'Foods in format: "name:amount,name2:amount2"' },
|
|
14
|
+
{ flag: 'water', description: 'Water (ml)' },
|
|
15
|
+
{ flag: 'extra-data', description: 'Extra data (JSON string)', formKey: 'extraData' },
|
|
16
|
+
{ flag: 'note', description: 'Note' }
|
|
17
|
+
],
|
|
18
|
+
fileFields: ['file']
|
|
19
|
+
})
|
|
159
20
|
|
|
160
21
|
export default diet
|
package/src/commands/exercise.js
CHANGED
|
@@ -1,156 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.option('--note <note>', 'Note')
|
|
20
|
-
.option('--date <date>', 'Date (YYYY-MM-DD or ISO 8601 datetime)')
|
|
21
|
-
.option('--file <paths...>', 'File paths to attach')
|
|
22
|
-
.action(async (options) => {
|
|
23
|
-
try {
|
|
24
|
-
const formData = createFormData({
|
|
25
|
-
type: options.type,
|
|
26
|
-
duration: options.duration,
|
|
27
|
-
caloriesBurned: options.calories,
|
|
28
|
-
activities: options.activities,
|
|
29
|
-
heartRateAvg: options.heartRateAvg,
|
|
30
|
-
heartRateMax: options.heartRateMax,
|
|
31
|
-
feeling: options.feeling,
|
|
32
|
-
extraData: options.extraData,
|
|
33
|
-
location: options.location,
|
|
34
|
-
note: options.note,
|
|
35
|
-
date: appendTimezoneOffset(options.date)
|
|
36
|
-
}, options.file || [])
|
|
37
|
-
|
|
38
|
-
const result = await request('/exercises', {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
body: formData,
|
|
41
|
-
isFormData: true
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
console.log('Exercise record added:', result.id)
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error('Failed to add exercise record:', error.message)
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
exercise
|
|
51
|
-
.command('list')
|
|
52
|
-
.option('--type <type>', 'Filter by type')
|
|
53
|
-
.option('--last <period>', 'Last N days/weeks/months/years')
|
|
54
|
-
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
55
|
-
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
56
|
-
.option('--page <number>', 'Page number', '1')
|
|
57
|
-
.option('--limit <number>', 'Items per page', '20')
|
|
58
|
-
.option('--include-deleted', 'Include deleted records')
|
|
59
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
60
|
-
.action(async (options) => {
|
|
61
|
-
try {
|
|
62
|
-
const { params, page } = buildQueryParams(options)
|
|
63
|
-
const result = await request(`/exercises?${params.toString()}`)
|
|
64
|
-
outputData(result, { format: options.format, type: 'exercise-list', page })
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error('Failed to list exercise records:', error.message)
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
exercise
|
|
71
|
-
.command('stats')
|
|
72
|
-
.option('--last <period>', 'Last N days/weeks/months/years')
|
|
73
|
-
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
74
|
-
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
75
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
76
|
-
.action(async (options) => {
|
|
77
|
-
try {
|
|
78
|
-
const { params } = buildQueryParams(options)
|
|
79
|
-
const result = await request(`/exercises/stats?${params.toString()}`)
|
|
80
|
-
outputData(result, { format: options.format, type: 'exercise-stats' })
|
|
81
|
-
} catch (error) {
|
|
82
|
-
console.error('Failed to get exercise stats:', error.message)
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
exercise
|
|
87
|
-
.command('get')
|
|
88
|
-
.requiredOption('--id <id>', 'Exercise record ID')
|
|
89
|
-
.option('--format <format>', 'Output format: json, table, toon', 'json')
|
|
90
|
-
.action(async (options) => {
|
|
91
|
-
try {
|
|
92
|
-
const result = await request(`/exercises/${options.id}`)
|
|
93
|
-
outputData(result, { format: options.format, type: 'exercise-get' })
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error('Failed to get exercise record:', error.message)
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
exercise
|
|
100
|
-
.command('update')
|
|
101
|
-
.requiredOption('--id <id>', 'Exercise record ID')
|
|
102
|
-
.option('--type <type>', 'Updated type')
|
|
103
|
-
.option('--duration <value>', 'Updated duration')
|
|
104
|
-
.option('--calories <value>', 'Updated calories burned')
|
|
105
|
-
.option('--activities <string>', 'Updated activities')
|
|
106
|
-
.option('--heart-rate-avg <value>', 'Updated average heart rate')
|
|
107
|
-
.option('--heart-rate-max <value>', 'Updated max heart rate')
|
|
108
|
-
.option('--feeling <value>', 'Updated feeling')
|
|
109
|
-
.option('--extra-data <json>', 'Updated extra data (JSON string)')
|
|
110
|
-
.option('--location <location>', 'Updated location')
|
|
111
|
-
.option('--note <note>', 'Updated note')
|
|
112
|
-
.option('--date <date>', 'Updated date (YYYY-MM-DD or ISO 8601 datetime)')
|
|
113
|
-
.option('--file <paths...>', 'File paths to attach')
|
|
114
|
-
.option('--replace-attachments', 'Replace existing attachments instead of adding')
|
|
115
|
-
.action(async (options) => {
|
|
116
|
-
try {
|
|
117
|
-
const formData = createFormData({
|
|
118
|
-
type: options.type,
|
|
119
|
-
duration: options.duration,
|
|
120
|
-
caloriesBurned: options.calories,
|
|
121
|
-
activities: options.activities,
|
|
122
|
-
heartRateAvg: options.heartRateAvg,
|
|
123
|
-
heartRateMax: options.heartRateMax,
|
|
124
|
-
feeling: options.feeling,
|
|
125
|
-
extraData: options.extraData,
|
|
126
|
-
location: options.location,
|
|
127
|
-
note: options.note,
|
|
128
|
-
date: appendTimezoneOffset(options.date),
|
|
129
|
-
replaceAttachments: options.replaceAttachments ? 'true' : undefined
|
|
130
|
-
}, options.file || [])
|
|
131
|
-
|
|
132
|
-
const result = await request(`/exercises/${options.id}`, {
|
|
133
|
-
method: 'PATCH',
|
|
134
|
-
body: formData,
|
|
135
|
-
isFormData: true
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
console.log('Exercise record updated:', result.id)
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error('Failed to update exercise record:', error.message)
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
exercise
|
|
145
|
-
.command('delete')
|
|
146
|
-
.requiredOption('--id <id>', 'Exercise record ID')
|
|
147
|
-
.action(async (options) => {
|
|
148
|
-
try {
|
|
149
|
-
await request(`/exercises/${options.id}`, { method: 'DELETE' })
|
|
150
|
-
console.log('Exercise record deleted')
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error('Failed to delete exercise record:', error.message)
|
|
153
|
-
}
|
|
154
|
-
})
|
|
1
|
+
import { createCrudCommand } from '../lib/crud-command.js'
|
|
2
|
+
|
|
3
|
+
const exercise = createCrudCommand('exercise', {
|
|
4
|
+
endpoint: '/exercises',
|
|
5
|
+
fields: [
|
|
6
|
+
{ flag: 'type', description: 'Exercise type (running/strength/cycling/swimming/other)', required: true },
|
|
7
|
+
{ flag: 'duration', description: 'Duration in minutes', required: true },
|
|
8
|
+
{ flag: 'calories', description: 'Calories burned', formKey: 'caloriesBurned' },
|
|
9
|
+
{ flag: 'activities', description: 'Activities in format: "name:prop1=val1,prop2=val2;name2:prop1=val1"' },
|
|
10
|
+
{ flag: 'heart-rate-avg', description: 'Average heart rate', formKey: 'heartRateAvg' },
|
|
11
|
+
{ flag: 'heart-rate-max', description: 'Max heart rate', formKey: 'heartRateMax' },
|
|
12
|
+
{ flag: 'feeling', description: 'Feeling 1-10' },
|
|
13
|
+
{ flag: 'extra-data', description: 'Extra data (JSON string)', formKey: 'extraData' },
|
|
14
|
+
{ flag: 'location', description: 'Location' },
|
|
15
|
+
{ flag: 'note', description: 'Note' }
|
|
16
|
+
],
|
|
17
|
+
fileFields: ['file']
|
|
18
|
+
})
|
|
155
19
|
|
|
156
20
|
export default exercise
|
package/src/commands/food.js
CHANGED
|
@@ -21,7 +21,7 @@ food
|
|
|
21
21
|
const cleaned = items.map(({ rawItem, ...rest }) => rest)
|
|
22
22
|
outputData({ foods: cleaned, totalPages: 1, total: cleaned.length }, { format: options.format, type: 'food-list' })
|
|
23
23
|
} catch (error) {
|
|
24
|
-
console.error(error.message)
|
|
24
|
+
console.error('查询食物失败:', error.message)
|
|
25
25
|
process.exitCode = 1
|
|
26
26
|
}
|
|
27
27
|
})
|
package/src/commands/record.js
CHANGED
|
@@ -29,9 +29,9 @@ record
|
|
|
29
29
|
body: JSON.stringify(recordData)
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
console.log('
|
|
32
|
+
console.log('记录已添加:', result.id)
|
|
33
33
|
} catch (error) {
|
|
34
|
-
console.error('
|
|
34
|
+
console.error('添加记录失败:', error.message)
|
|
35
35
|
}
|
|
36
36
|
})
|
|
37
37
|
|
|
@@ -53,7 +53,7 @@ record
|
|
|
53
53
|
const result = await request(`/records?${params.toString()}`)
|
|
54
54
|
outputData(result, { format: options.format, type: 'record-list', page })
|
|
55
55
|
} catch (error) {
|
|
56
|
-
console.error('
|
|
56
|
+
console.error('获取记录列表失败:', error.message)
|
|
57
57
|
}
|
|
58
58
|
})
|
|
59
59
|
|
|
@@ -72,7 +72,7 @@ record
|
|
|
72
72
|
const result = await request(`/records/${options.id}${queryString ? '?' + queryString : ''}`)
|
|
73
73
|
outputData(result, { format: options.format, type: 'record-get' })
|
|
74
74
|
} catch (error) {
|
|
75
|
-
console.error('
|
|
75
|
+
console.error('获取记录失败:', error.message)
|
|
76
76
|
}
|
|
77
77
|
})
|
|
78
78
|
|
|
@@ -98,9 +98,9 @@ record
|
|
|
98
98
|
body: JSON.stringify(updateData)
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
-
console.log('
|
|
101
|
+
console.log('记录已更新:', result.id)
|
|
102
102
|
} catch (error) {
|
|
103
|
-
console.error('
|
|
103
|
+
console.error('更新记录失败:', error.message)
|
|
104
104
|
}
|
|
105
105
|
})
|
|
106
106
|
|
|
@@ -113,9 +113,9 @@ record
|
|
|
113
113
|
method: 'DELETE'
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
console.log('
|
|
116
|
+
console.log('记录已删除')
|
|
117
117
|
} catch (error) {
|
|
118
|
-
console.error('
|
|
118
|
+
console.error('删除记录失败:', error.message)
|
|
119
119
|
}
|
|
120
120
|
})
|
|
121
121
|
|
|
@@ -135,7 +135,7 @@ record
|
|
|
135
135
|
const result = await request(`/records/search?${params.toString()}`)
|
|
136
136
|
outputData(result, { format: options.format, type: 'record-list', page })
|
|
137
137
|
} catch (error) {
|
|
138
|
-
console.error('
|
|
138
|
+
console.error('搜索记录失败:', error.message)
|
|
139
139
|
}
|
|
140
140
|
})
|
|
141
141
|
|