yoto-nodejs-client 0.0.1 → 0.0.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.
Files changed (92) hide show
  1. package/README.md +523 -30
  2. package/bin/auth.js +36 -46
  3. package/bin/content.js +0 -0
  4. package/bin/device-model.d.ts +3 -0
  5. package/bin/device-model.d.ts.map +1 -0
  6. package/bin/device-model.js +360 -0
  7. package/bin/device-tui.TODO.md +125 -0
  8. package/bin/device-tui.d.ts +31 -0
  9. package/bin/device-tui.d.ts.map +1 -0
  10. package/bin/device-tui.js +1123 -0
  11. package/bin/devices.js +166 -28
  12. package/bin/groups.js +0 -0
  13. package/bin/icons.js +0 -0
  14. package/bin/lib/cli-helpers.d.ts +33 -1
  15. package/bin/lib/cli-helpers.d.ts.map +1 -1
  16. package/bin/lib/cli-helpers.js +5 -5
  17. package/bin/lib/token-helpers.d.ts +32 -0
  18. package/bin/lib/token-helpers.d.ts.map +1 -1
  19. package/bin/refresh-token.js +6 -6
  20. package/bin/token-info.js +3 -3
  21. package/index.d.ts +4 -217
  22. package/index.d.ts.map +1 -1
  23. package/index.js +11 -689
  24. package/lib/api-client.d.ts +576 -0
  25. package/lib/api-client.d.ts.map +1 -0
  26. package/lib/api-client.js +681 -0
  27. package/lib/api-endpoints/auth.d.ts +280 -4
  28. package/lib/api-endpoints/auth.d.ts.map +1 -1
  29. package/lib/api-endpoints/auth.js +224 -7
  30. package/lib/api-endpoints/auth.test.js +54 -2
  31. package/lib/api-endpoints/constants.d.ts +30 -2
  32. package/lib/api-endpoints/constants.d.ts.map +1 -1
  33. package/lib/api-endpoints/constants.js +17 -10
  34. package/lib/api-endpoints/content.d.ts +760 -0
  35. package/lib/api-endpoints/content.d.ts.map +1 -1
  36. package/lib/api-endpoints/content.test.js +1 -1
  37. package/lib/api-endpoints/devices.d.ts +917 -48
  38. package/lib/api-endpoints/devices.d.ts.map +1 -1
  39. package/lib/api-endpoints/devices.js +114 -52
  40. package/lib/api-endpoints/devices.test.js +1 -1
  41. package/lib/api-endpoints/endpoint-test-helpers.d.ts +28 -0
  42. package/lib/api-endpoints/endpoint-test-helpers.d.ts.map +1 -0
  43. package/lib/api-endpoints/family-library-groups.d.ts +187 -0
  44. package/lib/api-endpoints/family-library-groups.d.ts.map +1 -1
  45. package/lib/api-endpoints/family-library-groups.test.js +1 -1
  46. package/lib/api-endpoints/family.d.ts +88 -0
  47. package/lib/api-endpoints/family.d.ts.map +1 -1
  48. package/lib/api-endpoints/family.test.js +1 -1
  49. package/lib/api-endpoints/helpers.d.ts +37 -3
  50. package/lib/api-endpoints/helpers.d.ts.map +1 -1
  51. package/lib/api-endpoints/icons.d.ts +196 -0
  52. package/lib/api-endpoints/icons.d.ts.map +1 -1
  53. package/lib/api-endpoints/icons.test.js +1 -1
  54. package/lib/api-endpoints/media.d.ts +83 -0
  55. package/lib/api-endpoints/media.d.ts.map +1 -1
  56. package/lib/helpers/power-state.d.ts +53 -0
  57. package/lib/helpers/power-state.d.ts.map +1 -0
  58. package/lib/helpers/power-state.js +73 -0
  59. package/lib/helpers/power-state.test.js +100 -0
  60. package/lib/helpers/temperature.d.ts +24 -0
  61. package/lib/helpers/temperature.d.ts.map +1 -0
  62. package/lib/helpers/temperature.js +61 -0
  63. package/lib/helpers/temperature.test.js +58 -0
  64. package/lib/helpers/typed-keys.d.ts +7 -0
  65. package/lib/helpers/typed-keys.d.ts.map +1 -0
  66. package/lib/helpers/typed-keys.js +8 -0
  67. package/lib/mqtt/client.d.ts +610 -7
  68. package/lib/mqtt/client.d.ts.map +1 -1
  69. package/lib/mqtt/client.js +213 -31
  70. package/lib/mqtt/commands.d.ts +195 -0
  71. package/lib/mqtt/commands.d.ts.map +1 -1
  72. package/lib/mqtt/factory.d.ts +62 -1
  73. package/lib/mqtt/factory.d.ts.map +1 -1
  74. package/lib/mqtt/factory.js +27 -5
  75. package/lib/mqtt/mqtt.test.js +85 -28
  76. package/lib/mqtt/topics.d.ts +186 -1
  77. package/lib/mqtt/topics.d.ts.map +1 -1
  78. package/lib/mqtt/topics.js +54 -20
  79. package/lib/pkg.d.cts +9 -0
  80. package/lib/token.d.ts +106 -3
  81. package/lib/token.d.ts.map +1 -1
  82. package/lib/token.js +30 -23
  83. package/lib/yoto-account.d.ts +163 -0
  84. package/lib/yoto-account.d.ts.map +1 -0
  85. package/lib/yoto-account.js +340 -0
  86. package/lib/yoto-device.d.ts +656 -0
  87. package/lib/yoto-device.d.ts.map +1 -0
  88. package/lib/yoto-device.js +2850 -0
  89. package/package.json +22 -15
  90. package/lib/api-endpoints/test-helpers.d.ts +0 -7
  91. package/lib/api-endpoints/test-helpers.d.ts.map +0 -1
  92. /package/lib/api-endpoints/{test-helpers.js → endpoint-test-helpers.js} +0 -0
package/bin/devices.js CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { printHelpText } from 'argsclopts'
8
8
  import { parseArgs } from 'node:util'
9
+ import * as readline from 'node:readline'
9
10
  import { pkg } from '../lib/pkg.cjs'
10
11
  import {
11
12
  getCommonOptions,
@@ -15,6 +16,7 @@ import {
15
16
  printHeader
16
17
  } from './lib/cli-helpers.js'
17
18
  import { createYotoMqttClient } from '../lib/mqtt/index.js'
19
+ import { parseTemperature } from '../lib/helpers/temperature.js'
18
20
 
19
21
  /** @type {ArgscloptsParseArgsOptionsConfig} */
20
22
  const options = {
@@ -27,16 +29,24 @@ const options = {
27
29
  status: {
28
30
  type: 'boolean',
29
31
  short: 's',
30
- help: 'Get device status (requires --device-id)'
32
+ help: 'Get only device status - battery, charging, online state (requires --device-id)'
31
33
  },
32
34
  mqtt: {
33
35
  type: 'boolean',
34
36
  short: 'm',
35
37
  help: 'Connect to MQTT and listen for device messages (requires --device-id)'
36
38
  },
37
- 'mqtt-stop-after-seconds': {
39
+ 'mqtt-timeout': {
38
40
  type: 'string',
39
41
  help: 'Auto-disconnect MQTT after N seconds (requires --mqtt)'
42
+ },
43
+ 'mqtt-request-status': {
44
+ type: 'string',
45
+ help: 'Request device status after N milliseconds of connecting (requires --mqtt, default: 1000)'
46
+ },
47
+ 'mqtt-request-events': {
48
+ type: 'string',
49
+ help: 'Request device events after N milliseconds of connecting (requires --mqtt, default: 3000)'
40
50
  }
41
51
  }
42
52
 
@@ -47,7 +57,7 @@ if (args.values['help']) {
47
57
  options,
48
58
  name: 'yoto-devices',
49
59
  version: pkg.version,
50
- exampleFn: ({ name }) => ` Yoto devices information helper\n\n Examples:\n ${name} # List all devices\n ${name} --device-id abc123 # Get specific device\n ${name} --device-id abc123 --status # Get device status\n ${name} --device-id abc123 --mqtt # Listen to MQTT messages\n ${name} --device-id abc123 --mqtt --mqtt-stop-after-seconds 10 # Sample MQTT for 10s\n`
60
+ exampleFn: ({ name }) => ` Yoto devices information helper\n\n Examples:\n ${name} # List all devices (GET /device-v2/devices/mine)\n ${name} --device-id abc123 # Get device details + config + shortcuts\n ${name} --device-id abc123 --status # Get only status (battery, charging, online)\n ${name} --device-id abc123 --mqtt # Listen to MQTT messages\n ${name} --device-id abc123 --mqtt --mqtt-timeout 10 # Sample MQTT for 10s\n ${name} --device-id abc123 --mqtt --mqtt-request-status 500 # Request status after 500ms\n ${name} --device-id abc123 --mqtt --mqtt-request-events 2000 # Request events after 2s\n`
51
61
  })
52
62
  process.exit(0)
53
63
  }
@@ -58,7 +68,9 @@ const { clientId, refreshToken, accessToken, envFile } = loadTokensFromEnv(args)
58
68
  const deviceId = args.values['device-id'] ? String(args.values['device-id']) : null
59
69
  const getStatus = Boolean(args.values['status'])
60
70
  const useMqtt = Boolean(args.values['mqtt'])
61
- const mqttStopAfter = args.values['mqtt-stop-after-seconds'] ? Number(args.values['mqtt-stop-after-seconds']) : null
71
+ const mqttStopAfter = args.values['mqtt-timeout'] ? Number(args.values['mqtt-timeout']) : null
72
+ const mqttRequestStatus = useMqtt && args.values['mqtt-request-status'] !== undefined ? Number(args.values['mqtt-request-status']) : null
73
+ const mqttRequestEvents = useMqtt && args.values['mqtt-request-events'] !== undefined ? Number(args.values['mqtt-request-events']) : null
62
74
 
63
75
  if (getStatus && !deviceId) {
64
76
  console.error('❌ --status flag requires --device-id')
@@ -73,8 +85,8 @@ if (useMqtt && !deviceId) {
73
85
  }
74
86
 
75
87
  if (mqttStopAfter && !useMqtt) {
76
- console.error('❌ --mqtt-stop-after-seconds requires --mqtt')
77
- console.error('Usage: yoto-devices --device-id abc123 --mqtt --mqtt-stop-after-seconds 10')
88
+ console.error('❌ --mqtt-timeout requires --mqtt')
89
+ console.error('Usage: yoto-devices --device-id abc123 --mqtt --mqtt-timeout 10')
78
90
  process.exit(1)
79
91
  }
80
92
 
@@ -93,12 +105,14 @@ async function main () {
93
105
  if (deviceId) {
94
106
  if (getStatus) {
95
107
  // Get device status
96
- console.log(`\nFetching status for device: ${deviceId}\n`)
108
+ console.log(`\n📡 GET /device-v2/devices/${deviceId}/status`)
109
+ console.log(`Fetching status for device: ${deviceId}\n`)
97
110
  const status = await client.getDeviceStatus({ deviceId })
98
111
  console.dir(status, { depth: null, colors: true })
99
112
  } else {
100
113
  // Get specific device from list
101
- console.log(`\nFetching device: ${deviceId}\n`)
114
+ console.log('\n📡 GET /device-v2/devices/mine')
115
+ console.log(`Fetching device: ${deviceId}\n`)
102
116
  const devicesResponse = await client.getDevices()
103
117
  const device = devicesResponse.devices.find(d => d.deviceId === deviceId)
104
118
 
@@ -106,13 +120,21 @@ async function main () {
106
120
  console.log('Device:')
107
121
  console.dir(device, { depth: null, colors: true })
108
122
 
123
+ // Get device status
124
+ console.log(`\n📡 GET /device-v2/devices/${deviceId}/status`)
125
+ console.log('Device Status:')
126
+ const status = await client.getDeviceStatus({ deviceId })
127
+ console.dir(status, { depth: null, colors: true })
128
+
109
129
  // Get device config
110
- console.log('\nDevice Config:')
130
+ console.log(`\n📡 GET /device-v2/devices/${deviceId}/config`)
131
+ console.log('Device Config:')
111
132
  const config = await client.getDeviceConfig({ deviceId })
112
133
  console.dir(config, { depth: null, colors: true })
113
134
 
114
135
  // Get device shortcuts
115
- console.log('\nDevice Shortcuts:')
136
+ console.log(`\n📡 GET /device-v2/devices/${deviceId}/config`)
137
+ console.log('Device Shortcuts:')
116
138
  const shortcuts = await client.getDeviceConfig({ deviceId })
117
139
  if (shortcuts.device?.shortcuts) {
118
140
  console.dir(shortcuts.device.shortcuts, { depth: null, colors: true })
@@ -126,7 +148,8 @@ async function main () {
126
148
  }
127
149
  } else {
128
150
  // Get all devices
129
- console.log('\nFetching all devices...\n')
151
+ console.log('\n📡 GET /device-v2/devices/mine')
152
+ console.log('Fetching all devices...\n')
130
153
  const devicesResponse = await client.getDevices()
131
154
  console.dir(devicesResponse, { depth: null, colors: true })
132
155
  }
@@ -145,43 +168,152 @@ async function main () {
145
168
  // Setup message handlers
146
169
  mqttClient.on('connected', () => {
147
170
  console.log('✅ Connected to MQTT broker')
148
- console.log(`📡 Subscribed to device/${deviceId}/*`)
149
- console.log('\n👂 Listening for messages... (Ctrl+C to stop)\n')
171
+ console.log('📡 Subscribed to topics:')
172
+ console.log(` • device/${deviceId}/data/events (playback events)`)
173
+ console.log(` • device/${deviceId}/data/status (regular status)`)
174
+ console.log(` • device/${deviceId}/status (legacy status - lifecycle events)`)
175
+ console.log(` • device/${deviceId}/response (command responses)`)
176
+ console.log('\n👂 Listening for messages... (Ctrl+C to stop)')
177
+ console.log('\n⌨️ Interactive Controls:')
178
+ console.log(' s = Request status')
179
+ console.log(' e = Request events')
180
+ console.log(' Enter = Insert dated separator line')
181
+ console.log(' q = Quit\n')
150
182
 
151
183
  if (mqttStopAfter) {
152
184
  console.log(`⏱️ Will auto-disconnect after ${mqttStopAfter} seconds\n`)
153
185
  }
154
186
 
155
- // Request initial status and events after a delay to ensure subscriptions are ready
156
- setTimeout(() => {
157
- mqttClient.requestStatus().catch(err => {
158
- console.error('⚠️ Failed to request status:', err.message)
159
- })
160
- mqttClient.requestEvents().catch(err => {
161
- console.error('⚠️ Failed to request events:', err.message)
162
- })
163
- }, 1000)
187
+ // Request initial status if configured
188
+ if (mqttRequestStatus !== null) {
189
+ console.log(`⏱️ Will request status after ${mqttRequestStatus}ms`)
190
+ setTimeout(() => {
191
+ const requestId = `auto-status-${Date.now()}`
192
+ mqttClient.requestStatus(requestId).catch(err => {
193
+ console.error('⚠️ Failed to request status:', err.message)
194
+ })
195
+ }, mqttRequestStatus)
196
+ }
197
+
198
+ // Request initial events if configured
199
+ if (mqttRequestEvents !== null) {
200
+ console.log(`⏱️ Will request events after ${mqttRequestEvents}ms`)
201
+ setTimeout(() => {
202
+ const requestId = `auto-events-${Date.now()}`
203
+ mqttClient.requestEvents(requestId).catch(err => {
204
+ console.error('⚠️ Failed to request events:', err.message)
205
+ })
206
+ }, mqttRequestEvents)
207
+ }
208
+
209
+ if (mqttRequestStatus === null && mqttRequestEvents === null) {
210
+ console.log('💡 Tip: Use --mqtt-request-status or --mqtt-request-events to request initial data')
211
+ }
212
+
213
+ console.log() // Add blank line for spacing
214
+
215
+ // Setup interactive keyboard input
216
+ readline.emitKeypressEvents(process.stdin)
217
+ if (process.stdin.isTTY) {
218
+ process.stdin.setRawMode(true)
219
+ }
220
+
221
+ process.stdin.on('keypress', (_str, key) => {
222
+ if (key.ctrl && key.name === 'c') {
223
+ cleanup()
224
+ return
225
+ }
226
+
227
+ if (key.name === 's') {
228
+ const requestId = `interactive-${Date.now()}`
229
+ console.log(`\n⌨️ Requesting status [${requestId}]...`)
230
+ mqttClient.requestStatus(requestId).catch(err => {
231
+ console.error('⚠️ Failed to request status:', err.message)
232
+ })
233
+ } else if (key.name === 'e') {
234
+ const requestId = `interactive-${Date.now()}`
235
+ console.log(`\n⌨️ Requesting events [${requestId}]...`)
236
+ mqttClient.requestEvents(requestId).catch(err => {
237
+ console.error('⚠️ Failed to request events:', err.message)
238
+ })
239
+ } else if (key.name === 'return') {
240
+ const timestamp = new Date().toISOString()
241
+ console.log(`\n${'─'.repeat(60)}`)
242
+ console.log(`📍 MARKER [${timestamp}]`)
243
+ console.log(`${'─'.repeat(60)}\n`)
244
+ } else if (key.name === 'q') {
245
+ cleanup()
246
+ }
247
+ })
164
248
  })
165
249
 
166
- mqttClient.on('events', (message) => {
250
+ mqttClient.on('events', (topic, payload) => {
167
251
  const timestamp = new Date().toISOString()
168
252
  console.log(`\n📨 EVENTS MESSAGE [${timestamp}]:`)
253
+ console.log(`Topic: ${topic}`)
169
254
  console.log('─'.repeat(60))
170
- console.dir(message, { depth: null, colors: true })
255
+ console.dir(payload, { depth: null, colors: true })
171
256
  })
172
257
 
173
- mqttClient.on('status', (message) => {
258
+ mqttClient.on('status', (topic, payload) => {
174
259
  const timestamp = new Date().toISOString()
175
260
  console.log(`\n📊 STATUS MESSAGE [${timestamp}]:`)
261
+ console.log(`Topic: ${topic}`)
262
+ console.log('─'.repeat(60))
263
+ console.dir(payload, { depth: null, colors: true })
264
+ })
265
+
266
+ mqttClient.on('status-legacy', (topic, payload) => {
267
+ const timestamp = new Date().toISOString()
268
+ console.log(`\n🔧 STATUS-LEGACY MESSAGE [${timestamp}]:`)
269
+ console.log(`Topic: ${topic}`)
176
270
  console.log('─'.repeat(60))
177
- console.dir(message, { depth: null, colors: true })
271
+
272
+ // Highlight lifecycle fields
273
+ if (payload.status?.shutDown) {
274
+ const shutDown = payload.status.shutDown
275
+ if (shutDown === 'nA') {
276
+ console.log('🟢 Device Status: RUNNING (shutDown: "nA")')
277
+ } else {
278
+ console.log(`🔴 Device Status: SHUTDOWN (shutDown: "${shutDown}")`)
279
+ }
280
+ }
281
+ if (payload.status?.upTime !== undefined) {
282
+ console.log(`⏱️ Uptime: ${payload.status.upTime}s`)
283
+ }
284
+ if (payload.status?.wifiStrength !== undefined) {
285
+ console.log(`📶 WiFi: ${payload.status.wifiStrength} dBm`)
286
+ }
287
+ if (payload.status?.battery !== undefined) {
288
+ console.log(`🔋 Battery: ${payload.status.battery} mV`)
289
+ }
290
+ if (payload.status?.temp !== undefined) {
291
+ const tempStr = String(payload.status.temp)
292
+ const parsedTemp = parseTemperature(payload.status.temp)
293
+ if (parsedTemp !== null) {
294
+ console.log(`🌡️ Temperature: ${parsedTemp}°C (raw: "${tempStr}")`)
295
+ } else {
296
+ console.log(`🌡️ Temperature: ${tempStr} (unavailable)`)
297
+ }
298
+ }
299
+ console.log('─'.repeat(60))
300
+ console.dir(payload, { depth: null, colors: true })
178
301
  })
179
302
 
180
- mqttClient.on('response', (message) => {
303
+ mqttClient.on('response', (topic, payload) => {
181
304
  const timestamp = new Date().toISOString()
182
305
  console.log(`\n✉️ RESPONSE MESSAGE [${timestamp}]:`)
306
+ console.log(`Topic: ${topic}`)
183
307
  console.log('─'.repeat(60))
184
- console.dir(message, { depth: null, colors: true })
308
+ console.dir(payload, { depth: null, colors: true })
309
+ })
310
+
311
+ mqttClient.on('unknown', (topic, payload) => {
312
+ const timestamp = new Date().toISOString()
313
+ console.log(`\n❓ UNKNOWN MESSAGE [${timestamp}]:`)
314
+ console.log(`Topic: ${topic}`)
315
+ console.log('─'.repeat(60))
316
+ console.dir(payload, { depth: null, colors: true })
185
317
  })
186
318
 
187
319
  mqttClient.on('disconnected', () => {
@@ -200,6 +332,12 @@ async function main () {
200
332
  const cleanup = async () => {
201
333
  console.log('\n\n🛑 Shutting down...')
202
334
  try {
335
+ // Restore terminal
336
+ if (process.stdin.isTTY) {
337
+ process.stdin.setRawMode(false)
338
+ }
339
+ process.stdin.pause()
340
+
203
341
  await mqttClient.disconnect()
204
342
  console.log('✅ Disconnected cleanly')
205
343
  process.exit(0)
package/bin/groups.js CHANGED
File without changes
package/bin/icons.js CHANGED
File without changes
@@ -1,5 +1,20 @@
1
+ /**
2
+ * Get common CLI option definitions shared across all tools
3
+ * @returns {ArgscloptsParseArgsOptionsConfig}
4
+ */
1
5
  export function getCommonOptions(): ArgscloptsParseArgsOptionsConfig;
6
+ /**
7
+ * Load .env file from specified path or default to .env in cwd
8
+ * @param {string} [envFile] - Optional path to .env file
9
+ * @returns {string} The path that was loaded (or attempted)
10
+ */
2
11
  export function loadEnvFile(envFile?: string): string;
12
+ /**
13
+ * Load and validate tokens from environment
14
+ * @param {{ values: Record<string, string | boolean | undefined> }} args - Parsed arguments from parseArgs
15
+ * @returns {{ clientId: string, refreshToken: string, accessToken: string, envFile: string }}
16
+ * @throws Exits process if tokens are missing
17
+ */
3
18
  export function loadTokensFromEnv(args: {
4
19
  values: Record<string, string | boolean | undefined>;
5
20
  }): {
@@ -8,14 +23,31 @@ export function loadTokensFromEnv(args: {
8
23
  accessToken: string;
9
24
  envFile: string;
10
25
  };
26
+ /**
27
+ * Create YotoClient with standard token persistence callbacks
28
+ * @param {object} options
29
+ * @param {string} options.clientId - OAuth client ID
30
+ * @param {string} options.refreshToken - OAuth refresh token
31
+ * @param {string} options.accessToken - OAuth access token
32
+ * @param {string} [options.outputFile='.env'] - File to save refreshed tokens to
33
+ * @returns {YotoClient}
34
+ */
11
35
  export function createYotoClient({ clientId, refreshToken, accessToken, outputFile }: {
12
36
  clientId: string;
13
37
  refreshToken: string;
14
38
  accessToken: string;
15
39
  outputFile?: string | undefined;
16
40
  }): YotoClient;
41
+ /**
42
+ * Standard error handler for CLI tools
43
+ * @param {any} error
44
+ */
17
45
  export function handleCliError(error: any): void;
46
+ /**
47
+ * Print CLI tool header with title
48
+ * @param {string} title - The title to display
49
+ */
18
50
  export function printHeader(title: string): void;
19
51
  import type { ArgscloptsParseArgsOptionsConfig } from 'argsclopts';
20
- import { YotoClient } from '../../index.js';
52
+ import { YotoClient } from '../../lib/api-client.js';
21
53
  //# sourceMappingURL=cli-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["cli-helpers.js"],"names":[],"mappings":"AAYA,oCAFa,gCAAgC,CAoB5C;AAOD,sCAHW,MAAM,GACJ,MAAM,CAUlB;AAQD,wCAJW;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;CAAE,GACtD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAoB5F;AAWD,sFANG;IAAwB,QAAQ,EAAxB,MAAM;IACU,YAAY,EAA5B,MAAM;IACU,WAAW,EAA3B,MAAM;IACW,UAAU;CACnC,GAAU,UAAU,CAsBtB;AAMD,sCAFW,GAAG,QAyBb;AAMD,mCAFW,MAAM,QAKhB;sDA1IoD,YAAY;2BAGtC,gBAAgB"}
1
+ {"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["cli-helpers.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH,oCAFa,gCAAgC,CAoB5C;AAED;;;;GAIG;AACH,sCAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;GAKG;AACH,wCAJW;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;CAAE,GACtD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAoB5F;AAED;;;;;;;;GAQG;AACH,sFANG;IAAwB,QAAQ,EAAxB,MAAM;IACU,YAAY,EAA5B,MAAM;IACU,WAAW,EAA3B,MAAM;IACW,UAAU;CACnC,GAAU,UAAU,CAsBtB;AAED;;;GAGG;AACH,sCAFW,GAAG,QAyBb;AAED;;;GAGG;AACH,mCAFW,MAAM,QAKhB;sDA1IoD,YAAY;2BAGtC,yBAAyB"}
@@ -2,7 +2,7 @@
2
2
  * @import { ArgscloptsParseArgsOptionsConfig } from 'argsclopts'
3
3
  */
4
4
 
5
- import { YotoClient } from '../../index.js'
5
+ import { YotoClient } from '../../lib/api-client.js'
6
6
  import { DEFAULT_CLIENT_ID } from '../../lib/api-endpoints/constants.js'
7
7
  import { saveTokensToEnv } from './token-helpers.js'
8
8
 
@@ -39,7 +39,7 @@ export function loadEnvFile (envFile) {
39
39
  const envPath = envFile || '.env'
40
40
  try {
41
41
  process.loadEnvFile(envPath)
42
- } catch (err) {
42
+ } catch (_err) {
43
43
  // File doesn't exist or can't be loaded, that's okay
44
44
  }
45
45
  return envPath
@@ -87,10 +87,10 @@ export function createYotoClient ({ clientId, refreshToken, accessToken, outputF
87
87
  onTokenRefresh: async (tokens) => {
88
88
  // Save tokens if they refresh during operation
89
89
  await saveTokensToEnv(outputFile, {
90
- access_token: tokens.accessToken,
91
- refresh_token: tokens.refreshToken,
90
+ access_token: tokens.updatedAccessToken,
91
+ refresh_token: tokens.updatedRefreshToken,
92
92
  token_type: 'Bearer',
93
- expires_in: tokens.expiresAt - Math.floor(Date.now() / 1000)
93
+ expires_in: tokens.updatedExpiresAt - Math.floor(Date.now() / 1000)
94
94
  }, tokens.clientId)
95
95
  },
96
96
  onRefreshStart: () => {
@@ -1,14 +1,46 @@
1
+ /**
2
+ * Format timestamp
3
+ * @param {number} timestamp
4
+ * @returns {string}
5
+ */
1
6
  export function formatTimestamp(timestamp: number): string;
7
+ /**
8
+ * Check if token is expired
9
+ * @param {number} exp
10
+ * @param {number} bufferSeconds
11
+ * @returns {{expired: boolean, message: string}}
12
+ */
2
13
  export function checkExpiration(exp: number, bufferSeconds?: number): {
3
14
  expired: boolean;
4
15
  message: string;
5
16
  };
17
+ /**
18
+ * Check if a JWT token is expired or about to expire
19
+ * @param {string} token - JWT token to check
20
+ * @param {number} bufferSeconds - Seconds before expiration to consider expired
21
+ * @returns {{expired: boolean, message: string}}
22
+ */
6
23
  export function checkTokenExpiration(token: string, bufferSeconds?: number): {
7
24
  expired: boolean;
8
25
  message: string;
9
26
  };
27
+ /**
28
+ * Decode a JWT token without verification
29
+ * @param {string} token
30
+ * @returns {any}
31
+ */
10
32
  export function decodeJwt(token: string): any;
33
+ /**
34
+ * Save tokens to .env file
35
+ * @param {string} filename
36
+ * @param {YotoTokenResponse} tokens
37
+ * @param {string} clientId
38
+ */
11
39
  export function saveTokensToEnv(filename: string, tokens: YotoTokenResponse, clientId: string): Promise<void>;
40
+ /**
41
+ * Sleep for a specified number of milliseconds
42
+ * @param {number} ms
43
+ */
12
44
  export function sleep(ms: number): Promise<any>;
13
45
  import type { YotoTokenResponse } from '../../lib/api-endpoints/auth.js';
14
46
  //# sourceMappingURL=token-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-helpers.d.ts","sourceRoot":"","sources":["token-helpers.js"],"names":[],"mappings":"AAaA,2CAHW,MAAM,GACJ,MAAM,CAKlB;AAQD,qCAJW,MAAM,kBACN,MAAM,GACJ;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAmB/C;AAQD,4CAJW,MAAM,kBACN,MAAM,GACJ;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAa/C;AAOD,iCAHW,MAAM,GACJ,GAAG,CASf;AAQD,0CAJW,MAAM,UACN,iBAAiB,YACjB,MAAM,iBA8DhB;AAMD,0BAFW,MAAM,gBAIhB;uCArJmC,iCAAiC"}
1
+ {"version":3,"file":"token-helpers.d.ts","sourceRoot":"","sources":["token-helpers.js"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,2CAHW,MAAM,GACJ,MAAM,CAKlB;AAED;;;;;GAKG;AACH,qCAJW,MAAM,kBACN,MAAM,GACJ;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAmB/C;AAED;;;;;GAKG;AACH,4CAJW,MAAM,kBACN,MAAM,GACJ;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAa/C;AAED;;;;GAIG;AACH,iCAHW,MAAM,GACJ,GAAG,CASf;AAED;;;;;GAKG;AACH,0CAJW,MAAM,UACN,iBAAiB,YACjB,MAAM,iBA8DhB;AAED;;;GAGG;AACH,0BAFW,MAAM,gBAIhB;uCArJmC,iCAAiC"}
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { printHelpText } from 'argsclopts'
8
8
  import { parseArgs } from 'node:util'
9
- import { YotoClient } from '../index.js'
9
+ import { YotoClient } from '../lib/api-client.js'
10
10
  import { pkg } from '../lib/pkg.cjs'
11
11
  import { saveTokensToEnv } from './lib/token-helpers.js'
12
12
  import {
@@ -91,10 +91,10 @@ async function main () {
91
91
  // This will be called after successful refresh
92
92
  // Convert RefreshSuccessEvent to YotoTokenResponse format
93
93
  await saveTokensToEnv(outputFile, {
94
- access_token: tokens.accessToken,
95
- refresh_token: tokens.refreshToken,
94
+ access_token: tokens.updatedAccessToken,
95
+ refresh_token: tokens.updatedRefreshToken,
96
96
  token_type: 'Bearer',
97
- expires_in: tokens.expiresAt - Math.floor(Date.now() / 1000)
97
+ expires_in: tokens.updatedExpiresAt - Math.floor(Date.now() / 1000)
98
98
  }, tokens.clientId)
99
99
  },
100
100
  onRefreshStart: () => {
@@ -136,8 +136,8 @@ async function main () {
136
136
  console.log('\n✅ Token refresh successful!')
137
137
 
138
138
  // Show expiration info
139
- const expiresDate = new Date(tokens.expiresAt * 1000)
140
- const timeRemaining = tokens.expiresAt - Math.floor(Date.now() / 1000)
139
+ const expiresDate = new Date(tokens.updatedExpiresAt * 1000)
140
+ const timeRemaining = tokens.updatedExpiresAt - Math.floor(Date.now() / 1000)
141
141
  const minutes = Math.floor(timeRemaining / 60)
142
142
  const hours = Math.floor(minutes / 60)
143
143
 
package/bin/token-info.js CHANGED
@@ -298,10 +298,10 @@ try {
298
298
  // Save tokens if they refresh during inspection
299
299
  console.log('\n⚠️ Token was refreshed during inspection! Saving...')
300
300
  await saveTokensToEnv(outputFile, {
301
- access_token: tokens.accessToken,
302
- refresh_token: tokens.refreshToken,
301
+ access_token: tokens.updatedAccessToken,
302
+ refresh_token: tokens.updatedRefreshToken,
303
303
  token_type: 'Bearer',
304
- expires_in: tokens.expiresAt - Math.floor(Date.now() / 1000)
304
+ expires_in: tokens.updatedExpiresAt - Math.floor(Date.now() / 1000)
305
305
  }, tokens.clientId)
306
306
  console.log(`✅ Updated tokens saved to ${outputFile}`)
307
307
  },