@mx-space/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +375 -0
  3. package/ROADMAP.md +42 -0
  4. package/bin/mxs.cjs +40 -0
  5. package/dist/Lexical.dev-DpU35QTu.mjs +10992 -0
  6. package/dist/Lexical.dev-DpU35QTu.mjs.map +1 -0
  7. package/dist/Lexical.node-gYNDt_p9.mjs +247 -0
  8. package/dist/Lexical.node-gYNDt_p9.mjs.map +1 -0
  9. package/dist/Lexical.prod-t4c3bHV3.mjs +5579 -0
  10. package/dist/Lexical.prod-t4c3bHV3.mjs.map +1 -0
  11. package/dist/LexicalClipboard.dev-CdeeWCIq.mjs +496 -0
  12. package/dist/LexicalClipboard.dev-CdeeWCIq.mjs.map +1 -0
  13. package/dist/LexicalClipboard.node-0e9GBL1g.mjs +26 -0
  14. package/dist/LexicalClipboard.node-0e9GBL1g.mjs.map +1 -0
  15. package/dist/LexicalClipboard.prod-CsCWYmV7.mjs +293 -0
  16. package/dist/LexicalClipboard.prod-CsCWYmV7.mjs.map +1 -0
  17. package/dist/LexicalCodeCore.dev-haI2cqH8.mjs +539 -0
  18. package/dist/LexicalCodeCore.dev-haI2cqH8.mjs.map +1 -0
  19. package/dist/LexicalCodeCore.prod-D_k7zuE5.mjs +383 -0
  20. package/dist/LexicalCodeCore.prod-D_k7zuE5.mjs.map +1 -0
  21. package/dist/LexicalDragon.dev-CAggdFh_.mjs +90 -0
  22. package/dist/LexicalDragon.dev-CAggdFh_.mjs.map +1 -0
  23. package/dist/LexicalDragon.node-QMR_wqJg.mjs +15 -0
  24. package/dist/LexicalDragon.node-QMR_wqJg.mjs.map +1 -0
  25. package/dist/LexicalDragon.prod-OssQhwS-.mjs +61 -0
  26. package/dist/LexicalDragon.prod-OssQhwS-.mjs.map +1 -0
  27. package/dist/LexicalExtension.dev-CtDZiMCU.mjs +1665 -0
  28. package/dist/LexicalExtension.dev-CtDZiMCU.mjs.map +1 -0
  29. package/dist/LexicalExtension.node-BM0gh8zu.mjs +51 -0
  30. package/dist/LexicalExtension.node-BM0gh8zu.mjs.map +1 -0
  31. package/dist/LexicalExtension.prod-Ug6emhEO.mjs +971 -0
  32. package/dist/LexicalExtension.prod-Ug6emhEO.mjs.map +1 -0
  33. package/dist/LexicalHeadless.dev-D4yQpl_s.mjs +45 -0
  34. package/dist/LexicalHeadless.dev-D4yQpl_s.mjs.map +1 -0
  35. package/dist/LexicalHeadless.prod-jeN65B3k.mjs +31 -0
  36. package/dist/LexicalHeadless.prod-jeN65B3k.mjs.map +1 -0
  37. package/dist/LexicalHtml.dev-BdU1_ITQ.mjs +686 -0
  38. package/dist/LexicalHtml.dev-BdU1_ITQ.mjs.map +1 -0
  39. package/dist/LexicalHtml.node-DLtAh00E.mjs +26 -0
  40. package/dist/LexicalHtml.node-DLtAh00E.mjs.map +1 -0
  41. package/dist/LexicalHtml.prod-DdLE1HHT.mjs +432 -0
  42. package/dist/LexicalHtml.prod-DdLE1HHT.mjs.map +1 -0
  43. package/dist/LexicalLink.dev-DK87Biuy.mjs +1084 -0
  44. package/dist/LexicalLink.dev-DK87Biuy.mjs.map +1 -0
  45. package/dist/LexicalLink.prod-fssuv6Sf.mjs +717 -0
  46. package/dist/LexicalLink.prod-fssuv6Sf.mjs.map +1 -0
  47. package/dist/LexicalList.dev-BJ4dr_rk.mjs +1352 -0
  48. package/dist/LexicalList.dev-BJ4dr_rk.mjs.map +1 -0
  49. package/dist/LexicalList.prod-jWBbaWfi.mjs +841 -0
  50. package/dist/LexicalList.prod-jWBbaWfi.mjs.map +1 -0
  51. package/dist/LexicalMarkdown.dev-C8e-eh6y.mjs +1207 -0
  52. package/dist/LexicalMarkdown.dev-C8e-eh6y.mjs.map +1 -0
  53. package/dist/LexicalMarkdown.prod-k73tZVb2.mjs +756 -0
  54. package/dist/LexicalMarkdown.prod-k73tZVb2.mjs.map +1 -0
  55. package/dist/LexicalRichText.dev-CrSqq3K-.mjs +645 -0
  56. package/dist/LexicalRichText.dev-CrSqq3K-.mjs.map +1 -0
  57. package/dist/LexicalRichText.node-B7k7wlQz.mjs +106 -0
  58. package/dist/LexicalRichText.node-B7k7wlQz.mjs.map +1 -0
  59. package/dist/LexicalRichText.prod-CqvyxQ9m.mjs +429 -0
  60. package/dist/LexicalRichText.prod-CqvyxQ9m.mjs.map +1 -0
  61. package/dist/LexicalSelection.dev-sNOHKHe_.mjs +725 -0
  62. package/dist/LexicalSelection.dev-sNOHKHe_.mjs.map +1 -0
  63. package/dist/LexicalSelection.node-Di7GwBCZ.mjs +38 -0
  64. package/dist/LexicalSelection.node-Di7GwBCZ.mjs.map +1 -0
  65. package/dist/LexicalSelection.prod-SWllPJop.mjs +388 -0
  66. package/dist/LexicalSelection.prod-SWllPJop.mjs.map +1 -0
  67. package/dist/LexicalTable.dev-CQkLHXJx.mjs +3262 -0
  68. package/dist/LexicalTable.dev-CQkLHXJx.mjs.map +1 -0
  69. package/dist/LexicalTable.prod-BtqCwKSi.mjs +2218 -0
  70. package/dist/LexicalTable.prod-BtqCwKSi.mjs.map +1 -0
  71. package/dist/LexicalUtils.dev-DiUpsXE8.mjs +850 -0
  72. package/dist/LexicalUtils.dev-DiUpsXE8.mjs.map +1 -0
  73. package/dist/LexicalUtils.node-CmK462d1.mjs +66 -0
  74. package/dist/LexicalUtils.node-CmK462d1.mjs.map +1 -0
  75. package/dist/LexicalUtils.prod-bWwV4GAF.mjs +377 -0
  76. package/dist/LexicalUtils.prod-bWwV4GAF.mjs.map +1 -0
  77. package/dist/auth-DIFZzoMm.mjs +211 -0
  78. package/dist/auth-DIFZzoMm.mjs.map +1 -0
  79. package/dist/bin/mxs.d.mts +927 -0
  80. package/dist/bin/mxs.mjs +3249 -0
  81. package/dist/bin/mxs.mjs.map +1 -0
  82. package/dist/chunk-hT5z_Zn9.mjs +35 -0
  83. package/dist/config-store-DIu7x0ao.mjs +121 -0
  84. package/dist/config-store-DIu7x0ao.mjs.map +1 -0
  85. package/dist/create-B2AOQL1e.mjs +24 -0
  86. package/dist/create-B2AOQL1e.mjs.map +1 -0
  87. package/dist/create-CwjGcv2n.mjs +23 -0
  88. package/dist/create-CwjGcv2n.mjs.map +1 -0
  89. package/dist/create-DT1l2hue.mjs +23 -0
  90. package/dist/create-DT1l2hue.mjs.map +1 -0
  91. package/dist/create-DjlRHPXy.mjs +23 -0
  92. package/dist/create-DjlRHPXy.mjs.map +1 -0
  93. package/dist/create-Dz6rJJSS.mjs +19 -0
  94. package/dist/create-Dz6rJJSS.mjs.map +1 -0
  95. package/dist/delete-B4dGrrus.mjs +22 -0
  96. package/dist/delete-B4dGrrus.mjs.map +1 -0
  97. package/dist/delete-BH6ZngTz.mjs +22 -0
  98. package/dist/delete-BH6ZngTz.mjs.map +1 -0
  99. package/dist/delete-BrY5zJ6S.mjs +22 -0
  100. package/dist/delete-BrY5zJ6S.mjs.map +1 -0
  101. package/dist/delete-CaG2d_xt.mjs +31 -0
  102. package/dist/delete-CaG2d_xt.mjs.map +1 -0
  103. package/dist/delete-YzRliRn8.mjs +31 -0
  104. package/dist/delete-YzRliRn8.mjs.map +1 -0
  105. package/dist/document-output-BVB8emLt.mjs +145 -0
  106. package/dist/document-output-BVB8emLt.mjs.map +1 -0
  107. package/dist/edit-Bb0Ze4w6.mjs +79 -0
  108. package/dist/edit-Bb0Ze4w6.mjs.map +1 -0
  109. package/dist/edit-BeFmC3XD.mjs +80 -0
  110. package/dist/edit-BeFmC3XD.mjs.map +1 -0
  111. package/dist/edit-DLDw_ZWX.mjs +86 -0
  112. package/dist/edit-DLDw_ZWX.mjs.map +1 -0
  113. package/dist/edit-z55ofr1S.mjs +40 -0
  114. package/dist/edit-z55ofr1S.mjs.map +1 -0
  115. package/dist/editor-xrF-j3UE.mjs +42 -0
  116. package/dist/editor-xrF-j3UE.mjs.map +1 -0
  117. package/dist/envelope-overlays-DPAlMNW1.mjs +26 -0
  118. package/dist/envelope-overlays-DPAlMNW1.mjs.map +1 -0
  119. package/dist/get-2qjq0wgG.mjs +28 -0
  120. package/dist/get-2qjq0wgG.mjs.map +1 -0
  121. package/dist/get-BGAk1Zmv.mjs +14 -0
  122. package/dist/get-BGAk1Zmv.mjs.map +1 -0
  123. package/dist/get-CRR13lgj.mjs +10 -0
  124. package/dist/get-CRR13lgj.mjs.map +1 -0
  125. package/dist/get-ClkpDBTL.mjs +13 -0
  126. package/dist/get-ClkpDBTL.mjs.map +1 -0
  127. package/dist/get-DJfUJ3sP.mjs +10 -0
  128. package/dist/get-DJfUJ3sP.mjs.map +1 -0
  129. package/dist/get-DiqaQvoS.mjs +14 -0
  130. package/dist/get-DiqaQvoS.mjs.map +1 -0
  131. package/dist/index.d.mts +338 -0
  132. package/dist/index.mjs +10 -0
  133. package/dist/list-BYzhflDg.mjs +14 -0
  134. package/dist/list-BYzhflDg.mjs.map +1 -0
  135. package/dist/list-DY-tPNG-.mjs +10 -0
  136. package/dist/list-DY-tPNG-.mjs.map +1 -0
  137. package/dist/list-DlQSkbqx.mjs +10 -0
  138. package/dist/list-DlQSkbqx.mjs.map +1 -0
  139. package/dist/list-DsrA5wIz.mjs +15 -0
  140. package/dist/list-DsrA5wIz.mjs.map +1 -0
  141. package/dist/list-Fp4nKfQb.mjs +10 -0
  142. package/dist/list-Fp4nKfQb.mjs.map +1 -0
  143. package/dist/list-s_tloiEX.mjs +10 -0
  144. package/dist/list-s_tloiEX.mjs.map +1 -0
  145. package/dist/litexml-codec-CDoRloiR.mjs +13451 -0
  146. package/dist/litexml-codec-CDoRloiR.mjs.map +1 -0
  147. package/dist/login-CLOJ-fTJ.mjs +579 -0
  148. package/dist/login-CLOJ-fTJ.mjs.map +1 -0
  149. package/dist/logout-v8ihxsim.mjs +11 -0
  150. package/dist/logout-v8ihxsim.mjs.map +1 -0
  151. package/dist/onboarding-640IldLz.mjs +888 -0
  152. package/dist/onboarding-640IldLz.mjs.map +1 -0
  153. package/dist/output-BM7aBFwt.mjs +120 -0
  154. package/dist/output-BM7aBFwt.mjs.map +1 -0
  155. package/dist/output-CTnO1lYt.d.mts +16 -0
  156. package/dist/payload-CN11pUto.mjs +547 -0
  157. package/dist/payload-CN11pUto.mjs.map +1 -0
  158. package/dist/publish-DkTYB-eK.mjs +23 -0
  159. package/dist/publish-DkTYB-eK.mjs.map +1 -0
  160. package/dist/publish-pRBfTJfe.mjs +23 -0
  161. package/dist/publish-pRBfTJfe.mjs.map +1 -0
  162. package/dist/resolve-BogJOR7R.mjs +119 -0
  163. package/dist/resolve-BogJOR7R.mjs.map +1 -0
  164. package/dist/resolve-Bp1AfBmM.mjs +26 -0
  165. package/dist/resolve-Bp1AfBmM.mjs.map +1 -0
  166. package/dist/resolve-Bw5Po9yK.mjs +16 -0
  167. package/dist/resolve-Bw5Po9yK.mjs.map +1 -0
  168. package/dist/resolve-CyjMlaoJ.mjs +23 -0
  169. package/dist/resolve-CyjMlaoJ.mjs.map +1 -0
  170. package/dist/resolve-helpers-CahPoJ0Y.mjs +30 -0
  171. package/dist/resolve-helpers-CahPoJ0Y.mjs.map +1 -0
  172. package/dist/set-BEGQz2si.mjs +43 -0
  173. package/dist/set-BEGQz2si.mjs.map +1 -0
  174. package/dist/shared-D0HVBDHI.mjs +39 -0
  175. package/dist/shared-D0HVBDHI.mjs.map +1 -0
  176. package/dist/status-LXrN9fI-.mjs +22 -0
  177. package/dist/status-LXrN9fI-.mjs.map +1 -0
  178. package/dist/update-B9ynFCdZ.mjs +30 -0
  179. package/dist/update-B9ynFCdZ.mjs.map +1 -0
  180. package/dist/update-BMZQPOvR.mjs +28 -0
  181. package/dist/update-BMZQPOvR.mjs.map +1 -0
  182. package/dist/update-BXz0l7Lz.mjs +36 -0
  183. package/dist/update-BXz0l7Lz.mjs.map +1 -0
  184. package/dist/update-Dx6AF8tf.mjs +30 -0
  185. package/dist/update-Dx6AF8tf.mjs.map +1 -0
  186. package/dist/update-q66TLS38.mjs +37 -0
  187. package/dist/update-q66TLS38.mjs.map +1 -0
  188. package/dist/whoami-CTkctOEd.mjs +22 -0
  189. package/dist/whoami-CTkctOEd.mjs.map +1 -0
  190. package/package.json +77 -0
package/LICENSE ADDED
@@ -0,0 +1,33 @@
1
+ ** Mix Space Open Source License**
2
+
3
+ This project is dual-licensed under different terms for different parts of the project. It is important for users and contributors to understand the licensing terms applicable to each part of the project.
4
+
5
+ ### For the `apps/` Directory
6
+
7
+ **License:** GNU Affero General Public License v3.0 (AGPLv3) with Additional Terms (ADDITIONAL_TERMS).
8
+
9
+ **Scope:** This license applies exclusively to all files within the `apps/` directory of this project.
10
+
11
+ **Summary:**
12
+ - The AGPLv3 is a free, copyleft license suitable for software that will be distributed over a network. It allows users to use, modify, and distribute the software and any modifications under the same terms.
13
+ - The "Additional Terms" (ADDITIONAL_TERMS) are specific conditions that are added to the AGPLv3 license. These terms must be reviewed in the accompanying LICENSE file or documentation to understand any additional restrictions or permissions that apply to the software.
14
+
15
+ ### For Other Parts of the Project
16
+
17
+ **License:** MIT License.
18
+
19
+ **Scope:** This license applies to all parts of the project that are not included within the `apps/` directory.
20
+
21
+ **Summary:**
22
+ - The MIT License is a permissive open-source license that allows users considerable freedom. It permits use, copying, modification, merging, publishing, distribution, sublicensing, and/or selling copies of the software.
23
+ - It also protects the authors by including a limitation of liability and a disclaimer of warranty.
24
+
25
+ **General Conditions:**
26
+ - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the above license notices and this permission notice appear in all copies of the software.
27
+ - The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement.
28
+
29
+ **Contribution:**
30
+ - Contributions to this project are accepted under the terms of the same license as the part of the project to which the contribution is made.
31
+
32
+ **Notice:**
33
+ - This summary is not the license itself and is not a substitute for reading the licenses in their entirety. All users and contributors are encouraged to read the full text of the licenses for a comprehensive understanding.
package/README.md ADDED
@@ -0,0 +1,375 @@
1
+ # @mx-space/cli (`mxs`)
2
+
3
+ Command-line interface for managing a deployed mx-core instance. The CLI is designed for single-owner blog operations, script automation, and AI agents that need stable read/write contracts.
4
+
5
+ ## Installation
6
+
7
+ | Method | Command |
8
+ | ------ | ------------------------------ |
9
+ | pnpm | `pnpm add -g @mx-space/cli` |
10
+ | npm | `npm install -g @mx-space/cli` |
11
+
12
+ Node.js 22 or newer is required.
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ mxs auth login
18
+ mxs post create --file ./post.xml
19
+ mxs post update my-slug --title "New title" --state publish
20
+ mxs post get my-slug --output llm
21
+ ```
22
+
23
+ When the CLI cannot resolve an API URL, it starts an interactive onboarding prompt in TTY contexts. Use `MXS_API_URL` or `--api-url` for non-interactive environments.
24
+
25
+ ## Global Flags
26
+
27
+ | Flag | Effect |
28
+ | ----------------- | ----------------------------------------------------------------------------- |
29
+ | `--json` | Emit `{ ok: true, data }` on stdout. Takes precedence over `--output`. |
30
+ | `--output <mode>` | Output mode. Supported: `pretty-json`, `json`, `readable`, `llm`, `envelope`. |
31
+ | `--api-url <url>` | Override the configured mx-core API origin. |
32
+ | `--token <token>` | Override the stored access token. |
33
+ | `--api-key <key>` | Authenticate with an API key through the `x-api-key` header. |
34
+ | `--quiet`, `-q` | Suppress non-error stderr messages. |
35
+ | `--verbose` | Print HTTP method, URL, status, and duration to stderr. |
36
+ | `--dry-run` | Resolve payloads without mutating the server where supported. |
37
+
38
+ `readable`, `llm`, and `envelope` are currently document output modes for `post get`, `note get`, and `page get`. Other commands keep their existing JSON-oriented output.
39
+
40
+ ## Output Modes
41
+
42
+ | Mode | Shape | Primary Use |
43
+ | ------------- | ------------------------------------------------ | --------------------------------------------- |
44
+ | `pretty-json` | Raw response payload formatted with indentation. | Human inspection and existing behavior. |
45
+ | `json` | `{ ok: true, data }` JSON envelope. | Scripts and structured automation. |
46
+ | `readable` | Compact key-value metadata plus readable body. | Human terminal reading. |
47
+ | `llm` | Same stable readable structure as `readable`. | AI-agent context with lower structural noise. |
48
+ | `envelope` | `<mxpost>` or `<mxnote>` LiteXML envelope. | Editable document round trips. |
49
+
50
+ For Lexical documents, `readable`, `llm`, and `envelope` render the body as LiteXML through `@haklex/rich-litexml` instead of exposing Lexical JSON.
51
+
52
+ Example:
53
+
54
+ ```bash
55
+ mxs post get my-slug --output llm
56
+ ```
57
+
58
+ ```text
59
+ post
60
+ id: 123
61
+ title: Example
62
+ slug: example
63
+ state: published
64
+ category: Tech
65
+ tags: cli, ai
66
+
67
+ summary:
68
+ Short summary.
69
+
70
+ content_format: litexml
71
+
72
+ content:
73
+ <p>Hello world.</p>
74
+ ```
75
+
76
+ ## Authentication
77
+
78
+ | Command | Description |
79
+ | ----------------- | ----------------------------------------------------------------------- |
80
+ | `mxs auth login` | Start the OIDC device authorization flow and store credentials. |
81
+ | `mxs auth logout` | Delete stored credentials. |
82
+ | `mxs auth whoami` | Show the server-validated authenticated user and resolved API URL. |
83
+ | `mxs auth status` | Show token presence, expiry, refresh-token availability, and user data. |
84
+
85
+ `auth login` prints the verification URL and user code. In non-JSON interactive mode, it attempts to open the complete verification URL in the browser.
86
+
87
+ The CLI refreshes credentials in two ways. If a stored OAuth `refresh_token`
88
+ exists, it uses the refresh-token grant. Device authorization normally stores a
89
+ Better Auth session token instead; for that path the CLI calls Better Auth
90
+ `/get-session`, accepts the refreshed `set-auth-token` header, and updates the
91
+ local expiry from the refreshed session.
92
+
93
+ ## Posts
94
+
95
+ | Command | Description |
96
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
97
+ | `mxs post list` | List posts. |
98
+ | `mxs post get <slugOrId>` | Read a post by Snowflake id or slug. |
99
+ | `mxs post create` | Create a post. |
100
+ | `mxs post edit <slugOrId>` | Edit a post through `$EDITOR` when no content flags are supplied; otherwise replace through flags/file. |
101
+ | `mxs post update <slugOrId>` | Patch selected post fields. Content is left unchanged unless `--content` or `--file` is supplied. |
102
+ | `mxs post delete <slugOrId>` | Delete a post. Requires `--force` in non-TTY contexts. |
103
+ | `mxs post publish <slugOrId>` | Set `isPublished=true`. |
104
+ | `mxs post unpublish <slugOrId>` | Set `isPublished=false`. |
105
+
106
+ ### Post List Flags
107
+
108
+ | Flag | Effect |
109
+ | ----------------- | --------------------------------------------------------------- |
110
+ | `--page <n>` | Page number. |
111
+ | `--size <n>` | Page size. |
112
+ | `--state <state>` | Publication filter, such as `draft` or `publish`. |
113
+ | `--sort <field>` | Sort field passed as `sortBy`, such as `created` or `modified`. |
114
+
115
+ ### Post Write Flags
116
+
117
+ | Flag | Field |
118
+ | ------------------------- | ---------------------------------------------------------------------- |
119
+ | `--title <text>` | `title` |
120
+ | `--slug <slug>` | `slug` |
121
+ | `--category <id-or-name>` | Resolved to `categoryId` by id, slug, or name. |
122
+ | `--content <spec>` | Body source. See content spec grammar below. |
123
+ | `--format <format>` | `contentFormat`; supported: `lexical`, `markdown`. Default: `lexical`. |
124
+ | `--summary <text>` | `summary` |
125
+ | `--state <state>` | `publish` or `draft`; maps to `isPublished`. |
126
+ | `--tags <csv>` | Comma-separated tags. |
127
+ | `--copyright <bool>` | `copyright`; accepts `true` or `false`. |
128
+ | `--pin <iso>` | Pin timestamp/date. |
129
+ | `--pin-order <n>` | Numeric pin order. |
130
+ | `--related <csv>` | Comma-separated related document ids. |
131
+ | `--meta <spec>` | JSON literal or `file=<path>`. |
132
+ | `--file <path>` | LiteXML envelope. |
133
+
134
+ ## Notes
135
+
136
+ | Command | Description |
137
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
138
+ | `mxs note list` | List notes. |
139
+ | `mxs note get <slugOrId>` | Read a note by Snowflake id, numeric nid, or direct identifier. |
140
+ | `mxs note create` | Create a note. |
141
+ | `mxs note edit <slugOrId>` | Edit a note through `$EDITOR` when no content flags are supplied; otherwise replace through flags/file. |
142
+ | `mxs note update <slugOrId>` | Patch selected note fields. Content is left unchanged unless `--content` or `--file` is supplied. |
143
+ | `mxs note delete <slugOrId>` | Delete a note. Requires `--force` in non-TTY contexts. |
144
+ | `mxs note publish <slugOrId>` | Set `isPublished=true`. |
145
+ | `mxs note unpublish <slugOrId>` | Set `isPublished=false`. |
146
+
147
+ ### Note List Flags
148
+
149
+ | Flag | Effect |
150
+ | ----------------- | ------------------------------ |
151
+ | `--page <n>` | Page number. |
152
+ | `--size <n>` | Page size. |
153
+ | `--state <state>` | Publication filter. |
154
+ | `--sort <field>` | Sort field passed as `sortBy`. |
155
+
156
+ ### Note Write Flags
157
+
158
+ | Flag | Field |
159
+ | ---------------------- | ---------------------------------------------------------------------- |
160
+ | `--title <text>` | `title`; defaults to `无题` for create payloads. |
161
+ | `--slug <slug>` | `slug` |
162
+ | `--topic <id-or-name>` | Resolved to `topicId` by id, slug, or name. |
163
+ | `--content <spec>` | Body source. |
164
+ | `--format <format>` | `contentFormat`; supported: `lexical`, `markdown`. Default: `lexical`. |
165
+ | `--state <state>` | `publish` or `draft`; maps to `isPublished`. |
166
+ | `--mood <text>` | `mood` |
167
+ | `--weather <text>` | `weather` |
168
+ | `--public-at <iso>` | `publicAt` |
169
+ | `--password <text>` | `password` |
170
+ | `--bookmark <bool>` | `bookmark`; accepts `true` or `false`. |
171
+ | `--coords <lat,lng>` | `coordinates` |
172
+ | `--location <text>` | `location` |
173
+ | `--images <spec>` | JSON literal or `file=<path>`. |
174
+ | `--meta <spec>` | JSON literal or `file=<path>`. |
175
+ | `--file <path>` | LiteXML envelope. |
176
+
177
+ ## Pages
178
+
179
+ | Command | Description |
180
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------- |
181
+ | `mxs page list` | List pages. |
182
+ | `mxs page get <slugOrId>` | Read a page by Snowflake id or slug. |
183
+ | `mxs page create` | Create a page. |
184
+ | `mxs page edit <slugOrId>` | Edit a page through `$EDITOR` when no content flags are supplied; otherwise replace through flags/file. |
185
+ | `mxs page update <slugOrId>` | Patch selected page fields. Content is left unchanged unless `--content` or `--file` is supplied. |
186
+ | `mxs page delete <slugOrId>` | Delete a page. Requires `--force` in non-TTY contexts. |
187
+
188
+ ### Page Write Flags
189
+
190
+ | Flag | Field |
191
+ | ------------------- | ---------------------------------------------------------------------- |
192
+ | `--title <text>` | `title` |
193
+ | `--slug <slug>` | `slug` |
194
+ | `--subtitle <text>` | `subtitle` |
195
+ | `--order <n>` | Numeric page order. |
196
+ | `--content <spec>` | Body source. |
197
+ | `--format <format>` | `contentFormat`; supported: `lexical`, `markdown`. Default: `lexical`. |
198
+ | `--meta <spec>` | JSON literal or `file=<path>`. |
199
+ | `--file <path>` | LiteXML envelope. |
200
+
201
+ Page edit and page file payloads currently reuse the `<mxpost>` envelope shape.
202
+
203
+ ## Categories
204
+
205
+ | Command | Description |
206
+ | ------------------------------------------------- | ----------------------------------------------------------------- |
207
+ | `mxs category list` | List categories and tags. |
208
+ | `mxs category get <slugOrId>` | Read a category or tag. |
209
+ | `mxs category create --name <name> --slug <slug>` | Create a category or tag. |
210
+ | `mxs category update <slugOrId>` | Patch category or tag fields. |
211
+ | `mxs category delete <slugOrId>` | Delete a category or tag. Requires `--force` in non-TTY contexts. |
212
+
213
+ ### Category Write Flags
214
+
215
+ | Flag | Field |
216
+ | --------------- | ---------------------------------------------------- |
217
+ | `--name <text>` | `name` |
218
+ | `--slug <slug>` | `slug` |
219
+ | `--type <type>` | `category` or `tag`; maps to server type `0` or `1`. |
220
+ | `--icon <text>` | `icon` |
221
+
222
+ ## Topics
223
+
224
+ | Command | Description |
225
+ | ---------------------------------------------- | ------------------------------------------------------- |
226
+ | `mxs topic list` | List all topics. |
227
+ | `mxs topic get <slugOrId>` | Read a topic by Snowflake id or slug. |
228
+ | `mxs topic create --name <name> --slug <slug>` | Create a topic. |
229
+ | `mxs topic update <slugOrId>` | Patch topic fields. |
230
+ | `mxs topic delete <slugOrId>` | Delete a topic. Requires `--force` in non-TTY contexts. |
231
+
232
+ ### Topic Write Flags
233
+
234
+ | Flag | Field |
235
+ | ---------------------- | ------------- |
236
+ | `--name <text>` | `name` |
237
+ | `--slug <slug>` | `slug` |
238
+ | `--description <text>` | `description` |
239
+ | `--icon <text>` | `icon` |
240
+
241
+ ## Configuration
242
+
243
+ | Command | Description |
244
+ | ------------------------------ | -------------------------------------------------------------- |
245
+ | `mxs config list` | Read all server options from `/options`. |
246
+ | `mxs config get <key>` | Read one server option. |
247
+ | `mxs config set <key> <value>` | Patch one server option. |
248
+ | `mxs config edit` | Open all options in `$EDITOR`, then patch changed JSON values. |
249
+
250
+ ### Config Set Flags
251
+
252
+ | Flag | Effect |
253
+ | --------------- | ---------------------------------------------------------------------- |
254
+ | `--type json` | Parse value as JSON and fail on invalid JSON. |
255
+ | `--type string` | Store the value as a string. |
256
+ | `--type number` | Parse the value as a number. |
257
+ | `--type bool` | Store `true` only when the value is exactly `true`; otherwise `false`. |
258
+
259
+ Without `--type`, `config set` attempts JSON parsing first and falls back to string.
260
+
261
+ ## Content Sources
262
+
263
+ `--content` accepts the following grammar:
264
+
265
+ | Spec | Meaning |
266
+ | ---------------------------- | -------------------------------- |
267
+ | `--content="inline literal"` | Use the argument value directly. |
268
+ | `--content=file=<path>` | Read body content from a file. |
269
+ | `--content=-` | Read body content from stdin. |
270
+ | `--content=stdin` | Read body content from stdin. |
271
+
272
+ `--meta`, `--images`, and other JSON spec fields accept either an inline JSON literal or `file=<path>`.
273
+
274
+ ## LiteXML Envelopes
275
+
276
+ LiteXML envelopes can carry metadata and body content in one file. Flag values override envelope metadata.
277
+ Use `--file -` to read an envelope from stdin.
278
+
279
+ ### Post Envelope
280
+
281
+ ```xml
282
+ <mxpost>
283
+ <meta>
284
+ <title>Title</title>
285
+ <slug>my-post</slug>
286
+ <category>tech</category>
287
+ <tags><tag>cli</tag><tag>ai</tag></tags>
288
+ <state>publish</state>
289
+ <summary>Summary</summary>
290
+ <format>lexical</format>
291
+ </meta>
292
+ <content>
293
+ <p>Body.</p>
294
+ </content>
295
+ </mxpost>
296
+ ```
297
+
298
+ ### Note Envelope
299
+
300
+ ```xml
301
+ <mxnote>
302
+ <meta>
303
+ <title>Daily Note</title>
304
+ <slug>daily-note</slug>
305
+ <topic>life</topic>
306
+ <state>draft</state>
307
+ <mood>calm</mood>
308
+ <weather>clear</weather>
309
+ <format>lexical</format>
310
+ </meta>
311
+ <content>
312
+ <p>Body.</p>
313
+ </content>
314
+ </mxnote>
315
+ ```
316
+
317
+ `lexical` envelope content is parsed through `@haklex/rich-litexml` and converted to Lexical JSON before writing to the server. `markdown` content is sent as-is.
318
+
319
+ ## Configuration Files
320
+
321
+ | File | Mode | Purpose |
322
+ | -------------------------------- | ------ | --------------------------------------------------------------- |
323
+ | `~/.config/mxs/config.json` | `0644` | API URL, API base, auth base, API version, and client id. |
324
+ | `~/.config/mxs/credentials.json` | `0600` | Access token, refresh token, expiry, and optional user profile. |
325
+
326
+ `XDG_CONFIG_HOME` changes the base directory. Credentials with wider permissions are automatically changed to `0600`.
327
+
328
+ Example config:
329
+
330
+ ```json
331
+ {
332
+ "api_url": "https://blog.example.com",
333
+ "api_base": "https://blog.example.com/api/v2",
334
+ "auth_base": "https://blog.example.com/api/v2/auth",
335
+ "api_version": 2,
336
+ "client_id": "mxs-cli"
337
+ }
338
+ ```
339
+
340
+ ## Environment Variables
341
+
342
+ | Variable | Meaning |
343
+ | ----------------- | ------------------------------------------------------------------------ |
344
+ | `MXS_API_URL` | API origin override. |
345
+ | `MXS_TOKEN` | Better Auth access token override; sent as `Authorization: Bearer`. |
346
+ | `MXS_API_KEY` | API key override; sent as `x-api-key`. |
347
+ | `MXS_DEBUG=1` | Enables verbose HTTP diagnostics in auth helpers. |
348
+ | `EDITOR` | Editor used by `post edit`, `note edit`, `page edit`, and `config edit`. |
349
+ | `XDG_CONFIG_HOME` | Base directory for `mxs` config files. |
350
+
351
+ ## Exit Codes
352
+
353
+ | Code | Meaning |
354
+ | ---- | --------------------------------------- |
355
+ | `0` | Success |
356
+ | `1` | Generic failure |
357
+ | `2` | Argument parsing failure |
358
+ | `3` | Authentication or authorization failure |
359
+ | `4` | Network failure |
360
+ | `5` | Validation or configuration failure |
361
+ | `6` | Server 5xx failure |
362
+ | `7` | Resource not found |
363
+
364
+ ## Troubleshooting
365
+
366
+ | Symptom | Resolution |
367
+ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
368
+ | `cannot detect auth endpoint` | Verify that the URL points to a live mx-core server with device authorization enabled. Use `--verbose` to inspect probes. |
369
+ | `API URL is not configured` | Set `MXS_API_URL`, pass `--api-url`, or run `mxs auth login` in an interactive terminal. |
370
+ | `EDITOR is not set` | Set `EDITOR`, for example `EDITOR=vim`. |
371
+ | API key no longer works in `Authorization: Bearer` | Use `--api-key` or `MXS_API_KEY` for API keys. Bearer auth is reserved for Better Auth session/OIDC access tokens. |
372
+
373
+ ## License
374
+
375
+ AGPL-3.0.
package/ROADMAP.md ADDED
@@ -0,0 +1,42 @@
1
+ # `@mx-space/cli` Roadmap
2
+
3
+ ## v2
4
+
5
+ - AI module commands
6
+ - `mxs ai summary regen <id>`
7
+ - `mxs ai translate <id> --to <locale>`
8
+ - `mxs ai insights refresh`
9
+ - `mxs ai tokens`
10
+ - Comment moderation
11
+ - `mxs comment list`
12
+ - `mxs comment approve <id>`
13
+ - `mxs comment reject <id>`
14
+ - `mxs comment delete <id>`
15
+ - Markdown → Lexical bridge (contingent on a haklex reader)
16
+
17
+ ## v3
18
+
19
+ - Maintenance
20
+ - `mxs cache clear [--scope=all|view|post|note|page|aggregate]`
21
+ - `mxs cache stats`
22
+ - `mxs search reindex`
23
+ - `mxs search status`
24
+ - `mxs job list`
25
+ - `mxs job run <name>`
26
+ - `mxs health`
27
+ - Backup
28
+ - `mxs backup create [--output <path>]`
29
+ - `mxs backup list`
30
+ - `mxs backup download <id> [--output <path>]`
31
+ - `mxs backup restore <id-or-file> [--force]`
32
+ - Export / import (content as files)
33
+ - `mxs export <dir> [--type=post,note,page] [--format=markdown|litexml]`
34
+ - `mxs import <dir> [--type=post] [--update-existing]`
35
+ - Multi-profile
36
+ - `mxs use <name>`
37
+ - `~/.config/mxs/profiles.json`
38
+ - Observability
39
+ - `mxs logs tail`
40
+ - `mxs metrics`
41
+ - OS keychain storage (keytar / libsecret) once monorepo Node version stabilizes
42
+ - `mxs init` — bootstrap a fresh mx-core deployment via compose
package/bin/mxs.cjs ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ const { existsSync } = require('node:fs')
3
+ const { join } = require('node:path')
4
+ const { spawnSync } = require('node:child_process')
5
+
6
+ const packageRoot = join(__dirname, '..')
7
+ const productionEntry = join(packageRoot, 'dist', 'bin', 'mxs.mjs')
8
+ const developmentEntry = join(packageRoot, 'src', 'bin', 'mxs.ts')
9
+
10
+ const args = process.argv.slice(2)
11
+
12
+ let result
13
+ if (existsSync(productionEntry)) {
14
+ result = spawnSync(process.execPath, [productionEntry, ...args], {
15
+ stdio: 'inherit',
16
+ })
17
+ } else if (existsSync(developmentEntry)) {
18
+ result = spawnSync(
19
+ process.execPath,
20
+ ['--import', 'tsx', developmentEntry, ...args],
21
+ {
22
+ cwd: packageRoot,
23
+ stdio: 'inherit',
24
+ },
25
+ )
26
+ } else {
27
+ console.error('mxs entry not found; run `pnpm -C packages/cli run build`.')
28
+ process.exit(1)
29
+ }
30
+
31
+ if (result.error) {
32
+ console.error(result.error.message)
33
+ process.exit(1)
34
+ }
35
+
36
+ if (result.signal) {
37
+ process.exit(1)
38
+ }
39
+
40
+ process.exit(result.status ?? 0)