@seliseblocks/cli-os 0.1.4 → 0.2.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 (392) hide show
  1. package/AI_USAGE_GUIDE.md +328 -58
  2. package/README.md +84 -47
  3. package/dist/commands/auth/client-credentials/delete.d.ts +1 -0
  4. package/dist/commands/auth/client-credentials/delete.js +23 -0
  5. package/dist/commands/auth/client-credentials/list.d.ts +1 -0
  6. package/dist/commands/auth/client-credentials/list.js +14 -0
  7. package/dist/commands/auth/client-credentials/save.d.ts +1 -0
  8. package/dist/commands/auth/client-credentials/save.js +41 -0
  9. package/dist/commands/auth/config/get.d.ts +1 -0
  10. package/dist/commands/auth/config/get.js +14 -0
  11. package/dist/commands/auth/config/save.d.ts +1 -0
  12. package/dist/commands/auth/config/save.js +37 -0
  13. package/dist/commands/auth/idp/create.d.ts +1 -0
  14. package/dist/commands/auth/idp/create.js +46 -0
  15. package/dist/commands/auth/idp/delete.d.ts +1 -0
  16. package/dist/commands/auth/idp/delete.js +23 -0
  17. package/dist/commands/auth/idp/get.d.ts +1 -0
  18. package/dist/commands/auth/idp/get.js +16 -0
  19. package/dist/commands/auth/idp/list.d.ts +1 -0
  20. package/dist/commands/auth/idp/list.js +14 -0
  21. package/dist/commands/auth/idp/status.d.ts +1 -0
  22. package/dist/commands/auth/idp/status.js +27 -0
  23. package/dist/commands/auth/idp/update.d.ts +7 -0
  24. package/dist/commands/auth/idp/update.js +45 -0
  25. package/dist/commands/auth/oidc-clients/delete.d.ts +1 -0
  26. package/dist/commands/auth/oidc-clients/delete.js +23 -0
  27. package/dist/commands/auth/oidc-clients/get.d.ts +1 -0
  28. package/dist/commands/auth/oidc-clients/get.js +16 -0
  29. package/dist/commands/auth/oidc-clients/list.d.ts +1 -0
  30. package/dist/commands/auth/oidc-clients/list.js +14 -0
  31. package/dist/commands/auth/oidc-clients/rotate-secret.d.ts +1 -0
  32. package/dist/commands/auth/oidc-clients/rotate-secret.js +23 -0
  33. package/dist/commands/auth/oidc-clients/save.d.ts +2 -0
  34. package/dist/commands/auth/oidc-clients/save.js +57 -0
  35. package/dist/commands/auth/refresh.js +1 -1
  36. package/dist/commands/data/config/create.d.ts +1 -0
  37. package/dist/commands/data/config/create.js +38 -0
  38. package/dist/commands/data/config/get.d.ts +1 -0
  39. package/dist/commands/data/config/get.js +14 -0
  40. package/dist/commands/data/config/update.d.ts +1 -0
  41. package/dist/commands/data/config/update.js +41 -0
  42. package/dist/commands/data/files/create-folder.d.ts +1 -0
  43. package/dist/commands/data/files/create-folder.js +34 -0
  44. package/dist/commands/data/files/delete-folder.d.ts +1 -0
  45. package/dist/commands/data/files/delete-folder.js +25 -0
  46. package/dist/commands/data/files/delete.d.ts +1 -0
  47. package/dist/commands/data/files/delete.js +28 -0
  48. package/dist/commands/data/files/dms-list.d.ts +1 -0
  49. package/dist/commands/data/files/dms-list.js +27 -0
  50. package/dist/commands/data/files/dms-upload.d.ts +6 -0
  51. package/dist/commands/data/files/dms-upload.js +41 -0
  52. package/dist/commands/data/files/get-many.d.ts +1 -0
  53. package/dist/commands/data/files/get-many.js +24 -0
  54. package/dist/commands/data/files/get.d.ts +1 -0
  55. package/dist/commands/data/files/get.js +21 -0
  56. package/dist/commands/data/files/info.d.ts +1 -0
  57. package/dist/commands/data/files/info.js +30 -0
  58. package/dist/commands/data/files/presigned-upload-url.d.ts +6 -0
  59. package/dist/commands/data/files/presigned-upload-url.js +34 -0
  60. package/dist/commands/data/files/update-additional-info.d.ts +1 -0
  61. package/dist/commands/data/files/update-additional-info.js +26 -0
  62. package/dist/commands/data/files/upload-to-local-storage.d.ts +5 -0
  63. package/dist/commands/data/files/upload-to-local-storage.js +55 -0
  64. package/dist/commands/data/files/upload-to-url.d.ts +8 -0
  65. package/dist/commands/data/files/upload-to-url.js +36 -0
  66. package/dist/commands/data/files/upload.d.ts +7 -0
  67. package/dist/commands/data/files/upload.js +145 -0
  68. package/dist/commands/data/rules/policy/delete.d.ts +1 -0
  69. package/dist/commands/data/rules/policy/delete.js +24 -0
  70. package/dist/commands/data/rules/policy/get.d.ts +1 -0
  71. package/dist/commands/data/rules/policy/get.js +17 -0
  72. package/dist/commands/data/schema/aggregation.d.ts +1 -0
  73. package/dist/commands/data/schema/aggregation.js +26 -0
  74. package/dist/commands/data/schema/change-logs.d.ts +1 -0
  75. package/dist/commands/data/schema/change-logs.js +14 -0
  76. package/dist/commands/data/schema/delete.d.ts +1 -0
  77. package/dist/commands/data/schema/delete.js +24 -0
  78. package/dist/commands/data/schema/fields.d.ts +5 -0
  79. package/dist/commands/data/schema/fields.js +39 -0
  80. package/dist/commands/data/schema/get-by-name.d.ts +1 -0
  81. package/dist/commands/data/schema/get-by-name.js +17 -0
  82. package/dist/commands/data/schema/get.d.ts +1 -0
  83. package/dist/commands/data/schema/get.js +17 -0
  84. package/dist/commands/data/schema/info/list.d.ts +1 -0
  85. package/dist/commands/data/schema/info/list.js +15 -0
  86. package/dist/commands/data/schema/info/save.d.ts +1 -0
  87. package/dist/commands/data/schema/info/save.js +34 -0
  88. package/dist/commands/data/schema/info/update.d.ts +1 -0
  89. package/dist/commands/data/schema/info/update.js +35 -0
  90. package/dist/commands/data/sync.d.ts +7 -0
  91. package/dist/commands/data/sync.js +46 -0
  92. package/dist/commands/data/validation/by-schema-field.d.ts +1 -0
  93. package/dist/commands/data/validation/by-schema-field.js +18 -0
  94. package/dist/commands/data/validation/by-schema.d.ts +1 -0
  95. package/dist/commands/data/validation/by-schema.js +17 -0
  96. package/dist/commands/data/validation/delete.d.ts +1 -0
  97. package/dist/commands/data/validation/delete.js +24 -0
  98. package/dist/commands/data/validation/get.d.ts +1 -0
  99. package/dist/commands/data/validation/get.js +17 -0
  100. package/dist/commands/data/validation/list.d.ts +1 -0
  101. package/dist/commands/data/validation/list.js +25 -0
  102. package/dist/commands/data/validation/save.d.ts +6 -0
  103. package/dist/commands/data/validation/save.js +44 -0
  104. package/dist/commands/deselect.js +12 -22
  105. package/dist/commands/iam/email/available.d.ts +1 -0
  106. package/dist/commands/iam/email/available.js +17 -0
  107. package/dist/commands/iam/me.js +4 -1
  108. package/dist/commands/iam/organizations/config-get.d.ts +1 -0
  109. package/dist/commands/iam/organizations/config-get.js +14 -0
  110. package/dist/commands/iam/organizations/config-save.d.ts +1 -0
  111. package/dist/commands/iam/organizations/config-save.js +34 -0
  112. package/dist/commands/iam/organizations/create.d.ts +1 -0
  113. package/dist/commands/iam/organizations/create.js +37 -0
  114. package/dist/commands/iam/organizations/get.d.ts +1 -0
  115. package/dist/commands/iam/organizations/get.js +16 -0
  116. package/dist/commands/iam/organizations/list.d.ts +1 -0
  117. package/dist/commands/iam/organizations/list.js +26 -0
  118. package/dist/commands/iam/organizations/my.d.ts +1 -0
  119. package/dist/commands/iam/organizations/my.js +14 -0
  120. package/dist/commands/iam/organizations/update.d.ts +1 -0
  121. package/dist/commands/iam/organizations/update.js +39 -0
  122. package/dist/commands/iam/permissions/by-severity.d.ts +1 -0
  123. package/dist/commands/iam/permissions/by-severity.js +14 -0
  124. package/dist/commands/iam/permissions/create.d.ts +1 -0
  125. package/dist/commands/iam/permissions/create.js +39 -0
  126. package/dist/commands/iam/permissions/get.d.ts +1 -0
  127. package/dist/commands/iam/permissions/get.js +16 -0
  128. package/dist/commands/iam/permissions/list.d.ts +1 -0
  129. package/dist/commands/iam/permissions/list.js +41 -0
  130. package/dist/commands/iam/permissions/update.d.ts +1 -0
  131. package/dist/commands/iam/permissions/update.js +40 -0
  132. package/dist/commands/iam/resources/features.d.ts +1 -0
  133. package/dist/commands/iam/resources/features.js +19 -0
  134. package/dist/commands/iam/resources/groups.d.ts +1 -0
  135. package/dist/commands/iam/resources/groups.js +14 -0
  136. package/dist/commands/iam/roles/assign-permissions.d.ts +1 -0
  137. package/dist/commands/iam/roles/assign-permissions.js +33 -0
  138. package/dist/commands/iam/roles/assignable.d.ts +1 -0
  139. package/dist/commands/iam/roles/assignable.js +14 -0
  140. package/dist/commands/iam/roles/create.d.ts +1 -0
  141. package/dist/commands/iam/roles/create.js +35 -0
  142. package/dist/commands/iam/roles/get.d.ts +1 -0
  143. package/dist/commands/iam/roles/get.js +16 -0
  144. package/dist/commands/iam/roles/list.d.ts +1 -0
  145. package/dist/commands/iam/roles/list.js +34 -0
  146. package/dist/commands/iam/roles/update.d.ts +1 -0
  147. package/dist/commands/iam/roles/update.js +35 -0
  148. package/dist/commands/iam/signup-settings/get.d.ts +1 -0
  149. package/dist/commands/iam/signup-settings/get.js +14 -0
  150. package/dist/commands/iam/signup-settings/save.d.ts +1 -0
  151. package/dist/commands/iam/signup-settings/save.js +32 -0
  152. package/dist/commands/iam/users/access-grant.d.ts +1 -0
  153. package/dist/commands/iam/users/access-grant.js +32 -0
  154. package/dist/commands/iam/users/access-revoke.d.ts +1 -0
  155. package/dist/commands/iam/users/access-revoke.js +24 -0
  156. package/dist/commands/iam/users/activate.d.ts +1 -0
  157. package/dist/commands/iam/users/activate.js +24 -0
  158. package/dist/commands/iam/users/create.d.ts +1 -0
  159. package/dist/commands/iam/users/create.js +39 -0
  160. package/dist/commands/iam/users/deactivate.d.ts +1 -0
  161. package/dist/commands/iam/users/deactivate.js +24 -0
  162. package/dist/commands/iam/users/exists.d.ts +1 -0
  163. package/dist/commands/iam/users/exists.js +17 -0
  164. package/dist/commands/iam/users/get.d.ts +1 -0
  165. package/dist/commands/iam/users/get.js +17 -0
  166. package/dist/commands/iam/users/list.d.ts +1 -0
  167. package/dist/commands/iam/users/list.js +34 -0
  168. package/dist/commands/iam/users/update.d.ts +1 -0
  169. package/dist/commands/iam/users/update.js +35 -0
  170. package/dist/commands/init.js +0 -12
  171. package/dist/commands/localization/assistant/translation-suggestion.d.ts +1 -0
  172. package/dist/commands/localization/assistant/translation-suggestion.js +35 -0
  173. package/dist/commands/localization/config/get-webhook.d.ts +1 -0
  174. package/dist/commands/localization/config/get-webhook.js +14 -0
  175. package/dist/commands/localization/config/save-webhook.d.ts +1 -0
  176. package/dist/commands/localization/config/save-webhook.js +47 -0
  177. package/dist/commands/localization/glossary/delete.d.ts +1 -0
  178. package/dist/commands/localization/glossary/delete.js +24 -0
  179. package/dist/commands/localization/glossary/get.d.ts +1 -0
  180. package/dist/commands/localization/glossary/get.js +17 -0
  181. package/dist/commands/localization/glossary/list.d.ts +1 -0
  182. package/dist/commands/localization/glossary/list.js +22 -0
  183. package/dist/commands/localization/glossary/save.d.ts +1 -0
  184. package/dist/commands/localization/glossary/save.js +38 -0
  185. package/dist/commands/localization/glossary/suggested.d.ts +1 -0
  186. package/dist/commands/localization/glossary/suggested.js +20 -0
  187. package/dist/commands/localization/key/delete-keys.d.ts +1 -0
  188. package/dist/commands/localization/key/delete-keys.js +28 -0
  189. package/dist/commands/localization/key/delete.d.ts +1 -0
  190. package/dist/commands/localization/key/delete.js +24 -0
  191. package/dist/commands/localization/key/generate-uilm-file.d.ts +1 -0
  192. package/dist/commands/localization/key/generate-uilm-file.js +29 -0
  193. package/dist/commands/localization/key/get-by-names.d.ts +1 -0
  194. package/dist/commands/localization/key/get-by-names.js +24 -0
  195. package/dist/commands/localization/key/get-language-file-generation-history.d.ts +1 -0
  196. package/dist/commands/localization/key/get-language-file-generation-history.js +19 -0
  197. package/dist/commands/localization/key/get-localization-timeline.d.ts +1 -0
  198. package/dist/commands/localization/key/get-localization-timeline.js +28 -0
  199. package/dist/commands/localization/key/get-timeline-by-operation-id.d.ts +1 -0
  200. package/dist/commands/localization/key/get-timeline-by-operation-id.js +21 -0
  201. package/dist/commands/localization/key/get-timeline.d.ts +1 -0
  202. package/dist/commands/localization/key/get-timeline.js +25 -0
  203. package/dist/commands/localization/key/get-uilm-exported-files.d.ts +1 -0
  204. package/dist/commands/localization/key/get-uilm-exported-files.js +22 -0
  205. package/dist/commands/localization/key/get-uilm-file.d.ts +1 -0
  206. package/dist/commands/localization/key/get-uilm-file.js +18 -0
  207. package/dist/commands/localization/key/get.d.ts +1 -0
  208. package/dist/commands/localization/key/get.js +17 -0
  209. package/dist/commands/localization/key/list.d.ts +1 -0
  210. package/dist/commands/localization/key/list.js +39 -0
  211. package/dist/commands/localization/key/rollback.d.ts +1 -0
  212. package/dist/commands/localization/key/rollback.js +24 -0
  213. package/dist/commands/localization/key/save.d.ts +1 -0
  214. package/dist/commands/localization/key/save.js +44 -0
  215. package/dist/commands/localization/key/translate-all.d.ts +1 -0
  216. package/dist/commands/localization/key/translate-all.js +30 -0
  217. package/dist/commands/localization/key/translate-and-export.d.ts +7 -0
  218. package/dist/commands/localization/key/translate-and-export.js +132 -0
  219. package/dist/commands/localization/key/translate-key.d.ts +1 -0
  220. package/dist/commands/localization/key/translate-key.js +27 -0
  221. package/dist/commands/localization/key/translate-keys.d.ts +1 -0
  222. package/dist/commands/localization/key/translate-keys.js +31 -0
  223. package/dist/commands/localization/key/uilm-export.d.ts +1 -0
  224. package/dist/commands/localization/key/uilm-export.js +33 -0
  225. package/dist/commands/localization/key/uilm-import.d.ts +1 -0
  226. package/dist/commands/localization/key/uilm-import.js +26 -0
  227. package/dist/commands/localization/language/delete.d.ts +1 -0
  228. package/dist/commands/localization/language/delete.js +24 -0
  229. package/dist/commands/localization/language/list-for-tenant.d.ts +1 -0
  230. package/dist/commands/localization/language/list-for-tenant.js +14 -0
  231. package/dist/commands/localization/language/list.d.ts +1 -0
  232. package/dist/commands/localization/language/list.js +14 -0
  233. package/dist/commands/localization/language/save.d.ts +1 -0
  234. package/dist/commands/localization/language/save.js +36 -0
  235. package/dist/commands/localization/language/set-default.d.ts +1 -0
  236. package/dist/commands/localization/language/set-default.js +24 -0
  237. package/dist/commands/localization/module/list-for-tenant.d.ts +1 -0
  238. package/dist/commands/localization/module/list-for-tenant.js +14 -0
  239. package/dist/commands/localization/module/list.d.ts +1 -0
  240. package/dist/commands/localization/module/list.js +14 -0
  241. package/dist/commands/localization/module/save.d.ts +1 -0
  242. package/dist/commands/localization/module/save.js +32 -0
  243. package/dist/commands/localization/module/tag-glossary.d.ts +1 -0
  244. package/dist/commands/localization/module/tag-glossary.js +28 -0
  245. package/dist/commands/login.js +32 -1
  246. package/dist/commands/mail/config/delete.d.ts +1 -0
  247. package/dist/commands/mail/config/delete.js +24 -0
  248. package/dist/commands/mail/config/duplicate.d.ts +1 -0
  249. package/dist/commands/mail/config/duplicate.js +24 -0
  250. package/dist/commands/mail/config/get.d.ts +1 -0
  251. package/dist/commands/mail/config/get.js +17 -0
  252. package/dist/commands/mail/config/list.d.ts +1 -0
  253. package/dist/commands/mail/config/list.js +14 -0
  254. package/dist/commands/mail/config/save.d.ts +1 -0
  255. package/dist/commands/mail/config/save.js +44 -0
  256. package/dist/commands/mail/mailbox/get.d.ts +1 -0
  257. package/dist/commands/mail/mailbox/get.js +17 -0
  258. package/dist/commands/mail/mailbox/list.d.ts +1 -0
  259. package/dist/commands/mail/mailbox/list.js +24 -0
  260. package/dist/commands/mail/send.d.ts +1 -0
  261. package/dist/commands/mail/send.js +40 -0
  262. package/dist/commands/mail/sendtoany.d.ts +1 -0
  263. package/dist/commands/mail/sendtoany.js +41 -0
  264. package/dist/commands/mail/template/clone.d.ts +1 -0
  265. package/dist/commands/mail/template/clone.js +33 -0
  266. package/dist/commands/mail/template/delete.d.ts +1 -0
  267. package/dist/commands/mail/template/delete.js +24 -0
  268. package/dist/commands/mail/template/get.d.ts +1 -0
  269. package/dist/commands/mail/template/get.js +17 -0
  270. package/dist/commands/mail/template/list.d.ts +1 -0
  271. package/dist/commands/mail/template/list.js +24 -0
  272. package/dist/commands/mail/template/save.d.ts +1 -0
  273. package/dist/commands/mail/template/save.js +37 -0
  274. package/dist/commands/mfa/backup-codes/generate.d.ts +1 -0
  275. package/dist/commands/mfa/backup-codes/generate.js +22 -0
  276. package/dist/commands/mfa/backup-codes/list.d.ts +1 -0
  277. package/dist/commands/mfa/backup-codes/list.js +14 -0
  278. package/dist/commands/mfa/backup-codes/use.d.ts +1 -0
  279. package/dist/commands/mfa/backup-codes/use.js +18 -0
  280. package/dist/commands/mfa/config-get.d.ts +1 -0
  281. package/dist/commands/mfa/config-get.js +14 -0
  282. package/dist/commands/mfa/config-save.d.ts +1 -0
  283. package/dist/commands/mfa/config-save.js +36 -0
  284. package/dist/commands/mfa/disable.d.ts +1 -0
  285. package/dist/commands/mfa/disable.js +22 -0
  286. package/dist/commands/mfa/generate.d.ts +1 -0
  287. package/dist/commands/mfa/generate.js +22 -0
  288. package/dist/commands/mfa/method-set.d.ts +1 -0
  289. package/dist/commands/mfa/method-set.js +20 -0
  290. package/dist/commands/mfa/resend.d.ts +1 -0
  291. package/dist/commands/mfa/resend.js +20 -0
  292. package/dist/commands/mfa/totp-enable.d.ts +10 -0
  293. package/dist/commands/mfa/totp-enable.js +52 -0
  294. package/dist/commands/mfa/totp-setup.d.ts +1 -0
  295. package/dist/commands/mfa/totp-setup.js +15 -0
  296. package/dist/commands/mfa/totp-verify-setup.d.ts +1 -0
  297. package/dist/commands/mfa/totp-verify-setup.js +17 -0
  298. package/dist/commands/mfa/verify.d.ts +1 -0
  299. package/dist/commands/mfa/verify.js +26 -0
  300. package/dist/commands/new/web.js +115 -15
  301. package/dist/commands/notification/delete.d.ts +1 -0
  302. package/dist/commands/notification/delete.js +24 -0
  303. package/dist/commands/notification/get.d.ts +1 -0
  304. package/dist/commands/notification/get.js +17 -0
  305. package/dist/commands/notification/list.d.ts +1 -0
  306. package/dist/commands/notification/list.js +22 -0
  307. package/dist/commands/notification/save.d.ts +1 -0
  308. package/dist/commands/notification/save.js +34 -0
  309. package/dist/commands/notifier/list.d.ts +1 -0
  310. package/dist/commands/notifier/list.js +23 -0
  311. package/dist/commands/notifier/mark-all-read.d.ts +1 -0
  312. package/dist/commands/notifier/mark-all-read.js +23 -0
  313. package/dist/commands/notifier/mark-read.d.ts +1 -0
  314. package/dist/commands/notifier/mark-read.js +24 -0
  315. package/dist/commands/notifier/notify.d.ts +1 -0
  316. package/dist/commands/notifier/notify.js +39 -0
  317. package/dist/commands/notifier/unread.d.ts +1 -0
  318. package/dist/commands/notifier/unread.js +25 -0
  319. package/dist/commands/projects/get.js +16 -14
  320. package/dist/commands/projects/list.js +2 -7
  321. package/dist/commands/release/builds/list.js +21 -3
  322. package/dist/commands/release/deploy.js +121 -30
  323. package/dist/commands/sdk/client.d.ts +1 -0
  324. package/dist/commands/sdk/client.js +99 -0
  325. package/dist/commands/secrets/get.d.ts +1 -0
  326. package/dist/commands/secrets/get.js +21 -0
  327. package/dist/commands/secrets/save.d.ts +1 -0
  328. package/dist/commands/secrets/save.js +51 -0
  329. package/dist/commands/skill/add.d.ts +1 -0
  330. package/dist/commands/skill/add.js +19 -0
  331. package/dist/commands/skill/list.d.ts +1 -0
  332. package/dist/commands/skill/list.js +15 -0
  333. package/dist/commands/skill/show.d.ts +1 -0
  334. package/dist/commands/skill/show.js +15 -0
  335. package/dist/commands/storage/config/delete.d.ts +1 -0
  336. package/dist/commands/storage/config/delete.js +24 -0
  337. package/dist/commands/storage/config/get.d.ts +1 -0
  338. package/dist/commands/storage/config/get.js +17 -0
  339. package/dist/commands/storage/config/list.d.ts +1 -0
  340. package/dist/commands/storage/config/list.js +14 -0
  341. package/dist/commands/storage/config/save.d.ts +1 -0
  342. package/dist/commands/storage/config/save.js +49 -0
  343. package/dist/commands/use.js +18 -3
  344. package/dist/index.js +965 -160
  345. package/dist/lib/api.d.ts +2 -1
  346. package/dist/lib/api.js +64 -20
  347. package/dist/lib/args.d.ts +3 -0
  348. package/dist/lib/args.js +15 -0
  349. package/dist/lib/auth.d.ts +6 -2
  350. package/dist/lib/auth.js +74 -35
  351. package/dist/lib/config.js +7 -7
  352. package/dist/lib/json-flag.d.ts +17 -0
  353. package/dist/lib/json-flag.js +55 -0
  354. package/dist/lib/project-info.d.ts +33 -0
  355. package/dist/lib/project-info.js +43 -0
  356. package/dist/lib/prompt.d.ts +2 -0
  357. package/dist/lib/prompt.js +28 -0
  358. package/dist/lib/scaffold-web/dashboard.js +1 -1
  359. package/dist/lib/scaffold-web/root-files.js +2 -2
  360. package/dist/lib/secret-store.js +7 -7
  361. package/dist/lib/skills.d.ts +17 -0
  362. package/dist/lib/skills.js +69 -0
  363. package/dist/lib/token.d.ts +2 -0
  364. package/dist/lib/token.js +12 -0
  365. package/dist/lib/workspace.d.ts +0 -4
  366. package/dist/lib/workspace.js +1 -2
  367. package/dist/skills/blocks-data-gateway-configuration/SKILL.md +204 -0
  368. package/dist/skills/blocks-data-gateway-crud/SKILL.md +223 -0
  369. package/dist/skills/blocks-data-storage/SKILL.md +161 -0
  370. package/dist/skills/blocks-frontend-local-https/SKILL.md +100 -0
  371. package/dist/skills/blocks-iam-access-control/SKILL.md +49 -0
  372. package/dist/skills/blocks-iam-access-control/flows/feature-gating.md +38 -0
  373. package/dist/skills/blocks-iam-access-control/flows/manage-roles-permissions.md +109 -0
  374. package/dist/skills/blocks-iam-account/SKILL.md +169 -0
  375. package/dist/skills/blocks-iam-mfa/SKILL.md +124 -0
  376. package/dist/skills/blocks-iam-organizations/SKILL.md +43 -0
  377. package/dist/skills/blocks-iam-organizations/flows/admin-mutations.md +89 -0
  378. package/dist/skills/blocks-iam-organizations/flows/read-and-switch.md +57 -0
  379. package/dist/skills/blocks-iam-sso-oidc-configuration/SKILL.md +89 -0
  380. package/dist/skills/blocks-iam-sso-oidc-implementation/SKILL.md +80 -0
  381. package/dist/skills/blocks-iam-users/SKILL.md +131 -0
  382. package/dist/skills/blocks-localization-configuration/SKILL.md +149 -0
  383. package/dist/skills/blocks-localization-implementation/SKILL.md +63 -0
  384. package/dist/skills/blocks-mail/SKILL.md +95 -0
  385. package/dist/skills/blocks-notification/SKILL.md +69 -0
  386. package/dist/skills/blocks-notifier/SKILL.md +107 -0
  387. package/dist/skills/blocks-onboarding/SKILL.md +78 -0
  388. package/dist/skills/blocks-release-deployment/SKILL.md +81 -0
  389. package/dist/skills/blocks-secrets/SKILL.md +81 -0
  390. package/dist/skills/blocks-storage-configuration/SKILL.md +93 -0
  391. package/dist/skills/lint.mjs +168 -0
  392. package/package.json +4 -4
package/dist/lib/api.d.ts CHANGED
@@ -5,8 +5,9 @@ type RequestOptions = {
5
5
  body?: unknown;
6
6
  impersonatedProjectAuth?: boolean;
7
7
  method?: string;
8
+ preferImpersonatedProjectAuth?: boolean;
8
9
  projectTenantId?: string;
9
- query?: Record<string, string | number | boolean | undefined>;
10
+ query?: Record<string, string | number | boolean | string[] | undefined>;
10
11
  };
11
12
  export declare function blocksRequest<T>(path: string, options?: RequestOptions): Promise<T>;
12
13
  export {};
package/dist/lib/api.js CHANGED
@@ -6,31 +6,75 @@ export async function blocksRequest(path, options = {}) {
6
6
  const baseUrl = options.apiUrl ?? profile.apiUrl;
7
7
  const url = buildUrl(baseUrl, path);
8
8
  for (const [key, value] of Object.entries(options.query ?? {})) {
9
- if (value !== undefined)
10
- url.searchParams.set(key, String(value));
9
+ if (value === undefined)
10
+ continue;
11
+ if (Array.isArray(value)) {
12
+ for (const item of value)
13
+ url.searchParams.append(key, item);
14
+ continue;
15
+ }
16
+ url.searchParams.set(key, String(value));
11
17
  }
12
- const headers = {
18
+ const isFormData = options.body instanceof FormData;
19
+ const baseHeaders = {
13
20
  Accept: "application/json"
14
21
  };
15
- if (options.body !== undefined)
16
- headers["Content-Type"] = "application/json";
17
- if (options.accountAuth) {
18
- const account = await getAccountSession(options.accountName);
19
- headers.Authorization = `Bearer ${account.accessToken}`;
20
- headers["x-blocks-key"] = account.accountTenant;
22
+ if (options.body !== undefined && !isFormData)
23
+ baseHeaders["Content-Type"] = "application/json";
24
+ const send = async (forceRefresh) => {
25
+ const headers = { ...baseHeaders };
26
+ if (options.accountAuth) {
27
+ const account = await getAccountSession(options.accountName, { forceRefresh });
28
+ headers.Authorization = `Bearer ${account.accessToken}`;
29
+ headers["x-blocks-key"] = account.accountTenant;
30
+ }
31
+ if (options.impersonatedProjectAuth) {
32
+ const project = await getImpersonatedProjectSession(options.accountName, options.projectTenantId, { forceRefresh });
33
+ headers.Authorization = `Bearer ${project.accessToken}`;
34
+ // The impersonated token is minted and signed by the root tenant's IdP --
35
+ // its JWKS only exists under the root tenant, so signature validation
36
+ // needs x-blocks-key pointed at root, not the target project. The actual
37
+ // tenant-data scoping comes from a claim already inside the validated
38
+ // token, not from this header.
39
+ headers["x-blocks-key"] = project.accountTenant;
40
+ }
41
+ if (options.preferImpersonatedProjectAuth) {
42
+ const tenantId = options.projectTenantId ?? config.selectedProject?.tenantId;
43
+ if (tenantId) {
44
+ const project = await getImpersonatedProjectSession(options.accountName, tenantId, { forceRefresh });
45
+ headers.Authorization = `Bearer ${project.accessToken}`;
46
+ headers["x-blocks-key"] = project.accountTenant;
47
+ }
48
+ else {
49
+ const account = await getAccountSession(options.accountName, { forceRefresh });
50
+ headers.Authorization = `Bearer ${account.accessToken}`;
51
+ headers["x-blocks-key"] = account.accountTenant;
52
+ }
53
+ }
54
+ return fetch(url, {
55
+ body: options.body === undefined ? undefined : isFormData ? options.body : JSON.stringify(options.body),
56
+ headers,
57
+ method: options.method ?? (options.body === undefined ? "GET" : "POST")
58
+ }).catch((error) => {
59
+ throw new Error(`Blocks API request failed for ${url.origin}${url.pathname}: ${error.message}`);
60
+ });
61
+ };
62
+ const method = options.method ?? (options.body === undefined ? "GET" : "POST");
63
+ let response = await send(false);
64
+ if (response.status === 401 && (options.accountAuth || options.impersonatedProjectAuth || options.preferImpersonatedProjectAuth)) {
65
+ // The locally cached expiry said the token was still good, but the server
66
+ // rejected it anyway (early revocation, clock skew, forced logout server-side).
67
+ // Force one refresh-and-retry before giving up -- this is what actually
68
+ // prevents a spurious 're-run blocks login' when the refresh token is still valid.
69
+ response = await send(true);
21
70
  }
22
- if (options.impersonatedProjectAuth) {
23
- const project = await getImpersonatedProjectSession(options.accountName, options.projectTenantId);
24
- headers.Authorization = `Bearer ${project.accessToken}`;
25
- headers["x-blocks-key"] = project.accountTenant;
71
+ else if (response.status === 500 && method === "GET") {
72
+ // Some tenant-scoped read endpoints (mfa config, signup-settings) intermittently
73
+ // 500 with a JWKS/kid lookup failure right after impersonation, then succeed on
74
+ // an immediate identical retry once the signing-key cache catches up. Safe to
75
+ // retry blindly here because GET is idempotent.
76
+ response = await send(false);
26
77
  }
27
- const response = await fetch(url, {
28
- body: options.body === undefined ? undefined : JSON.stringify(options.body),
29
- headers,
30
- method: options.method ?? (options.body === undefined ? "GET" : "POST")
31
- }).catch((error) => {
32
- throw new Error(`Blocks API request failed for ${url.origin}${url.pathname}: ${error.message}`);
33
- });
34
78
  const text = await response.text();
35
79
  const data = parseJson(text);
36
80
  if (!response.ok) {
@@ -9,3 +9,6 @@ export declare function stringFlag(flags: Record<string, string | boolean>, name
9
9
  }): string;
10
10
  export declare function integerFlag(flags: Record<string, string | boolean>, name: string, defaultValue: number): number;
11
11
  export declare function booleanFlag(flags: Record<string, string | boolean>, name: string): boolean;
12
+ export declare function optionalIntegerFlag(flags: Record<string, string | boolean>, name: string): number | undefined;
13
+ /** Unlike `booleanFlag`, distinguishes "not passed" (undefined) from an explicit `--flag=false`. */
14
+ export declare function optionalBooleanFlag(flags: Record<string, string | boolean>, name: string): boolean | undefined;
package/dist/lib/args.js CHANGED
@@ -42,3 +42,18 @@ export function integerFlag(flags, name, defaultValue) {
42
42
  export function booleanFlag(flags, name) {
43
43
  return flags[name] === true || flags[name] === "true";
44
44
  }
45
+ export function optionalIntegerFlag(flags, name) {
46
+ const value = flags[name];
47
+ if (value === undefined || value === true)
48
+ return undefined;
49
+ const parsed = Number(value);
50
+ if (!Number.isInteger(parsed))
51
+ throw new Error(`--${name} must be an integer`);
52
+ return parsed;
53
+ }
54
+ /** Unlike `booleanFlag`, distinguishes "not passed" (undefined) from an explicit `--flag=false`. */
55
+ export function optionalBooleanFlag(flags, name) {
56
+ if (!(name in flags))
57
+ return undefined;
58
+ return booleanFlag(flags, name);
59
+ }
@@ -12,6 +12,9 @@ export type ProjectSession = {
12
12
  accountTenant: string;
13
13
  tenantId: string;
14
14
  };
15
+ export type SessionOptions = {
16
+ forceRefresh?: boolean;
17
+ };
15
18
  export type DeviceAuthorizationResponse = {
16
19
  device_code: string;
17
20
  expires_in: number;
@@ -25,7 +28,8 @@ export type DevicePollingOptions = {
25
28
  };
26
29
  export declare function requestDeviceAuthorization(profile: AccountProfile): Promise<DeviceAuthorizationResponse>;
27
30
  export declare function pollDeviceToken(profile: AccountProfile, device: DeviceAuthorizationResponse, options?: DevicePollingOptions): Promise<TokenResponse>;
28
- export declare function getAccountSession(accountOverride?: string): Promise<AccountSession>;
31
+ export declare function getAccountSession(accountOverride?: string, options?: SessionOptions): Promise<AccountSession>;
29
32
  export declare function selectProject(tenantId: string): Promise<void>;
30
- export declare function getImpersonatedProjectSession(accountOverride?: string, tenantOverride?: string): Promise<ProjectSession>;
33
+ export declare function getImpersonatedProjectSession(accountOverride?: string, tenantOverride?: string, options?: SessionOptions): Promise<ProjectSession>;
34
+ export declare function stopProjectImpersonation(accountOverride?: string, tenantOverride?: string): Promise<void>;
31
35
  export declare function revokeCurrentSession(accountOverride?: string): Promise<void>;
package/dist/lib/auth.js CHANGED
@@ -4,7 +4,6 @@ import { readTokenStore, writeTokenStore } from "./token-store.js";
4
4
  import { getClientSecret } from "./secret-store.js";
5
5
  import { CliActionableError } from "./errors.js";
6
6
  const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
7
- const FALLBACK_IMPERSONATION_CLIENT_ID = "57214b67-aa9c-4307-92ab-a25e35180fac";
8
7
  const MAX_CONSECUTIVE_TRANSIENT_ERRORS = 3;
9
8
  export async function requestDeviceAuthorization(profile) {
10
9
  const rootTenantId = await resolveRootTenantForDevice(profile);
@@ -81,33 +80,33 @@ export async function pollDeviceToken(profile, device, options = {}) {
81
80
  continue;
82
81
  }
83
82
  if (response.error === "access_denied") {
84
- throw new CliActionableError("Device authorization was denied.", "device_login_denied", "blocks-os login");
83
+ throw new CliActionableError("Device authorization was denied.", "device_login_denied", "blocks login");
85
84
  }
86
85
  if (response.error === "expired_token") {
87
- throw new CliActionableError("Device login expired before approval.", "device_login_expired", "blocks-os login");
86
+ throw new CliActionableError("Device login expired before approval.", "device_login_expired", "blocks login");
88
87
  }
89
88
  if (response.error) {
90
- throw new CliActionableError(response.error_description ?? response.error, "device_login_failed", "blocks-os login");
89
+ throw new CliActionableError(response.error_description ?? response.error, "device_login_failed", "blocks login");
91
90
  }
92
91
  return response;
93
92
  }
94
- throw new CliActionableError("Device login expired before approval.", "device_login_expired", "blocks-os login");
93
+ throw new CliActionableError("Device login expired before approval.", "device_login_expired", "blocks login");
95
94
  }
96
95
  function throwIfTooManyTransientErrors(count, cause) {
97
96
  if (count <= MAX_CONSECUTIVE_TRANSIENT_ERRORS)
98
97
  return;
99
98
  const detail = cause instanceof Error ? cause.message : String(cause);
100
- throw new CliActionableError(`Could not reach the identity provider while waiting for device approval (${detail}).`, "device_login_network_error", "Check your network connection and run 'blocks-os login' again.");
99
+ throw new CliActionableError(`Could not reach the identity provider while waiting for device approval (${detail}).`, "device_login_network_error", "Check your network connection and run 'blocks login' again.");
101
100
  }
102
- export async function getAccountSession(accountOverride) {
101
+ export async function getAccountSession(accountOverride, options = {}) {
103
102
  let config = await readConfig();
104
103
  let store = await readTokenStore();
105
104
  const { name, profile } = getAccountProfile(config, accountOverride);
106
105
  const token = store.accounts[name]?.account;
107
106
  if (!token?.accessToken || !token.accountTenant) {
108
- throw new Error(`Account '${name}' is not logged in. Run 'blocks-os login' first.`);
107
+ throw new Error(`Account '${name}' is not logged in. Run 'blocks login' first.`);
109
108
  }
110
- if (!isExpiring(token.expiresAt)) {
109
+ if (!options.forceRefresh && !isExpiring(token.expiresAt)) {
111
110
  return {
112
111
  accessToken: token.accessToken,
113
112
  account: name,
@@ -116,7 +115,7 @@ export async function getAccountSession(accountOverride) {
116
115
  };
117
116
  }
118
117
  if (!token.refreshToken) {
119
- throw new Error(`Account '${name}' token expired and no refresh token is available. Run 'blocks-os login' again.`);
118
+ throw new Error(`Account '${name}' token expired and no refresh token is available. Run 'blocks login' again.`);
120
119
  }
121
120
  const refreshed = await refreshToken(profile.oidcUrl, profile.clientId, token.refreshToken, await getClientSecret(name), profile.rootTenantId ?? token.accountTenant);
122
121
  const next = applyAccountToken(config, store, name, profile.clientId, refreshed);
@@ -142,16 +141,16 @@ export async function selectProject(tenantId) {
142
141
  }
143
142
  });
144
143
  }
145
- export async function getImpersonatedProjectSession(accountOverride, tenantOverride) {
144
+ export async function getImpersonatedProjectSession(accountOverride, tenantOverride, options = {}) {
146
145
  let config = await readConfig();
147
146
  let store = await readTokenStore();
148
147
  const { name, profile } = getAccountProfile(config, accountOverride);
149
148
  const tenantId = tenantOverride ?? config.selectedProject?.tenantId;
150
149
  if (!tenantId) {
151
- throw new Error("No project selected. Run 'blocks-os use <tenantId>' first.");
150
+ throw new Error("No project selected. Run 'blocks use <tenantId>' first.");
152
151
  }
153
152
  const projectToken = store.accounts[name]?.projects?.[tenantId];
154
- if (projectToken?.accessToken && !isExpiring(projectToken.expiresAt)) {
153
+ if (!options.forceRefresh && projectToken?.accessToken && !isExpiring(projectToken.expiresAt)) {
155
154
  return {
156
155
  accessToken: projectToken.accessToken,
157
156
  account: name,
@@ -168,12 +167,12 @@ export async function getImpersonatedProjectSession(accountOverride, tenantOverr
168
167
  await writeTokenStore(store);
169
168
  return projectSessionFromToken(name, tenantId, store.accounts[name].projects[tenantId], store.accounts[name]?.account?.accountTenant ?? profile.rootTenantId);
170
169
  }
171
- const account = await getAccountSession(name);
170
+ const account = await getAccountSession(name, options);
172
171
  config = await readConfig();
173
172
  store = await readTokenStore();
174
173
  const rootRefresh = store.accounts[name]?.account?.refreshToken;
175
174
  if (!rootRefresh) {
176
- throw new Error(`Project impersonation needs a fresh account refresh token. Run 'blocks-os login' again.`);
175
+ throw new Error(`Project impersonation needs a fresh account refresh token. Run 'blocks login' again.`);
177
176
  }
178
177
  const data = await impersonateProject({
179
178
  accessToken: account.accessToken,
@@ -195,6 +194,57 @@ export async function getImpersonatedProjectSession(accountOverride, tenantOverr
195
194
  await writeTokenStore(next.store);
196
195
  return projectSessionFromToken(name, tenantId, next.store.accounts[name].projects[tenantId], account.accountTenant);
197
196
  }
197
+ // Ends the active project impersonation and restores a fresh, refreshable
198
+ // account-level session. The IAM server revokes the account refresh token the
199
+ // moment it's used to start an impersonation (see impersonateProject) and
200
+ // only ever hands back a project-scoped one in exchange -- calling
201
+ // '/impersonation/stop' is the only way to get a new account-level refresh
202
+ // token back. No-ops if no project is selected or nothing was ever
203
+ // impersonated for it.
204
+ export async function stopProjectImpersonation(accountOverride, tenantOverride) {
205
+ const config = await readConfig();
206
+ const { name, profile } = getAccountProfile(config, accountOverride);
207
+ const tenantId = tenantOverride ?? config.selectedProject?.tenantId;
208
+ if (!tenantId)
209
+ return;
210
+ const store = await readTokenStore();
211
+ if (!store.accounts[name]?.projects?.[tenantId]?.refreshToken)
212
+ return;
213
+ // The stop endpoint requires a currently-valid bearer token to authenticate
214
+ // the call, so refresh the project session first if it's expiring.
215
+ const project = await getImpersonatedProjectSession(name, tenantId);
216
+ const beforeStop = await readTokenStore();
217
+ const projectToken = beforeStop.accounts[name]?.projects?.[tenantId];
218
+ if (!projectToken?.refreshToken)
219
+ return;
220
+ const refreshed = await postStopImpersonation(profile.apiUrl, project.accessToken, project.accountTenant, projectToken.refreshToken);
221
+ const latestConfig = await readConfig();
222
+ const latestStore = await readTokenStore();
223
+ const next = applyAccountToken(latestConfig, latestStore, name, profile.clientId, refreshed);
224
+ const { [tenantId]: _removed, ...remainingProjects } = next.store.accounts[name]?.projects ?? {};
225
+ next.store.accounts[name] = {
226
+ ...next.store.accounts[name],
227
+ projects: remainingProjects
228
+ };
229
+ await writeConfig(next.config);
230
+ await writeTokenStore(next.store);
231
+ }
232
+ async function postStopImpersonation(apiUrl, accessToken, accountTenant, refreshToken) {
233
+ const response = await fetch(new URL("/iam/v4/auth/impersonation/stop", apiUrl), {
234
+ body: JSON.stringify({ refresh_token: refreshToken }),
235
+ headers: {
236
+ Accept: "application/json",
237
+ Authorization: `Bearer ${accessToken}`,
238
+ "Content-Type": "application/json",
239
+ "x-blocks-key": accountTenant
240
+ },
241
+ method: "POST"
242
+ });
243
+ const data = parseJson(await response.text());
244
+ if (response.ok && !data.error)
245
+ return data;
246
+ throw new Error(data.error_description ?? data.error ?? `Stop impersonation failed with HTTP ${response.status}`);
247
+ }
198
248
  export async function revokeCurrentSession(accountOverride) {
199
249
  const config = await readConfig();
200
250
  const store = await readTokenStore();
@@ -236,7 +286,7 @@ async function refreshToken(oidcUrl, clientId, refreshToken, clientSecret, rootT
236
286
  // The OIDC server's exact wording for an expired/revoked refresh token
237
287
  // varies (invalid_grant, blank description, etc.) -- don't depend on it.
238
288
  // Always surface a clear, consistent next step instead.
239
- throw new CliActionableError(`Refresh token was rejected by the identity provider (${error.message}).`, "refresh_token_rejected", "blocks-os login");
289
+ throw new CliActionableError(`Refresh token was rejected by the identity provider (${error.message}).`, "refresh_token_rejected", "blocks login");
240
290
  }
241
291
  // fetch() itself failed (DNS, connection refused, TLS, etc.) -- the
242
292
  // refresh token may still be valid, so don't tell the user to log in.
@@ -245,22 +295,9 @@ async function refreshToken(oidcUrl, clientId, refreshToken, clientSecret, rootT
245
295
  }
246
296
  }
247
297
  async function impersonateProject(args) {
248
- const first = await postImpersonate(args, args.clientId);
249
- if (first.ok)
250
- return first.data;
251
- const message = first.data.error_description ?? first.data.error ?? "";
252
- if (args.clientId !== FALLBACK_IMPERSONATION_CLIENT_ID && /invalid_client|client configuration/i.test(message)) {
253
- const fallback = await postImpersonate(args, FALLBACK_IMPERSONATION_CLIENT_ID);
254
- if (fallback.ok)
255
- return fallback.data;
256
- throw new Error(fallback.data.error_description ?? fallback.data.error ?? `Impersonation failed with HTTP ${fallback.status}`);
257
- }
258
- throw new Error(first.data.error_description ?? first.data.error ?? `Impersonation failed with HTTP ${first.status}`);
259
- }
260
- async function postImpersonate(args, clientId) {
261
298
  const response = await fetch(new URL("/iam/v4/auth/impersonate", args.apiUrl), {
262
299
  body: JSON.stringify({
263
- client_id: clientId,
300
+ client_id: args.clientId,
264
301
  refresh_token: args.refreshToken,
265
302
  targeted_tenant_id: args.tenantId
266
303
  }),
@@ -273,11 +310,13 @@ async function postImpersonate(args, clientId) {
273
310
  method: "POST"
274
311
  });
275
312
  const data = parseJson(await response.text());
276
- return {
277
- data,
278
- ok: response.ok && !data.error,
279
- status: response.status
280
- };
313
+ if (response.ok && !data.error)
314
+ return data;
315
+ const message = data.error_description ?? data.error ?? "";
316
+ if (/invalid_client|client configuration/i.test(message)) {
317
+ throw new CliActionableError(`Impersonation failed: the account's OIDC client ('${args.clientId}') is not registered for impersonation (${message}).`, "impersonation_invalid_client", "Check the client_id in 'blocks auth config get', then contact an admin to register it for impersonation.");
318
+ }
319
+ throw new Error(message || `Impersonation failed with HTTP ${response.status}`);
281
320
  }
282
321
  // Thrown only once we have an actual response from the identity provider
283
322
  // that rejects the request (bad grant, bad client, etc.) -- as opposed to
@@ -1,11 +1,11 @@
1
1
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { homedir, platform } from "node:os";
3
3
  import { dirname, join } from "node:path";
4
- const BAD_GATEWAY_OS_URL = "https://api.seliseblocks.com/os/v4";
5
- const DEFAULT_API_URL = "https://os.seliseblocks.com";
4
+ const BAD_GATEWAY_OS_URL = "https://os.seliseblocks.com";
5
+ const DEFAULT_API_URL = "https://api.seliseblocks.com";
6
6
  const DEFAULT_OS_CLIENT_ID = "4a633b13-1108-4fbf-84fd-b196c9dcdee2";
7
7
  const DEFAULT_OIDC_URL = "https://iam.seliseblocks.com";
8
- const DEFAULT_OS_URL = "https://os.seliseblocks.com";
8
+ const DEFAULT_OS_URL = "https://api.seliseblocks.com";
9
9
  const DEFAULT_PROFILE_TIMESTAMP = "2026-01-01T00:00:00.000Z";
10
10
  const DEFAULT_ROOT_TENANT_ID = "d7e5554c758541db8a18694b64ef423d";
11
11
  const DEFAULT_SCOPE = "openid profile offline_access";
@@ -20,16 +20,16 @@ export function defaults() {
20
20
  };
21
21
  }
22
22
  export function configDir() {
23
- const override = nonEmptyEnv("BLOCKS_OS_CONFIG_DIR");
23
+ const override = nonEmptyEnv("BLOCKS_CONFIG_DIR");
24
24
  if (override)
25
25
  return override;
26
26
  if (platform() === "win32") {
27
- return join(nonEmptyEnv("APPDATA") ?? join(homedir(), "AppData", "Roaming"), "seliseblocks", "cli-os");
27
+ return join(nonEmptyEnv("APPDATA") ?? join(homedir(), "AppData", "Roaming"), "seliseblocks", "cli");
28
28
  }
29
29
  if (platform() === "darwin") {
30
- return join(homedir(), "Library", "Application Support", "seliseblocks", "cli-os");
30
+ return join(homedir(), "Library", "Application Support", "seliseblocks", "cli");
31
31
  }
32
- return join(nonEmptyEnv("XDG_CONFIG_HOME") ?? join(homedir(), ".config"), "seliseblocks", "cli-os");
32
+ return join(nonEmptyEnv("XDG_CONFIG_HOME") ?? join(homedir(), ".config"), "seliseblocks", "cli");
33
33
  }
34
34
  export function configPath() {
35
35
  return join(configDir(), "config.json");
@@ -0,0 +1,17 @@
1
+ type Flags = Record<string, string | boolean>;
2
+ /**
3
+ * Reads a JSON object payload from `--body '<json>'` or `--file <path.json>`.
4
+ * Rich IAM/MFA/Auth-admin payloads have far more optional fields than are
5
+ * worth turning into individual flags; this is the escape hatch every
6
+ * create/update command layers convenience flags on top of.
7
+ */
8
+ export declare function jsonBodyFlag(flags: Flags, options?: {
9
+ required?: boolean;
10
+ }): Promise<Record<string, unknown>>;
11
+ /** Splits a comma-separated flag value (e.g. `--roles admin,editor`) into a trimmed string array. */
12
+ export declare function listFlag(flags: Flags, name: string): string[] | undefined;
13
+ /** Parses a single named flag as an arbitrary JSON value (object or array), e.g. `--attachments '["a.pdf"]'`. */
14
+ export declare function jsonFlag(flags: Flags, name: string): unknown;
15
+ /** Drops `undefined` values so unset convenience flags never overwrite a `--body`/`--file` payload's fields. */
16
+ export declare function compact<T extends object>(obj: T): Partial<T>;
17
+ export {};
@@ -0,0 +1,55 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { stringFlag } from "./args.js";
3
+ /**
4
+ * Reads a JSON object payload from `--body '<json>'` or `--file <path.json>`.
5
+ * Rich IAM/MFA/Auth-admin payloads have far more optional fields than are
6
+ * worth turning into individual flags; this is the escape hatch every
7
+ * create/update command layers convenience flags on top of.
8
+ */
9
+ export async function jsonBodyFlag(flags, options = {}) {
10
+ const inline = stringFlag(flags, "body");
11
+ if (inline)
12
+ return parseJsonObject(inline, "--body");
13
+ const file = stringFlag(flags, "file");
14
+ if (file)
15
+ return parseJsonObject(await readFile(file, "utf8"), `--file ${file}`);
16
+ if (options.required)
17
+ throw new Error("Provide --body '<json>' or --file <path.json> with the request payload.");
18
+ return {};
19
+ }
20
+ /** Splits a comma-separated flag value (e.g. `--roles admin,editor`) into a trimmed string array. */
21
+ export function listFlag(flags, name) {
22
+ const value = flags[name];
23
+ if (typeof value !== "string" || !value)
24
+ return undefined;
25
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
26
+ }
27
+ /** Parses a single named flag as an arbitrary JSON value (object or array), e.g. `--attachments '["a.pdf"]'`. */
28
+ export function jsonFlag(flags, name) {
29
+ const raw = stringFlag(flags, name);
30
+ if (!raw)
31
+ return undefined;
32
+ try {
33
+ return JSON.parse(raw);
34
+ }
35
+ catch (error) {
36
+ throw new Error(`Could not parse JSON from --${name}: ${error.message}`);
37
+ }
38
+ }
39
+ /** Drops `undefined` values so unset convenience flags never overwrite a `--body`/`--file` payload's fields. */
40
+ export function compact(obj) {
41
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
42
+ }
43
+ function parseJsonObject(text, source) {
44
+ let value;
45
+ try {
46
+ value = JSON.parse(text);
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Could not parse JSON from ${source}: ${error.message}`);
50
+ }
51
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
52
+ throw new Error(`${source} must be a JSON object.`);
53
+ }
54
+ return value;
55
+ }
@@ -0,0 +1,33 @@
1
+ export type ProjectRecord = {
2
+ applications?: Array<{
3
+ domain?: string;
4
+ }>;
5
+ environment?: string;
6
+ name?: string;
7
+ tenantId?: string;
8
+ } & Record<string, unknown>;
9
+ export type ProjectGroupRecord = {
10
+ name?: string;
11
+ projects?: ProjectRecord[];
12
+ tenantGroupId?: string;
13
+ } & Record<string, unknown>;
14
+ export type TenantAssetResponse = {
15
+ assets?: {
16
+ resources?: Array<{
17
+ link?: string;
18
+ name?: string;
19
+ resourceId?: string;
20
+ }>;
21
+ };
22
+ };
23
+ export declare function listProjectGroups(flags: Record<string, string | boolean>): Promise<ProjectGroupRecord[]>;
24
+ export declare function findProjectByTenantId(tenantId: string, flags: Record<string, string | boolean>): Promise<{
25
+ group: ProjectGroupRecord;
26
+ project: ProjectRecord;
27
+ }>;
28
+ export declare function resolveSelectedProject(flags: Record<string, string | boolean>): Promise<{
29
+ group: ProjectGroupRecord;
30
+ project: ProjectRecord;
31
+ tenantId: string;
32
+ }>;
33
+ export declare function getProjectAssets(tenantGroupId: string, flags: Record<string, string | boolean>): Promise<TenantAssetResponse>;
@@ -0,0 +1,43 @@
1
+ import { blocksRequest } from "./api.js";
2
+ import { requestContext } from "./request-context.js";
3
+ import { selectedProject } from "./workspace.js";
4
+ // Project metadata (Project/Gets, Project/GetAsset) is authorized at the
5
+ // account/tenant-group level, above any single project's own API surface --
6
+ // but the platform's permission check rebuilds to the root tenant while
7
+ // impersonating, and the underlying query filters by user id rather than
8
+ // tenant, so the impersonated project session works here too. Prefer it when
9
+ // a project is selected (avoids an extra account-session refresh mid-project
10
+ // work); fall back to the account token when nothing is selected yet.
11
+ export async function listProjectGroups(flags) {
12
+ return blocksRequest("/os/v4/Project/Gets", {
13
+ preferImpersonatedProjectAuth: true,
14
+ query: { page: 0, pageSize: 100, tenantGroupId: "" },
15
+ ...requestContext(flags)
16
+ });
17
+ }
18
+ export async function findProjectByTenantId(tenantId, flags) {
19
+ const groups = await listProjectGroups(flags);
20
+ for (const group of groups) {
21
+ const project = (group.projects ?? []).find((item) => item.tenantId === tenantId);
22
+ if (project)
23
+ return { group, project };
24
+ }
25
+ throw new Error(`Project '${tenantId}' was not found in Project/Gets.`);
26
+ }
27
+ // Resolves the CLI's currently selected project (blocks.json, --project flag,
28
+ // or 'blocks use') and looks it up via Project/Gets. Throws the same
29
+ // actionable "No project selected" error as selectedProject() when nothing
30
+ // is selected -- callers should not catch that error, letting it surface to
31
+ // the user with the "run blocks use <tenantId>" next step intact.
32
+ export async function resolveSelectedProject(flags) {
33
+ const tenantId = await selectedProject(flags);
34
+ const { group, project } = await findProjectByTenantId(tenantId, flags);
35
+ return { group, project, tenantId };
36
+ }
37
+ export async function getProjectAssets(tenantGroupId, flags) {
38
+ return blocksRequest("/os/v4/Project/GetAsset", {
39
+ preferImpersonatedProjectAuth: true,
40
+ query: { page: 0, pageSize: 100, tenantGroupId },
41
+ ...requestContext(flags)
42
+ });
43
+ }
@@ -0,0 +1,2 @@
1
+ export declare function promptText(message: string): Promise<string>;
2
+ export declare function selectFromList(message: string, options: string[]): Promise<number>;
@@ -0,0 +1,28 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ export async function promptText(message) {
4
+ const rl = createInterface({ input, output });
5
+ try {
6
+ return (await rl.question(message)).trim();
7
+ }
8
+ finally {
9
+ rl.close();
10
+ }
11
+ }
12
+ export async function selectFromList(message, options) {
13
+ const rl = createInterface({ input, output });
14
+ try {
15
+ console.log(message);
16
+ options.forEach((option, index) => console.log(` ${index + 1}) ${option}`));
17
+ while (true) {
18
+ const answer = (await rl.question(`Choose 1-${options.length}: `)).trim();
19
+ const choice = Number(answer);
20
+ if (Number.isInteger(choice) && choice >= 1 && choice <= options.length)
21
+ return choice - 1;
22
+ console.log(`Enter a number between 1 and ${options.length}.`);
23
+ }
24
+ }
25
+ finally {
26
+ rl.close();
27
+ }
28
+ }
@@ -55,7 +55,7 @@ export async function writeDashboard(root) {
55
55
  " </li>",
56
56
  " <li>",
57
57
  " <Languages size={18} />",
58
- " <span>Edit <code>blocks/localization/*.en.json</code>, then run <code>blocks-os localization:push</code> to sync copy to Blocks.</span>",
58
+ " <span>Edit <code>blocks/localization/*.en.json</code>, then run <code>blocks localization push</code> to sync copy to Blocks.</span>",
59
59
  " </li>",
60
60
  " </ul>",
61
61
  " </div>",
@@ -18,7 +18,7 @@ export async function writeRootFiles(root, options) {
18
18
  },
19
19
  dependencies: {
20
20
  "@radix-ui/react-dropdown-menu": "^2.1.24",
21
- "@seliseblocks/client": "^0.1.1",
21
+ "@seliseblocks/client": "^0.1.3",
22
22
  "@tanstack/react-query": "^5.101.4",
23
23
  clsx: "^2.1.1",
24
24
  "lucide-react": "^1.28.0",
@@ -122,7 +122,7 @@ export async function writeRootFiles(root, options) {
122
122
  "- `/login/callback` — completes the hosted IAM callback via `blocksClient.auth.idp.callback()`, then returns to the page you started from.",
123
123
  "- `/` and `/profile` — protected; redirect to `/login` when signed out.",
124
124
  "- Sidebar + topbar shell matching the `@seliseblocks/blocks-kit` look (icon-only rail on narrow screens, avatar dropdown, notifications menu, active-item accent bar).",
125
- "- `blocks/localization/*.en.json` local i18n seed files for AI or human edits. Sync them through `blocks-os localization:validate` and `blocks-os localization:push`; the runtime app reads Localization service data through `blocksClient.localization`.",
125
+ "- `blocks/localization/*.en.json` local i18n seed files for AI or human edits. Sync them through `blocks localization validate` and `blocks localization push`; the runtime app reads Localization service data through `blocksClient.localization`.",
126
126
  "",
127
127
  "IAM's hosted login sets the session as a **Secure, httpOnly** cookie by default -- this app never reads, stores, or refreshes a token itself. \"Signed in\" is determined by calling `blocksClient.auth.userInfo()` (`GET /iam/v4/auth/me`), which the browser's cookie authenticates automatically; this is different from `blocksClient.iam.me()`, the full IAM profile call used on the Profile page. Logging out calls `blocksClient.auth.logout()` so IAM ends the session server-side. A cached bearer token (and `blocksClient.auth.oidc.refreshToken()` to refresh it) is only used if a tenant's OIDC config explicitly returns tokens in the response body instead of a cookie.",
128
128
  ""