fostrom 0.0.19 → 0.1.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 (3) hide show
  1. package/dl-agent.sh +22 -3
  2. package/index.js +24 -12
  3. package/package.json +1 -1
package/dl-agent.sh CHANGED
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Usage: ./dl-agent.sh <directory>
6
6
 
7
- VERSION="v0.0.17"
7
+ VERSION="v0.1.0"
8
8
 
9
9
  # CDN URLs in order of preference
10
10
  CDN_PRIMARY="https://cdn.fostrom.dev/fostrom-device-agent/$VERSION"
@@ -135,6 +135,14 @@ install_binary() {
135
135
  ln -sf "$FILENAME" "$INSTALL_DIR/fostrom-device-agent"
136
136
  }
137
137
 
138
+ read_installed_version() {
139
+ BIN="$1"
140
+ [ ! -x "$BIN" ] && return 1
141
+ RAW_VERSION="$("$BIN" version 2>/dev/null || true)"
142
+ [ -z "$RAW_VERSION" ] && return 1
143
+ printf "%s\n" "$RAW_VERSION" | tr -d '[:space:]'
144
+ }
145
+
138
146
  main() {
139
147
  # Detect OS and architecture
140
148
  OS="$(uname -s)"
@@ -165,9 +173,16 @@ main() {
165
173
 
166
174
  FILENAME="fostrom-device-agent-${OS}-${ARCH}"
167
175
 
168
- # Check if binary already exists
176
+ # Check if binary already exists and the version matches.
169
177
  if [ -f "$LOCATION/$FILENAME" ]; then
170
- exit 0
178
+ INSTALLED_VERSION="$(read_installed_version "$LOCATION/$FILENAME" || true)"
179
+ if [ "$INSTALLED_VERSION" = "$VERSION" ]; then
180
+ # Ensure symlink exists even when reusing.
181
+ ln -sf "$FILENAME" "$LOCATION/fostrom-device-agent"
182
+ exit 0
183
+ fi
184
+
185
+ printf "Updating Fostrom Device Agent to %s...\n" "$VERSION"
171
186
  fi
172
187
 
173
188
  printf "Downloading Fostrom Device Agent...\n"
@@ -179,6 +194,10 @@ main() {
179
194
  xattr -r -d com.apple.quarantine "$LOCATION/$FILENAME" 2>/dev/null || true
180
195
  fi
181
196
 
197
+ INSTALLED_VERSION="$(read_installed_version "$LOCATION/$FILENAME" || true)"
198
+ [ "$INSTALLED_VERSION" = "$VERSION" ] || die \
199
+ "Fatal: Installed Version Mismatch (expected $VERSION, got ${INSTALLED_VERSION:-unknown})"
200
+
182
201
  printf "Fostrom Device Agent downloaded successfully\n"
183
202
  }
184
203
 
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { execSync } from "child_process"
1
+ import { execFileSync } from "child_process"
2
2
  import { fileURLToPath } from "url"
3
3
  import path from "path"
4
4
  import http from "node:http"
@@ -40,6 +40,7 @@ export default class Fostrom {
40
40
  #log = true
41
41
  #creds = {}
42
42
  #sseBuffer = ""
43
+ #sseEvent = {}
43
44
  #sseReq = null
44
45
  #reconnectTimer = null
45
46
  #stopped = true
@@ -52,7 +53,7 @@ export default class Fostrom {
52
53
  onMail = async mail => {
53
54
  if (this.#log) {
54
55
  console.warn(`[Fostrom] Received Mail (Mailbox Size: ${mail.mailbox_size}): ${mail.name} -> ID ${mail.id}`)
55
- console.warn(" Auto-Acknowledging Mail. Define Mail Handler to handle incoming mail.\n `fostrom.on_mail = async (mail) => { ...; await mail.ack(); }`\n")
56
+ console.warn(" Auto-Acknowledging Mail. Define Mail Handler to handle incoming mail.\n `fostrom.onMail = async (mail) => { ...; await mail.ack(); }`\n")
56
57
  }
57
58
  await mail.ack()
58
59
  }
@@ -108,18 +109,18 @@ export default class Fostrom {
108
109
  env["FOSTROM_RUNTIME_ENV"] = String(this.#runtimeEnv)
109
110
  }
110
111
 
111
- const args = ["start"]
112
-
113
112
  try {
114
- const output = execSync(`${agent_path()} ${args.join(" ")}`, { encoding: "utf8", env })
113
+ const output = execFileSync(agent_path(), ["start"], { encoding: "utf8", env })
115
114
  const out = output.trim()
116
115
  if (out.startsWith("started:")) return
117
116
  if (out.startsWith("already_started:")) return
118
117
  return
119
118
  } catch (error) {
120
119
  const out = (error.stdout || "").toString().trim()
121
- if (out) {
122
- const [atom, rest] = out.split(":", 2)
120
+ const err = (error.stderr || "").toString().trim()
121
+ const text = out || err
122
+ if (text) {
123
+ const [atom, rest] = text.split(":", 2)
123
124
  throw new FostromError(atom || "failed", (rest || "Failed to start Device Agent").trim())
124
125
  }
125
126
  throw new FostromError("failed", "Failed to start Device Agent")
@@ -166,7 +167,7 @@ export default class Fostrom {
166
167
 
167
168
  static stopAgent() {
168
169
  try {
169
- execSync(`${agent_path()} stop`, { encoding: "utf8" })
170
+ execFileSync(agent_path(), ["stop"], { encoding: "utf8" })
170
171
  } catch (e) {
171
172
  console.error("[Fostrom] Failed to stop the Fostrom Device Agent")
172
173
  }
@@ -192,6 +193,8 @@ export default class Fostrom {
192
193
  }
193
194
  } catch { }
194
195
  this.#sseReq = null
196
+ this.#sseBuffer = ""
197
+ this.#sseEvent = {}
195
198
  const doStop = (stopAgent === null) ? this.#stopAgentOnExit : Boolean(stopAgent)
196
199
  if (doStop) Fostrom.stopAgent()
197
200
  }
@@ -282,6 +285,7 @@ export default class Fostrom {
282
285
  if (this.#stopped) return
283
286
  if (this.#sseReq) return
284
287
  this.#sseBuffer = ""
288
+ this.#sseEvent = {}
285
289
  const { fleet_id, device_id } = this.#creds
286
290
  const options = {
287
291
  socketPath: Fostrom.#SOCK,
@@ -298,6 +302,7 @@ export default class Fostrom {
298
302
  const scheduleReconnect = (delay) => {
299
303
  this.#sseReq = null
300
304
  this.#sseBuffer = ""
305
+ this.#sseEvent = {}
301
306
  if (!this.#stopped) {
302
307
  this.#reconnectTimer = setTimeout(() => this.#open_event_stream(), delay)
303
308
  }
@@ -306,7 +311,14 @@ export default class Fostrom {
306
311
  const req = http.request(options, (res) => {
307
312
  res.setEncoding('utf8')
308
313
  res.on('data', (chunk) => {
309
- this.#sseBuffer = Fostrom.#parse_events(this.#sseBuffer, chunk, this.#event_handler.bind(this))
314
+ const parsed = Fostrom.#parse_events(
315
+ this.#sseBuffer,
316
+ chunk,
317
+ this.#sseEvent,
318
+ this.#event_handler.bind(this)
319
+ )
320
+ this.#sseBuffer = parsed.buffer
321
+ this.#sseEvent = parsed.event
310
322
  })
311
323
  res.on('error', () => scheduleReconnect(500))
312
324
  res.on('aborted', () => scheduleReconnect(500))
@@ -376,12 +388,12 @@ export default class Fostrom {
376
388
  })
377
389
  }
378
390
 
379
- static #parse_events(buffer, chunk, event_handler) {
391
+ static #parse_events(buffer, chunk, currentEvent, event_handler) {
380
392
  buffer += chunk
381
393
  const lines = buffer.split('\n')
382
394
  buffer = lines.pop() || ''
383
395
 
384
- let event = {}
396
+ let event = currentEvent || {}
385
397
  for (const raw of lines) {
386
398
  const line = raw.replace(/\r$/, '')
387
399
  if (line === '') {
@@ -400,7 +412,7 @@ export default class Fostrom {
400
412
  }
401
413
  }
402
414
 
403
- return buffer
415
+ return { buffer, event }
404
416
  }
405
417
 
406
418
  async #deliverMail(mail) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fostrom",
3
- "version": "0.0.19",
3
+ "version": "0.1.0",
4
4
  "description": "Fostrom's Official Device SDK for JS. Fostrom (https://fostrom.io) is an IoT Cloud Platform.",
5
5
  "keywords": [
6
6
  "fostrom",