@alwaysai/device-agent 1.5.0 → 2.0.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 (273) hide show
  1. package/lib/application-control/config.d.ts.map +1 -1
  2. package/lib/application-control/config.js +8 -3
  3. package/lib/application-control/config.js.map +1 -1
  4. package/lib/application-control/environment-variables.d.ts +1 -5
  5. package/lib/application-control/environment-variables.d.ts.map +1 -1
  6. package/lib/application-control/environment-variables.js +9 -26
  7. package/lib/application-control/environment-variables.js.map +1 -1
  8. package/lib/application-control/environment-variables.test.js +27 -7
  9. package/lib/application-control/environment-variables.test.js.map +1 -1
  10. package/lib/application-control/index.d.ts +4 -4
  11. package/lib/application-control/index.d.ts.map +1 -1
  12. package/lib/application-control/index.js +1 -4
  13. package/lib/application-control/index.js.map +1 -1
  14. package/lib/application-control/install.d.ts.map +1 -1
  15. package/lib/application-control/install.js +8 -7
  16. package/lib/application-control/install.js.map +1 -1
  17. package/lib/application-control/models.d.ts +0 -11
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +5 -54
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/utils.d.ts +0 -4
  22. package/lib/application-control/utils.d.ts.map +1 -1
  23. package/lib/application-control/utils.js +1 -24
  24. package/lib/application-control/utils.js.map +1 -1
  25. package/lib/cloud-connection/bootstrap-provision.js +3 -2
  26. package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
  27. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +10 -15
  28. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  29. package/lib/cloud-connection/device-agent-cloud-connection.js +279 -250
  30. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  31. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  32. package/lib/cloud-connection/device-agent.js +11 -9
  33. package/lib/cloud-connection/device-agent.js.map +1 -1
  34. package/lib/cloud-connection/live-updates-handler.d.ts +18 -28
  35. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  36. package/lib/cloud-connection/live-updates-handler.js +54 -169
  37. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  38. package/lib/cloud-connection/live-updates-handler.test.js +71 -165
  39. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  40. package/lib/cloud-connection/passthrough-handler.d.ts +4 -1
  41. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  42. package/lib/cloud-connection/passthrough-handler.js +30 -11
  43. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  44. package/lib/cloud-connection/shadow-handler.d.ts +5 -3
  45. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  46. package/lib/cloud-connection/shadow-handler.js +59 -27
  47. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  48. package/lib/cloud-connection/shadow-handler.test.js +45 -57
  49. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  50. package/lib/cloud-connection/shadow.d.ts.map +1 -1
  51. package/lib/cloud-connection/shadow.js +2 -1
  52. package/lib/cloud-connection/shadow.js.map +1 -1
  53. package/lib/cloud-connection/transaction-manager.d.ts +4 -2
  54. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  55. package/lib/cloud-connection/transaction-manager.js +18 -29
  56. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  57. package/lib/cloud-connection/transaction-manager.test.js +3 -3
  58. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  59. package/lib/device-control/device-control.d.ts +8 -8
  60. package/lib/device-control/device-control.d.ts.map +1 -1
  61. package/lib/device-control/device-control.js +95 -71
  62. package/lib/device-control/device-control.js.map +1 -1
  63. package/lib/docker/docker-compose.d.ts.map +1 -1
  64. package/lib/docker/docker-compose.js +2 -1
  65. package/lib/docker/docker-compose.js.map +1 -1
  66. package/lib/infrastructure/agent-config.d.ts +2 -1
  67. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  68. package/lib/infrastructure/agent-config.js +7 -7
  69. package/lib/infrastructure/agent-config.js.map +1 -1
  70. package/lib/infrastructure/agent-config.test.js +3 -1
  71. package/lib/infrastructure/agent-config.test.js.map +1 -1
  72. package/lib/infrastructure/config-check-utility.d.ts +6 -0
  73. package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
  74. package/lib/infrastructure/config-check-utility.js +67 -0
  75. package/lib/infrastructure/config-check-utility.js.map +1 -0
  76. package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
  77. package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
  78. package/lib/infrastructure/config-check-utility.test.js +109 -0
  79. package/lib/infrastructure/config-check-utility.test.js.map +1 -0
  80. package/lib/infrastructure/device-certificate.d.ts +10 -0
  81. package/lib/infrastructure/device-certificate.d.ts.map +1 -0
  82. package/lib/infrastructure/device-certificate.js +47 -0
  83. package/lib/infrastructure/device-certificate.js.map +1 -0
  84. package/lib/infrastructure/device-certificate.test.d.ts +2 -0
  85. package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
  86. package/lib/infrastructure/device-certificate.test.js +24 -0
  87. package/lib/infrastructure/device-certificate.test.js.map +1 -0
  88. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
  89. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
  90. package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
  91. package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
  92. package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
  93. package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
  94. package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
  95. package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
  96. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
  97. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
  98. package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
  99. package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
  100. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
  101. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
  102. package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
  103. package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
  104. package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
  105. package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
  106. package/lib/infrastructure/require-files-present-ready.test.js +44 -0
  107. package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
  108. package/lib/infrastructure/required-config-checks.d.ts +2 -0
  109. package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
  110. package/lib/infrastructure/required-config-checks.js +30 -0
  111. package/lib/infrastructure/required-config-checks.js.map +1 -0
  112. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  113. package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
  114. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  115. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
  116. package/lib/local-connection/rabbitmq-connection.js +14 -14
  117. package/lib/local-connection/rabbitmq-connection.js.map +1 -1
  118. package/lib/secure-tunneling/secure-tunneling.d.ts +9 -9
  119. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
  120. package/lib/secure-tunneling/secure-tunneling.js +21 -16
  121. package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
  122. package/lib/secure-tunneling/secure-tunneling.test.js +11 -13
  123. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
  124. package/lib/subcommands/app/analytics.d.ts.map +1 -1
  125. package/lib/subcommands/app/analytics.js +1 -2
  126. package/lib/subcommands/app/analytics.js.map +1 -1
  127. package/lib/subcommands/app/env-vars.d.ts +4 -0
  128. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  129. package/lib/subcommands/app/env-vars.js +52 -6
  130. package/lib/subcommands/app/env-vars.js.map +1 -1
  131. package/lib/subcommands/app/index.d.ts.map +1 -1
  132. package/lib/subcommands/app/index.js +1 -3
  133. package/lib/subcommands/app/index.js.map +1 -1
  134. package/lib/subcommands/app/models.d.ts +0 -11
  135. package/lib/subcommands/app/models.d.ts.map +1 -1
  136. package/lib/subcommands/app/models.js +2 -58
  137. package/lib/subcommands/app/models.js.map +1 -1
  138. package/lib/subcommands/app/shadow.d.ts.map +1 -1
  139. package/lib/subcommands/app/shadow.js +6 -5
  140. package/lib/subcommands/app/shadow.js.map +1 -1
  141. package/lib/subcommands/app/version.d.ts.map +1 -1
  142. package/lib/subcommands/app/version.js +2 -4
  143. package/lib/subcommands/app/version.js.map +1 -1
  144. package/lib/subcommands/config.d.ts +2 -0
  145. package/lib/subcommands/config.d.ts.map +1 -0
  146. package/lib/subcommands/config.js +39 -0
  147. package/lib/subcommands/config.js.map +1 -0
  148. package/lib/subcommands/device/clean.d.ts +1 -1
  149. package/lib/subcommands/device/clean.d.ts.map +1 -1
  150. package/lib/subcommands/device/clean.js +23 -13
  151. package/lib/subcommands/device/clean.js.map +1 -1
  152. package/lib/subcommands/device/index.d.ts.map +1 -1
  153. package/lib/subcommands/device/index.js +3 -1
  154. package/lib/subcommands/device/index.js.map +1 -1
  155. package/lib/subcommands/device/init.js +8 -8
  156. package/lib/subcommands/device/init.js.map +1 -1
  157. package/lib/subcommands/device/migrate.d.ts +2 -0
  158. package/lib/subcommands/device/migrate.d.ts.map +1 -0
  159. package/lib/subcommands/device/migrate.js +24 -0
  160. package/lib/subcommands/device/migrate.js.map +1 -0
  161. package/lib/subcommands/device/refresh.d.ts.map +1 -1
  162. package/lib/subcommands/device/refresh.js +1 -0
  163. package/lib/subcommands/device/refresh.js.map +1 -1
  164. package/lib/subcommands/index.d.ts +1 -1
  165. package/lib/subcommands/index.d.ts.map +1 -1
  166. package/lib/subcommands/index.js +3 -1
  167. package/lib/subcommands/index.js.map +1 -1
  168. package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
  169. package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
  170. package/lib/util/aai-error.d.ts +12 -0
  171. package/lib/util/aai-error.d.ts.map +1 -0
  172. package/lib/util/aai-error.js +11 -0
  173. package/lib/util/aai-error.js.map +1 -0
  174. package/lib/util/aws-regions.d.ts +2 -0
  175. package/lib/util/aws-regions.d.ts.map +1 -0
  176. package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
  177. package/lib/util/aws-regions.js.map +1 -0
  178. package/lib/util/check-for-updates.d.ts.map +1 -1
  179. package/lib/util/check-for-updates.js +5 -28
  180. package/lib/util/check-for-updates.js.map +1 -1
  181. package/lib/util/clean-certs.d.ts.map +1 -1
  182. package/lib/util/clean-certs.js +5 -4
  183. package/lib/util/clean-certs.js.map +1 -1
  184. package/lib/util/directories.d.ts +4 -18
  185. package/lib/util/directories.d.ts.map +1 -1
  186. package/lib/util/directories.js +18 -32
  187. package/lib/util/directories.js.map +1 -1
  188. package/lib/util/file.d.ts +4 -0
  189. package/lib/util/file.d.ts.map +1 -1
  190. package/lib/util/file.js +65 -4
  191. package/lib/util/file.js.map +1 -1
  192. package/lib/util/get-device-id.d.ts.map +1 -1
  193. package/lib/util/get-device-id.js +7 -1
  194. package/lib/util/get-device-id.js.map +1 -1
  195. package/lib/util/http-client.js +3 -3
  196. package/lib/util/http-client.js.map +1 -1
  197. package/package.json +19 -17
  198. package/readme.md +12 -32
  199. package/src/application-control/config.ts +9 -12
  200. package/src/application-control/environment-variables.test.ts +28 -7
  201. package/src/application-control/environment-variables.ts +13 -40
  202. package/src/application-control/index.ts +3 -16
  203. package/src/application-control/install.ts +15 -10
  204. package/src/application-control/models.ts +6 -87
  205. package/src/application-control/utils.ts +0 -28
  206. package/src/cloud-connection/bootstrap-provision.ts +7 -7
  207. package/src/cloud-connection/device-agent-cloud-connection.ts +639 -525
  208. package/src/cloud-connection/device-agent.ts +16 -7
  209. package/src/cloud-connection/live-updates-handler.test.ts +121 -189
  210. package/src/cloud-connection/live-updates-handler.ts +99 -234
  211. package/src/cloud-connection/passthrough-handler.ts +55 -18
  212. package/src/cloud-connection/shadow-handler.test.ts +45 -57
  213. package/src/cloud-connection/shadow-handler.ts +103 -57
  214. package/src/cloud-connection/shadow.ts +4 -1
  215. package/src/cloud-connection/transaction-manager.test.ts +3 -3
  216. package/src/cloud-connection/transaction-manager.ts +53 -39
  217. package/src/device-control/device-control.ts +102 -70
  218. package/src/docker/docker-compose.ts +3 -2
  219. package/src/infrastructure/agent-config.test.ts +6 -2
  220. package/src/infrastructure/agent-config.ts +8 -7
  221. package/src/infrastructure/config-check-utility.test.ts +154 -0
  222. package/src/infrastructure/config-check-utility.ts +77 -0
  223. package/src/infrastructure/device-certificate.test.ts +40 -0
  224. package/src/infrastructure/device-certificate.ts +58 -0
  225. package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
  226. package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
  227. package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
  228. package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
  229. package/src/infrastructure/require-files-present-ready.test.ts +53 -0
  230. package/src/infrastructure/required-config-checks.ts +33 -0
  231. package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
  232. package/src/local-connection/rabbitmq-connection.ts +22 -17
  233. package/src/secure-tunneling/secure-tunneling.test.ts +20 -22
  234. package/src/secure-tunneling/secure-tunneling.ts +41 -29
  235. package/src/subcommands/app/analytics.ts +2 -4
  236. package/src/subcommands/app/env-vars.ts +72 -9
  237. package/src/subcommands/app/index.ts +3 -11
  238. package/src/subcommands/app/models.ts +5 -81
  239. package/src/subcommands/app/shadow.ts +6 -5
  240. package/src/subcommands/app/version.ts +3 -4
  241. package/src/subcommands/config.ts +42 -0
  242. package/src/subcommands/device/clean.ts +31 -17
  243. package/src/subcommands/device/index.ts +3 -1
  244. package/src/subcommands/device/init.ts +11 -11
  245. package/src/subcommands/device/migrate.ts +20 -0
  246. package/src/subcommands/device/refresh.ts +1 -0
  247. package/src/subcommands/index.ts +3 -1
  248. package/src/util/aai-error.ts +20 -0
  249. package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
  250. package/src/util/check-for-updates.ts +14 -30
  251. package/src/util/clean-certs.ts +8 -4
  252. package/src/util/directories.ts +23 -67
  253. package/src/util/file.ts +83 -3
  254. package/src/util/get-device-id.ts +7 -7
  255. package/src/util/http-client.ts +2 -2
  256. package/lib/util/cloud-mode-ready.d.ts +0 -3
  257. package/lib/util/cloud-mode-ready.d.ts.map +0 -1
  258. package/lib/util/cloud-mode-ready.js.map +0 -1
  259. package/lib/util/download-file.d.ts +0 -6
  260. package/lib/util/download-file.d.ts.map +0 -1
  261. package/lib/util/download-file.js +0 -25
  262. package/lib/util/download-file.js.map +0 -1
  263. package/lib/util/fetch-with-timeout.d.ts +0 -4
  264. package/lib/util/fetch-with-timeout.d.ts.map +0 -1
  265. package/lib/util/fetch-with-timeout.js +0 -30
  266. package/lib/util/fetch-with-timeout.js.map +0 -1
  267. package/lib/util/parsing.d.ts +0 -2
  268. package/lib/util/parsing.d.ts.map +0 -1
  269. package/lib/util/parsing.js +0 -17
  270. package/lib/util/parsing.js.map +0 -1
  271. package/src/util/download-file.ts +0 -25
  272. package/src/util/fetch-with-timeout.ts +0 -35
  273. package/src/util/parsing.ts +0 -11
@@ -1,23 +1,10 @@
1
- import {
2
- CliLeaf,
3
- CliNumberInput,
4
- CliStringArrayInput,
5
- CliStringInput
6
- } from '@alwaysai/alwayscli';
7
- import {
8
- getAppModels,
9
- readAppCfgFile,
10
- removeModel,
11
- replaceModels,
12
- updateModels
13
- } from '../../application-control';
1
+ import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
2
+ import { getAppModels, readAppCfgFile } from '../../application-control';
14
3
  import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
15
4
  import sleep from '../../util/sleep';
16
- import { logger } from '../../util/logger';
17
5
  import {
18
- buildUpdateProjectShadowMessage,
19
- getShadowTopic,
20
- validateShadowProjectsUpdateAll
6
+ buildUpdateShadowMessage,
7
+ getShadowTopic
21
8
  } from '@alwaysai/device-agent-schemas';
22
9
 
23
10
  export const showAppModelsCliLeaf = CliLeaf({
@@ -56,7 +43,6 @@ export const addModelCliLeaf = CliLeaf({
56
43
  async action(_, opts) {
57
44
  const { project: projectId, model, version } = opts;
58
45
  const deviceAgent = new DeviceAgentCloudConnection();
59
- await deviceAgent.setupHandlers();
60
46
 
61
47
  // Update the shadow as a client
62
48
 
@@ -71,7 +57,7 @@ export const addModelCliLeaf = CliLeaf({
71
57
  deviceAgent.publisher.publish(
72
58
  getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
73
59
  JSON.stringify(
74
- buildUpdateProjectShadowMessage({
60
+ buildUpdateShadowMessage({
75
61
  clientId: 'client',
76
62
  desired: toDesire
77
63
  })
@@ -86,65 +72,3 @@ export const addModelCliLeaf = CliLeaf({
86
72
  await deviceAgent.stop();
87
73
  }
88
74
  });
89
-
90
- export const removeModelCliLeaf = CliLeaf({
91
- name: 'remove-model',
92
- description: 'Remove a model from an alwaysAI app',
93
- namedInputs: {
94
- project: CliStringInput({
95
- description: 'Project ID',
96
- required: true
97
- }),
98
- model: CliStringInput({
99
- description: 'Model ID',
100
- required: true
101
- })
102
- },
103
- async action(_, opts) {
104
- const { project, model } = opts;
105
- // TODO: Replace with handleMessage()
106
- await removeModel({ projectId: project, modelId: model });
107
- }
108
- });
109
-
110
- export const replaceModelsCliLeaf = CliLeaf({
111
- name: 'replace-models',
112
- description: 'Replace all models of an alwaysAI app with new models',
113
- namedInputs: {
114
- project: CliStringInput({
115
- description: 'Project Id',
116
- required: true
117
- }),
118
- models: CliStringArrayInput({
119
- description: 'One or more model IDs',
120
- required: true
121
- })
122
- },
123
- hidden: true,
124
- async action(_, opts) {
125
- const { project, models } = opts;
126
- console.log(
127
- 'This command is deprecated and will be removed in a future release'
128
- );
129
- await replaceModels({ projectId: project, modelIds: models });
130
- }
131
- });
132
-
133
- export const updateModelsCliLeaf = CliLeaf({
134
- name: 'update-models',
135
- description: 'Update all models for an alwaysAI app',
136
- namedInputs: {
137
- project: CliStringInput({
138
- description: 'Project Id',
139
- required: true
140
- })
141
- },
142
- hidden: true,
143
- async action(_, opts) {
144
- const { project } = opts;
145
- console.log(
146
- 'This command is deprecated and will be removed in a future release'
147
- );
148
- await updateModels({ projectId: project });
149
- }
150
- });
@@ -14,10 +14,9 @@ export const getShadowCliLeaf = CliLeaf({
14
14
  async action(_, opts) {
15
15
  const { project } = opts;
16
16
  const deviceAgent = new DeviceAgentCloudConnection();
17
- await deviceAgent.setupHandlers();
17
+ deviceAgent.shadowHandler.getProjectShadowUpdates();
18
18
 
19
- // The Device Agent always gets the latest shadow and updates
20
- // itself. 5 seconds should cover the initial response wait time
19
+ // 5 seconds should cover the response wait time
21
20
  await sleep(5000);
22
21
  while (deviceAgent.isCmdInProgress(project)) {
23
22
  await sleep(1000);
@@ -38,11 +37,13 @@ export const updateShadowCliLeaf = CliLeaf({
38
37
  async action(_, opts) {
39
38
  const { project } = opts;
40
39
  const deviceAgent = new DeviceAgentCloudConnection();
41
- await deviceAgent.setupHandlers();
42
- await deviceAgent.updateProjectShadow(project);
40
+ await deviceAgent.shadowHandler.updateProjectShadow(project);
43
41
 
44
42
  // 5 seconds should cover the response wait time
45
43
  await sleep(5000);
44
+ while (deviceAgent.isCmdInProgress(project)) {
45
+ await sleep(1000);
46
+ }
46
47
  await deviceAgent.stop();
47
48
  }
48
49
  });
@@ -2,6 +2,7 @@ import { CliLeaf, CliStringInput, CliTerseError } from '@alwaysai/alwayscli';
2
2
  import {
3
3
  AppVersionControlMessage,
4
4
  generateTxId,
5
+ getToDeviceTopic,
5
6
  keyMirrors
6
7
  } from '@alwaysai/device-agent-schemas';
7
8
  import { rollbackApp } from '../../application-control';
@@ -50,8 +51,7 @@ export const installAppCliLeaf = CliLeaf({
50
51
  throw new CliTerseError('--release-hash flag is required!');
51
52
  }
52
53
  const deviceAgent = new DeviceAgentCloudConnection();
53
- await deviceAgent.setupHandlers();
54
- const topic = deviceAgent.getToDeviceTopic();
54
+ const topic = getToDeviceTopic(deviceAgent.getClientId());
55
55
  const message: AppVersionControlMessage = {
56
56
  timestamp: '',
57
57
  topic,
@@ -83,8 +83,7 @@ export const uninstallAppCliLeaf = CliLeaf({
83
83
  async action(_, opts) {
84
84
  const { project } = opts;
85
85
  const deviceAgent = new DeviceAgentCloudConnection();
86
- await deviceAgent.setupHandlers();
87
- const topic = deviceAgent.getToDeviceTopic();
86
+ const topic = getToDeviceTopic(deviceAgent.getClientId());
88
87
  const message: AppVersionControlMessage = {
89
88
  timestamp: '',
90
89
  topic,
@@ -0,0 +1,42 @@
1
+ import { CliBranch, CliLeaf, CliOneOfInput } from '@alwaysai/alwayscli';
2
+ import { SYSTEM_IDS } from 'alwaysai/lib/constants';
3
+ import { ALWAYSAI_SYSTEM_ID } from 'alwaysai/lib/environment';
4
+ import { getSystemId, setSystemId } from 'alwaysai/lib/infrastructure';
5
+ import { echo } from 'alwaysai/lib/util';
6
+ import { ALWAYSAI_SHOW_HIDDEN } from '../environment';
7
+
8
+ const show = CliLeaf({
9
+ name: 'show',
10
+ description: `Show your current configuration`,
11
+ action() {
12
+ if (ALWAYSAI_SYSTEM_ID) {
13
+ echo(
14
+ '[WARNING] System ID is overridden by environment variable "ALWAYSAI_SYSTEM_ID"'
15
+ );
16
+ }
17
+ return { systemId: getSystemId() };
18
+ }
19
+ });
20
+
21
+ const set = CliLeaf({
22
+ name: 'set',
23
+ description: 'Set an configuration value',
24
+ namedInputs: {
25
+ systemId: CliOneOfInput({ values: SYSTEM_IDS, required: true })
26
+ },
27
+ action(_, { systemId }) {
28
+ if (ALWAYSAI_SYSTEM_ID) {
29
+ echo(
30
+ 'WARNING] System ID is overridden by environment variable "ALWAYSAI_SYSTEM_ID"'
31
+ );
32
+ }
33
+ return setSystemId(systemId);
34
+ }
35
+ });
36
+
37
+ export const configBranch = CliBranch({
38
+ name: 'config',
39
+ hidden: !ALWAYSAI_SHOW_HIDDEN,
40
+ description: `Show or set configuration values`,
41
+ subcommands: [show, set]
42
+ });
@@ -1,20 +1,25 @@
1
1
  import { CliLeaf } from '@alwaysai/alwayscli';
2
- import { logger } from '../../util/logger';
3
- import { AgentConfigFile } from '../../infrastructure/agent-config';
4
2
  import { DeviceConfigFile } from 'alwaysai/lib/core/device';
5
3
  import {
6
- APP_ROOT,
7
- CREDENTIALS_FILE_PATH,
8
- DEVICE_AGENT_CFG_PATH,
9
- DEVICE_AGENT_DOCKER_COMPOSE_PATH
10
- } from '../../util/directories';
4
+ LOCAL_CERT_AND_KEY_DIR,
5
+ LocalLegacyDeviceCertificates
6
+ } from 'alwaysai/lib/infrastructure';
7
+ import { stringifyError } from 'alwaysai/lib/util';
8
+ import { readdir } from 'fs/promises';
9
+ import { uninstallApp } from '../../application-control';
10
+ import { AgentConfigFile } from '../../infrastructure/agent-config';
11
11
  import {
12
12
  checkRabbitMQContainerRunning,
13
13
  stopRabbitMQContainer
14
14
  } from '../../local-connection/rabbitmq-connection';
15
- import { stopApp } from '../../application-control';
16
- import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/paths';
15
+ import {
16
+ APP_ROOT,
17
+ CREDENTIALS_FILE_PATH,
18
+ getDeviceAgentConfigPath,
19
+ getDeviceAgentDockerComposePath
20
+ } from '../../util/directories';
17
21
  import { safeRimraf } from '../../util/file';
22
+ import { logger } from '../../util/logger';
18
23
 
19
24
  export const cleanCliLeaf = CliLeaf({
20
25
  name: 'clean',
@@ -27,23 +32,27 @@ export const cleanCliLeaf = CliLeaf({
27
32
  }
28
33
  } catch (e) {
29
34
  logger.error(
30
- `You may need to manually stop the container by running docker-compose down in the following directory: ${DEVICE_AGENT_DOCKER_COMPOSE_PATH}`
35
+ `You may need to manually stop the container by running docker-compose down in the following directory: ${getDeviceAgentDockerComposePath()}`
36
+ );
37
+ logger.debug(
38
+ `Error in checking / stopping RabbitMQ container!\n${stringifyError(e)}`
31
39
  );
32
- logger.debug(`Error in checking / stopping RabbitMQ container: ${e}`);
33
40
  }
34
41
 
35
- await safeRimraf(DEVICE_AGENT_CFG_PATH);
42
+ await safeRimraf(getDeviceAgentConfigPath());
36
43
 
37
44
  logger.debug('Checking for alwaysAI applications still running');
38
45
  const apps = await AgentConfigFile().getApps();
39
- const stopAppPromises: Promise<void>[] = [];
46
+ const uninstallAppPromises: Promise<void>[] = [];
40
47
  for (const app of apps) {
41
48
  const { projectId } = app;
42
- stopAppPromises.push(stopApp({ projectId }));
49
+ uninstallAppPromises.push(uninstallApp({ projectId }));
43
50
  }
44
- await Promise.all(stopAppPromises);
51
+ await Promise.all(uninstallAppPromises);
45
52
 
46
53
  await safeRimraf(LOCAL_CERT_AND_KEY_DIR);
54
+ const legacyCertificates = new LocalLegacyDeviceCertificates();
55
+ await safeRimraf(legacyCertificates.getCertificateDirectoryPath());
47
56
 
48
57
  logger.debug(`Removing ${AgentConfigFile().path}`);
49
58
  AgentConfigFile().remove();
@@ -51,8 +60,13 @@ export const cleanCliLeaf = CliLeaf({
51
60
  DeviceConfigFile().remove();
52
61
 
53
62
  await safeRimraf(CREDENTIALS_FILE_PATH);
54
- await safeRimraf(APP_ROOT);
55
-
63
+ const files = await readdir(APP_ROOT);
64
+ logger.debug(`${APP_ROOT} folder contents ${JSON.stringify(files.length)}`);
65
+ // This won't handle hidden files, but will remove if truly empty, which is safer
66
+ if (files.length === 0) {
67
+ logger.debug('Applications directory is empty, removing.');
68
+ await safeRimraf(APP_ROOT);
69
+ }
56
70
  logger.info('Device configuration cleaned');
57
71
  }
58
72
  });
@@ -4,6 +4,7 @@ import { cleanCliLeaf } from './clean';
4
4
  import { initCliLeaf } from './init';
5
5
  import { restartCliLeaf } from './restart';
6
6
  import { refreshCliLeaf } from './refresh';
7
+ import { migrateCliLeaf } from './migrate';
7
8
 
8
9
  export const deviceCliBranch = CliBranch({
9
10
  name: 'device',
@@ -13,6 +14,7 @@ export const deviceCliBranch = CliBranch({
13
14
  getInfoCliLeaf,
14
15
  refreshCliLeaf,
15
16
  cleanCliLeaf,
16
- restartCliLeaf
17
+ restartCliLeaf,
18
+ migrateCliLeaf
17
19
  ]
18
20
  });
@@ -1,21 +1,21 @@
1
1
  import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
2
2
  import { getTargetHardwareUuid } from 'alwaysai/lib/core/app';
3
- import { v4 as uuidv4 } from 'uuid';
4
- import { CliAuthenticationClient } from 'alwaysai/lib/infrastructure';
3
+ import {
4
+ CliAuthenticationClient,
5
+ DEVICE_PRIVATE_KEY_FILE_PATH,
6
+ LOCAL_CERT_AND_KEY_DIR
7
+ } from 'alwaysai/lib/infrastructure';
5
8
  import { existsSync } from 'fs';
9
+ import { v4 as uuidv4 } from 'uuid';
10
+ import { AWS_ROOT_CERTIFICATE_FILE_NAME } from '../../util/directories';
6
11
  import { httpClient, microServiceHttpClient } from '../../util/http-client';
7
- import {
8
- AWS_ROOT_CERTIFICATE_FILE_NAME,
9
- BOOTSTRAP_CERTIFICATES_DIR_PATH,
10
- DEVICE_PRIVATE_KEY_FILE_PATH
11
- } from '../../util/directories';
12
12
 
13
+ import { DeviceConfigFile } from 'alwaysai/lib/core/device';
13
14
  import { JsSpawner } from 'alwaysai/lib/util';
15
+ import { getBootstrapCertificateDirectoryPath } from '../../infrastructure/device-certificate';
14
16
  import { writeTokenAndDeviceCfg } from '../../infrastructure/tokens-and-device-cfg';
15
17
  import { logger } from '../../util/logger';
16
- import { DeviceConfigFile } from 'alwaysai/lib/core/device';
17
18
  import { requireLoggedInAndPaidPlan } from '../../util/require-logged-in-and-paid-plan';
18
- import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/paths';
19
19
 
20
20
  export const initCliLeaf = CliLeaf({
21
21
  name: 'init',
@@ -79,14 +79,14 @@ export const initCliLeaf = CliLeaf({
79
79
  deviceUuid: response.deviceUuid
80
80
  });
81
81
 
82
- await JsSpawner().mkdirp(BOOTSTRAP_CERTIFICATES_DIR_PATH());
82
+ await JsSpawner().mkdirp(getBootstrapCertificateDirectoryPath());
83
83
 
84
84
  await JsSpawner({ path: LOCAL_CERT_AND_KEY_DIR }).writeFile(
85
85
  AWS_ROOT_CERTIFICATE_FILE_NAME,
86
86
  ROOT_CERT_AWS
87
87
  );
88
88
  const certSpawner = JsSpawner({
89
- path: BOOTSTRAP_CERTIFICATES_DIR_PATH()
89
+ path: getBootstrapCertificateDirectoryPath()
90
90
  });
91
91
  const writeCertFilePromises: Promise<void>[] = [];
92
92
  for (const key in response.claimCertificate) {
@@ -0,0 +1,20 @@
1
+ import { CliLeaf } from '@alwaysai/alwayscli';
2
+ import { logger } from '../../util/logger';
3
+ import { requiredConfigFilesPresentAndValid } from '../../infrastructure/required-config-checks';
4
+ import { migrateFromLegacyCertsAndTokens } from '../../infrastructure/legacy-migration/legacy-migration';
5
+
6
+ export const migrateCliLeaf = CliLeaf({
7
+ name: 'migrate',
8
+ description: 'Migrate configuration to the latest',
9
+ namedInputs: {},
10
+ async action(_, opts) {
11
+ const filesAlreadyMigrated = await requiredConfigFilesPresentAndValid();
12
+ if (filesAlreadyMigrated) {
13
+ logger.info('Configuration already migrated');
14
+ } else {
15
+ logger.info('Attempting configuration file migration');
16
+ await migrateFromLegacyCertsAndTokens();
17
+ logger.info('Migration complete');
18
+ }
19
+ }
20
+ });
@@ -3,6 +3,7 @@ import { writeTokenAndDeviceCfg } from '../../infrastructure/tokens-and-device-c
3
3
  import { logger } from '../../util/logger';
4
4
  import { DeviceConfigFile } from 'alwaysai/lib/core/device';
5
5
 
6
+ // FIXME: Do we need this command now that tokens aren't used outside of provisioning step?
6
7
  export const refreshCliLeaf = CliLeaf({
7
8
  name: 'refresh',
8
9
  description: 'Refresh the device tokens',
@@ -1,11 +1,13 @@
1
1
  import { appCliBranch } from './app';
2
+ import { configBranch } from './config';
2
3
  import { deviceCliBranch } from './device';
3
- import { loginCliLeaf } from './login';
4
4
  import { getModelPackageCliLeaf } from './get-model-package';
5
+ import { loginCliLeaf } from './login';
5
6
  import { stopRabbitMQContainerCliLeaf } from './rabbitmq-connection';
6
7
 
7
8
  export const subcommands = [
8
9
  loginCliLeaf,
10
+ configBranch,
9
11
  appCliBranch,
10
12
  deviceCliBranch,
11
13
  getModelPackageCliLeaf,
@@ -0,0 +1,20 @@
1
+ import { ErrorObject } from 'ajv';
2
+
3
+ type ErrorOption = ErrorObject<string, Record<string, any>, unknown>;
4
+
5
+ type Details = ErrorOption | ErrorOption[] | Error | null;
6
+
7
+ interface Options {
8
+ cause?: Details;
9
+ }
10
+
11
+ class AaiError extends Error {
12
+ public detailed?: Details;
13
+ constructor(message: string, options?: Options) {
14
+ super(message);
15
+ this.name = 'AaiError';
16
+ this.detailed = options?.cause;
17
+ }
18
+ }
19
+
20
+ export default AaiError;
@@ -1,10 +1,3 @@
1
- import { existsSync } from 'fs';
2
- import {
3
- AWS_ROOT_CERTIFICATE_FILE_PATH,
4
- DEVICE_PRIVATE_KEY_FILE_PATH,
5
- DEVICE_CERTIFICATE_FILE_PATH
6
- } from '../util/directories';
7
-
8
1
  const VALID_AWS_REGIONS = [
9
2
  // 'af-south-1', // Africa (Cape Town)
10
3
  // 'ap-east-1', // Asia Pacific (Hong Kong)
@@ -40,20 +33,3 @@ const VALID_AWS_REGIONS = [
40
33
  export function isValidAwsRegion(region: string): boolean {
41
34
  return VALID_AWS_REGIONS.includes(region);
42
35
  }
43
-
44
- export function cloudModeReady() {
45
- let ready = true;
46
- const requiredFiles = [
47
- DEVICE_CERTIFICATE_FILE_PATH,
48
- DEVICE_PRIVATE_KEY_FILE_PATH,
49
- AWS_ROOT_CERTIFICATE_FILE_PATH
50
- ];
51
-
52
- for (const path of requiredFiles) {
53
- if (!existsSync(path)) {
54
- ready = false;
55
- break;
56
- }
57
- }
58
- return ready;
59
- }
@@ -1,35 +1,15 @@
1
1
  import { logger } from '../util/logger';
2
- import { JsSpawner } from 'alwaysai/lib/util';
2
+ import {
3
+ JsSpawner,
4
+ getLatestVersionFromNpm,
5
+ stringifyError
6
+ } from 'alwaysai/lib/util';
3
7
  import * as path from 'path';
4
- import { get } from 'https';
5
8
 
6
9
  const appPrompt =
7
10
  'Please restart the Device Agent or reboot the device to update. ';
8
11
  const updatePrompt = `A new version of the Device Agent is available! \n\n`;
9
12
 
10
- function getLatestVersion(packageName: string): Promise<string> {
11
- return new Promise<string>((resolve, reject) => {
12
- const url = `https://registry.npmjs.org/${packageName}/latest`;
13
-
14
- get(url, (res) => {
15
- let data = '';
16
- res.on('data', (chunk) => {
17
- data += chunk;
18
- });
19
- res.on('end', () => {
20
- try {
21
- const latestPackage = JSON.parse(data);
22
- resolve(latestPackage.version);
23
- } catch (error) {
24
- reject(error);
25
- }
26
- });
27
- }).on('error', (error) => {
28
- reject(error);
29
- });
30
- });
31
- }
32
-
33
13
  export async function getDeviceAgentVersion(): Promise<string> {
34
14
  let daVersion = '';
35
15
  try {
@@ -39,7 +19,9 @@ export async function getDeviceAgentVersion(): Promise<string> {
39
19
  const parsed = JSON.parse(output);
40
20
  daVersion = parsed.version;
41
21
  } catch (e) {
42
- logger.error(`Could not retrieve the Device Agent version: $ {e}`);
22
+ logger.error(
23
+ `Could not retrieve the Device Agent version!\n${stringifyError(e)}`
24
+ );
43
25
  }
44
26
  return daVersion;
45
27
  }
@@ -52,8 +34,10 @@ function constructPrompt(props: { currentVer: string; latestVer: string }) {
52
34
 
53
35
  export async function checkForUpdatesAndPrompt() {
54
36
  try {
55
- logger.debug('Checking for updates...');
56
- const latestVersion = await getLatestVersion('@alwaysai/device-agent');
37
+ logger.debug('Checking for available Device Agent updates...');
38
+ const latestVersion = await getLatestVersionFromNpm(
39
+ '@alwaysai/device-agent'
40
+ );
57
41
  const version = await getDeviceAgentVersion();
58
42
  if (version !== latestVersion) {
59
43
  logger.warn(
@@ -63,7 +47,7 @@ export async function checkForUpdatesAndPrompt() {
63
47
  })
64
48
  );
65
49
  }
66
- } catch (error) {
67
- logger.error(`Could not check for updates: ${error}`);
50
+ } catch (e) {
51
+ logger.error(`Could not check for updates!\n${stringifyError(e)}`);
68
52
  }
69
53
  }
@@ -1,12 +1,16 @@
1
+ import {
2
+ LOCAL_CERT_AND_KEY_DIR,
3
+ LocalDeviceCertificates
4
+ } from 'alwaysai/lib/infrastructure';
1
5
  import { JsSpawner } from 'alwaysai/lib/util';
6
+ import { getBootstrapCertificateDirectoryPath } from '../infrastructure/device-certificate';
2
7
  import { logger } from '../util/logger';
3
- import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/paths';
4
- import { BOOTSTRAP_CERTIFICATES_DIR_PATH } from '../util/directories';
5
8
 
6
9
  export const rmBootstrapCertsAndClose = async () => {
10
+ const deviceCertificates = new LocalDeviceCertificates();
7
11
  const spawner = JsSpawner();
8
- await spawner.rimraf(BOOTSTRAP_CERTIFICATES_DIR_PATH());
9
- await spawner.rimraf(LOCAL_CERT_AND_KEY_DIR);
12
+ await spawner.rimraf(getBootstrapCertificateDirectoryPath());
13
+ await spawner.rimraf(deviceCertificates.getCertificateDirectoryPath());
10
14
  logger.error('Could not provision device. Try again.');
11
15
  process.exit(1);
12
16
  };
@@ -1,9 +1,12 @@
1
+ import {
2
+ LocalDeviceCertificates,
3
+ getDeviceConfigPath
4
+ } from 'alwaysai/lib/infrastructure';
1
5
  import {
2
6
  AAI_DIR,
3
7
  DEVICE_TOKEN_FILE_NAME,
4
8
  DOCKER_COMPOSE_FILE,
5
- LOCAL_AAI_CFG_DIR,
6
- LOCAL_CERT_AND_KEY_DIR
9
+ LOCAL_AAI_CFG_DIR
7
10
  } from 'alwaysai/lib/paths';
8
11
  import { join } from 'path';
9
12
  import { getSystemId } from '../infrastructure/system-id';
@@ -11,25 +14,28 @@ import { getSystemId } from '../infrastructure/system-id';
11
14
  export const APP_ROOT = join(AAI_DIR, 'applications');
12
15
 
13
16
  export const DEVICE_AGENT_CFG_DIR = 'device-agent';
14
- export const DEVICE_AGENT_CFG_PATH = join(
15
- LOCAL_AAI_CFG_DIR,
16
- DEVICE_AGENT_CFG_DIR
17
- );
18
- export const DEVICE_AGENT_DOCKER_COMPOSE_PATH = join(
19
- DEVICE_AGENT_CFG_PATH,
20
- DOCKER_COMPOSE_FILE
21
- );
17
+
18
+ export function getDeviceAgentConfigPath(baseDir?) {
19
+ if (baseDir) {
20
+ return join(baseDir, getDeviceConfigPath(), DEVICE_AGENT_CFG_DIR);
21
+ }
22
+ return join(LOCAL_AAI_CFG_DIR, getDeviceConfigPath(), DEVICE_AGENT_CFG_DIR);
23
+ }
24
+
25
+ export function getDeviceAgentDockerComposePath(baseDir?) {
26
+ return join(getDeviceAgentConfigPath(baseDir), DOCKER_COMPOSE_FILE);
27
+ }
22
28
 
23
29
  export const CREDENTIALS_FILE_PATH = join(
24
30
  LOCAL_AAI_CFG_DIR,
25
31
  DEVICE_TOKEN_FILE_NAME
26
32
  );
27
33
 
28
- export const AWS_ROOT_CERTIFICATE_FILE_NAME = 'AmazonRootCA1.pem';
29
- export const AWS_ROOT_CERTIFICATE_FILE_PATH = join(
30
- LOCAL_CERT_AND_KEY_DIR,
31
- AWS_ROOT_CERTIFICATE_FILE_NAME
32
- );
34
+ const localDeviceCertificates = new LocalDeviceCertificates();
35
+ export const AWS_ROOT_CERTIFICATE_FILE_PATH =
36
+ localDeviceCertificates.getRootCertificateFilePath();
37
+ export const AWS_ROOT_CERTIFICATE_FILE_NAME =
38
+ localDeviceCertificates.getRootCertificateFileName();
33
39
 
34
40
  export const shortenSystemId = () => {
35
41
  const id = getSystemId();
@@ -40,61 +46,11 @@ export const shortenSystemId = () => {
40
46
  return 'qa';
41
47
  case 'production':
42
48
  return 'prod';
49
+ default:
50
+ return 'prod';
43
51
  }
44
52
  };
45
53
 
46
- /*===================================================================
47
- Bootstrap Certificates
48
- ===================================================================*/
49
-
50
- export const BOOTSTRAP_DIR_NAME = 'bootstrap-certificates';
51
- export const BOOTSTRAP_PRIVATE_KEY_FILE_NAME = () =>
52
- `aai-claim-private-key_${shortenSystemId()}.pem.key`;
53
- export const BOOTSTRAP_CERTIFICATE_FILE_NAME = () =>
54
- `aai-claim-cert_${shortenSystemId()}.pem.crt`;
55
- export const BOOTSTRAP_ID_FILE_NAME = () =>
56
- `aai-claim-cert-id_${shortenSystemId()}.txt`;
57
- export const CERTIFICATE_OWNERSHIP_TOKEN_FILE_NAME =
58
- 'certificate-ownership-token.txt';
59
-
60
- export const BOOTSTRAP_CERTIFICATES_DIR_PATH = () =>
61
- join(LOCAL_CERT_AND_KEY_DIR, BOOTSTRAP_DIR_NAME);
62
-
63
- export const BOOTSTRAP_PRIVATE_KEY_FILE_PATH = () =>
64
- join(
65
- LOCAL_CERT_AND_KEY_DIR,
66
- BOOTSTRAP_DIR_NAME,
67
- BOOTSTRAP_PRIVATE_KEY_FILE_NAME()
68
- );
69
-
70
- export const BOOTSTRAP_CERTIFICATE_FILE_PATH = () =>
71
- join(
72
- LOCAL_CERT_AND_KEY_DIR,
73
- BOOTSTRAP_DIR_NAME,
74
- BOOTSTRAP_CERTIFICATE_FILE_NAME()
75
- );
76
-
77
- export const BOOTSTRAP_ID_FILE_PATH = () =>
78
- join(LOCAL_CERT_AND_KEY_DIR, BOOTSTRAP_DIR_NAME, BOOTSTRAP_ID_FILE_NAME());
79
-
80
- /*===================================================================
81
- Device Certificates
82
- ===================================================================*/
83
-
84
- export const DEVICE_PRIVATE_KEY_FILE_NAME = 'aai-device-private-key.pem.key';
85
- export const DEVICE_CERTIFICATE_FILE_NAME = 'aai-device-cert.pem.crt';
86
- export const DEVICE_ID_FILE_NAME = 'aai-device-cert-id.txt';
87
-
88
- export const DEVICE_PRIVATE_KEY_FILE_PATH = join(
89
- LOCAL_CERT_AND_KEY_DIR,
90
- DEVICE_PRIVATE_KEY_FILE_NAME
91
- );
92
-
93
- export const DEVICE_CERTIFICATE_FILE_PATH = join(
94
- LOCAL_CERT_AND_KEY_DIR,
95
- DEVICE_CERTIFICATE_FILE_NAME
96
- );
97
-
98
54
  /*===================================================================
99
55
  Secure Tunnel bin directory
100
56
  ===================================================================*/