rowork 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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/bin/rowork.js +18 -0
  4. package/dashboard/app.css +234 -0
  5. package/dashboard/app.js +117 -0
  6. package/dashboard/index.html +67 -0
  7. package/dist/cli/errors.d.ts +15 -0
  8. package/dist/cli/errors.js +18 -0
  9. package/dist/cli/program.d.ts +19 -0
  10. package/dist/cli/program.js +59 -0
  11. package/dist/cli/registry.d.ts +37 -0
  12. package/dist/cli/registry.js +61 -0
  13. package/dist/commands/add.d.ts +8 -0
  14. package/dist/commands/add.js +77 -0
  15. package/dist/commands/assets-setup.d.ts +18 -0
  16. package/dist/commands/assets-setup.js +136 -0
  17. package/dist/commands/assets.d.ts +2 -0
  18. package/dist/commands/assets.js +153 -0
  19. package/dist/commands/console.d.ts +4 -0
  20. package/dist/commands/console.js +141 -0
  21. package/dist/commands/dashboard.d.ts +2 -0
  22. package/dist/commands/dashboard.js +50 -0
  23. package/dist/commands/dev-background.d.ts +3 -0
  24. package/dist/commands/dev-background.js +87 -0
  25. package/dist/commands/dev.d.ts +2 -0
  26. package/dist/commands/dev.js +270 -0
  27. package/dist/commands/eject.d.ts +2 -0
  28. package/dist/commands/eject.js +136 -0
  29. package/dist/commands/index.d.ts +8 -0
  30. package/dist/commands/index.js +54 -0
  31. package/dist/commands/info.d.ts +3 -0
  32. package/dist/commands/info.js +50 -0
  33. package/dist/commands/init.d.ts +2 -0
  34. package/dist/commands/init.js +55 -0
  35. package/dist/commands/links.d.ts +32 -0
  36. package/dist/commands/links.js +152 -0
  37. package/dist/commands/make-event.d.ts +2 -0
  38. package/dist/commands/make-event.js +160 -0
  39. package/dist/commands/make-menu.d.ts +3 -0
  40. package/dist/commands/make-menu.js +36 -0
  41. package/dist/commands/make-stat.d.ts +2 -0
  42. package/dist/commands/make-stat.js +199 -0
  43. package/dist/commands/make-tool.d.ts +2 -0
  44. package/dist/commands/make-tool.js +124 -0
  45. package/dist/commands/make-ui.d.ts +3 -0
  46. package/dist/commands/make-ui.js +199 -0
  47. package/dist/commands/make.d.ts +21 -0
  48. package/dist/commands/make.js +181 -0
  49. package/dist/commands/next-steps.d.ts +5 -0
  50. package/dist/commands/next-steps.js +18 -0
  51. package/dist/commands/start.d.ts +2 -0
  52. package/dist/commands/start.js +144 -0
  53. package/dist/commands/studio.d.ts +3 -0
  54. package/dist/commands/studio.js +45 -0
  55. package/dist/commands/update.d.ts +2 -0
  56. package/dist/commands/update.js +173 -0
  57. package/dist/commands/wire.d.ts +2 -0
  58. package/dist/commands/wire.js +34 -0
  59. package/dist/core/agent-docs.d.ts +23 -0
  60. package/dist/core/agent-docs.js +151 -0
  61. package/dist/core/assets.d.ts +96 -0
  62. package/dist/core/assets.js +268 -0
  63. package/dist/core/background.d.ts +47 -0
  64. package/dist/core/background.js +149 -0
  65. package/dist/core/config.d.ts +9 -0
  66. package/dist/core/config.js +62 -0
  67. package/dist/core/exec.d.ts +22 -0
  68. package/dist/core/exec.js +38 -0
  69. package/dist/core/format-generated.d.ts +12 -0
  70. package/dist/core/format-generated.js +28 -0
  71. package/dist/core/generate.d.ts +37 -0
  72. package/dist/core/generate.js +94 -0
  73. package/dist/core/github-release.d.ts +28 -0
  74. package/dist/core/github-release.js +38 -0
  75. package/dist/core/info.d.ts +77 -0
  76. package/dist/core/info.js +65 -0
  77. package/dist/core/modules.d.ts +32 -0
  78. package/dist/core/modules.js +265 -0
  79. package/dist/core/naming.d.ts +6 -0
  80. package/dist/core/naming.js +26 -0
  81. package/dist/core/open-cloud.d.ts +58 -0
  82. package/dist/core/open-cloud.js +170 -0
  83. package/dist/core/project-index.d.ts +26 -0
  84. package/dist/core/project-index.js +59 -0
  85. package/dist/core/rojo-edit.d.ts +18 -0
  86. package/dist/core/rojo-edit.js +53 -0
  87. package/dist/core/rokit-installer.d.ts +15 -0
  88. package/dist/core/rokit-installer.js +77 -0
  89. package/dist/core/scaffold.d.ts +44 -0
  90. package/dist/core/scaffold.js +163 -0
  91. package/dist/core/schema-edit.d.ts +46 -0
  92. package/dist/core/schema-edit.js +125 -0
  93. package/dist/core/source-scan.d.ts +24 -0
  94. package/dist/core/source-scan.js +84 -0
  95. package/dist/core/studio.d.ts +83 -0
  96. package/dist/core/studio.js +225 -0
  97. package/dist/core/toolchain.d.ts +31 -0
  98. package/dist/core/toolchain.js +98 -0
  99. package/dist/core/tsconfig-edit.d.ts +11 -0
  100. package/dist/core/tsconfig-edit.js +47 -0
  101. package/dist/core/ui-edit.d.ts +31 -0
  102. package/dist/core/ui-edit.js +89 -0
  103. package/dist/core/versions.d.ts +24 -0
  104. package/dist/core/versions.js +67 -0
  105. package/dist/dashboard/open.d.ts +3 -0
  106. package/dist/dashboard/open.js +18 -0
  107. package/dist/dashboard/server.d.ts +28 -0
  108. package/dist/dashboard/server.js +191 -0
  109. package/dist/index.d.ts +3 -0
  110. package/dist/index.js +3 -0
  111. package/dist/main.d.ts +9 -0
  112. package/dist/main.js +135 -0
  113. package/dist/modules/format.d.ts +3 -0
  114. package/dist/modules/format.js +28 -0
  115. package/dist/modules/index.d.ts +7 -0
  116. package/dist/modules/index.js +12 -0
  117. package/dist/modules/integrations.d.ts +8 -0
  118. package/dist/modules/integrations.js +54 -0
  119. package/dist/modules/leaderstats.d.ts +3 -0
  120. package/dist/modules/leaderstats.js +104 -0
  121. package/dist/modules/lint.d.ts +5 -0
  122. package/dist/modules/lint.js +43 -0
  123. package/dist/modules/networking.d.ts +23 -0
  124. package/dist/modules/networking.js +246 -0
  125. package/dist/modules/player-data.d.ts +19 -0
  126. package/dist/modules/player-data.js +176 -0
  127. package/dist/modules/types.d.ts +96 -0
  128. package/dist/modules/types.js +2 -0
  129. package/dist/modules/ui.d.ts +3 -0
  130. package/dist/modules/ui.js +67 -0
  131. package/dist/plugins/api.d.ts +127 -0
  132. package/dist/plugins/api.js +21 -0
  133. package/dist/plugins/loader.d.ts +20 -0
  134. package/dist/plugins/loader.js +128 -0
  135. package/dist/process/log-mux.d.ts +13 -0
  136. package/dist/process/log-mux.js +31 -0
  137. package/dist/process/supervisor.d.ts +48 -0
  138. package/dist/process/supervisor.js +141 -0
  139. package/dist/process/tree-kill.d.ts +25 -0
  140. package/dist/process/tree-kill.js +70 -0
  141. package/dist/templates/engine.d.ts +18 -0
  142. package/dist/templates/engine.js +54 -0
  143. package/dist/ui/logger.d.ts +5 -0
  144. package/dist/ui/logger.js +53 -0
  145. package/dist/ui/prompt.d.ts +9 -0
  146. package/dist/ui/prompt.js +24 -0
  147. package/package.json +69 -0
  148. package/templates/init/README.md.tmpl +37 -0
  149. package/templates/init/_gitignore.tmpl +23 -0
  150. package/templates/init/default.project.json.tmpl +57 -0
  151. package/templates/init/package.json.tmpl +12 -0
  152. package/templates/init/rokit.toml.tmpl +5 -0
  153. package/templates/init/src/client/controllers/ExampleController.ts.tmpl +12 -0
  154. package/templates/init/src/client/runtime.client.ts.tmpl +6 -0
  155. package/templates/init/src/server/runtime.server.ts.tmpl +12 -0
  156. package/templates/init/src/server/services/ExampleService.ts.tmpl +12 -0
  157. package/templates/init/src/shared/_gitkeep.tmpl +0 -0
  158. package/templates/init/tsconfig.json.tmpl +28 -0
  159. package/templates/integrations/data-replication/DataReplicationService.ts.tmpl +33 -0
  160. package/templates/integrations/data-replication/PlayerDataController.ts.tmpl +40 -0
  161. package/templates/integrations/data-replication/dataEvents.ts.tmpl +21 -0
  162. package/templates/make/component.ts.tmpl +8 -0
  163. package/templates/make/controller.ts.tmpl +6 -0
  164. package/templates/make/event-handler.ts.tmpl +22 -0
  165. package/templates/make/screen.tsx.tmpl +35 -0
  166. package/templates/make/service.ts.tmpl +6 -0
  167. package/templates/make/stat-service.ts.tmpl +26 -0
  168. package/templates/make/ui-button.tsx.tmpl +22 -0
  169. package/templates/make/ui-image.tsx.tmpl +11 -0
  170. package/templates/make/ui-label.tsx.tmpl +18 -0
  171. package/templates/make/ui-panel.tsx.tmpl +33 -0
  172. package/templates/make/ui-story.tsx.tmpl +13 -0
  173. package/templates/modules/format/prettierignore.tmpl +9 -0
  174. package/templates/modules/format/prettierrc.json.tmpl +4 -0
  175. package/templates/modules/leaderstats/LeaderstatsService.ts.tmpl +69 -0
  176. package/templates/modules/lint/eslint.config.mjs.tmpl +17 -0
  177. package/templates/modules/networking/client-network.ts.tmpl +9 -0
  178. package/templates/modules/networking/networking.ts.tmpl +22 -0
  179. package/templates/modules/networking/rate-limit.ts.tmpl +52 -0
  180. package/templates/modules/networking/server-network.ts.tmpl +20 -0
  181. package/templates/modules/player-data/PlayerData.ts.tmpl +15 -0
  182. package/templates/modules/player-data/PlayerDataService.ts.tmpl +127 -0
  183. package/templates/modules/ui/App.tsx.tmpl +19 -0
  184. package/templates/modules/ui/Button.story.tsx.tmpl +13 -0
  185. package/templates/modules/ui/Button.tsx.tmpl +21 -0
  186. package/templates/modules/ui/UiController.tsx.tmpl +21 -0
  187. package/templates/modules/ui/screens/HomeScreen.tsx.tmpl +20 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 julian
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Rowork
2
+
3
+ **The meta-framework CLI for Roblox game development.**
4
+
5
+ Rowork is to Roblox what `artisan` is to Laravel: a single CLI that sets up the
6
+ toolchain, runs it, generates code and keeps your architecture coherent.
7
+
8
+ > ## ⚠️ This is only the very beginning
9
+ >
10
+ > Rowork is at **version 0.0.1**. The goal is big: a real **meta-framework for Roblox**, from the first
11
+ > line of a project to a live game. **What exists today is a small first step towards it**, and most of the
12
+ > vision is not built yet.
13
+ >
14
+ > - **Expect missing features, bugs and breaking changes** without warning.
15
+ > - **It is not on npm yet**, and it has been tried on very few games.
16
+ > - **Do not build a serious game on it yet.** Come and look, try it, and tell us what is wrong.
17
+ >
18
+ > The [roadmap](docs/roadmap.md) says what exists and what is missing, without promises.
19
+
20
+ ## The problem
21
+
22
+ Starting a modern Roblox project means wiring Rojo, roblox-ts, Flamework, a
23
+ pinned toolchain and a package manager together by hand: either several hours of
24
+ setup, or a GitHub template that goes stale in three months. And once the project
25
+ is running, nothing keeps file #200 consistent with the architecture of file #1.
26
+
27
+ ## What Rowork does
28
+
29
+ - **Generates.** `rowork start` walks you through creating a project;
30
+ `rowork init` does it without questions. `make:service`, `make:controller`
31
+ and `make:component` create Flamework classes and register them; `make:stat` and
32
+ `make:event` extend your saved data and networking without a forgotten line, and `make:screen` and `make:ui`
33
+ build your interface without plumbing.
34
+ - **Orchestrates.** `rowork dev` runs the roblox-ts compiler, the Rojo server
35
+ and a sourcemap watcher together, with unified logs and a clean shutdown.
36
+ - **Installs the toolchain.** Rokit and Rojo can be installed for you, with the
37
+ download verified by checksum.
38
+ - **Extends.** A plugin system so the community can add its own commands.
39
+
40
+ ## What is not there yet
41
+
42
+ Rowork is at the start, and a lot is still missing. Today there is **nothing** for:
43
+
44
+ - **an existing project**: `rowork init` starts a new one, nothing adopts a game that already exists;
45
+ - **changing your saved data** once players already have some (migrations);
46
+ - **tests for your game**, several environments (development, production), feature flags, monitoring;
47
+ - **seeing your interface in a browser** (only the UI Labs plugin inside Studio);
48
+ - **a plugin ecosystem**: the plugin API is tiny and will change, and no plugin exists yet;
49
+ - **Wally packages from TypeScript**, or anything outside roblox-ts, Flamework and Rojo.
50
+
51
+ ## What Rowork does not do
52
+
53
+ Rowork does not replace Rojo, roblox-ts, Flamework or Wally: it drives them.
54
+ `default.project.json`, `tsconfig.json` and `package.json` stay plain, visible,
55
+ editable files, and you can drop down to the bare tools at any time.
56
+ **No lock-in.**
57
+
58
+ ## Quick start
59
+
60
+ Rowork is not on npm yet, so install it from the repository:
61
+
62
+ ```bash
63
+ git clone https://github.com/nokogoat/Rowork
64
+ cd Rowork && npm install && npm run build && npm link
65
+ ```
66
+
67
+ Then:
68
+
69
+ ```bash
70
+ rowork start # guided setup: a few questions, then a ready project
71
+ cd MyGame
72
+ rowork dev # compiler + Rojo + sourcemap, together
73
+ ```
74
+
75
+ Connect the Rojo plugin in Studio and you are live. The full walkthrough,
76
+ including Studio, is in [Getting started](docs/getting-started.md).
77
+
78
+ ## Documentation
79
+
80
+ | | |
81
+ | --- | --- |
82
+ | [Getting started](docs/getting-started.md) | install, create a project, see it in Studio |
83
+ | [Commands](docs/commands.md) | every command and flag |
84
+ | [Project structure](docs/project-structure.md) | what is generated, where your code goes |
85
+ | [Configuration](docs/configuration.md) | `rowork.json` |
86
+ | [How `rowork dev` works](docs/dev-command.md) | orchestration, first build, shutdown |
87
+ | [Plugins](docs/plugins.md) | write and share commands |
88
+ | [Troubleshooting](docs/troubleshooting.md) | fix an error message |
89
+ | [Architecture](docs/architecture.md) | the codebase and its decisions |
90
+ | [Contributing](docs/contributing.md) | development setup and workflow |
91
+ | [Roadmap](docs/roadmap.md) | what exists, what is next |
92
+
93
+ ## License
94
+
95
+ MIT
package/bin/rowork.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ const entry = join(here, "..", "dist", "main.js");
8
+
9
+ if (!existsSync(entry)) {
10
+ console.error("Rowork is not built. Run `npm run build` at the repository root.");
11
+ process.exit(1);
12
+ }
13
+
14
+ // pathToFileURL is mandatory: `import("G:\...\main.js")` fails on Windows,
15
+ // because an absolute Windows path is not a valid URL.
16
+ const { run } = await import(pathToFileURL(entry).href);
17
+
18
+ await run(process.argv);
@@ -0,0 +1,234 @@
1
+ :root {
2
+ --bg: #f6f7f9;
3
+ --card: #ffffff;
4
+ --text: #1c2330;
5
+ --muted: #5b6577;
6
+ --line: #dfe3ea;
7
+ --accent: #3b6cff;
8
+ --good: #1c8c4e;
9
+ --warn: #b76e00;
10
+ --code: #eef1f6;
11
+ --log-bg: #10141c;
12
+ --log-text: #d5dbe8;
13
+ }
14
+ @media (prefers-color-scheme: dark) {
15
+ :root {
16
+ --bg: #0e1218;
17
+ --card: #161c26;
18
+ --text: #e7ebf3;
19
+ --muted: #93a0b5;
20
+ --line: #262f3d;
21
+ --accent: #7b9bff;
22
+ --good: #4cc38a;
23
+ --warn: #e0a545;
24
+ --code: #1f2733;
25
+ --log-bg: #0a0d13;
26
+ --log-text: #d5dbe8;
27
+ }
28
+ }
29
+ * {
30
+ box-sizing: border-box;
31
+ }
32
+ body {
33
+ margin: 0;
34
+ background: var(--bg);
35
+ color: var(--text);
36
+ font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
37
+ }
38
+ .top {
39
+ display: flex;
40
+ flex-wrap: wrap;
41
+ gap: 8px 24px;
42
+ align-items: baseline;
43
+ justify-content: space-between;
44
+ padding: 16px 24px 8px;
45
+ }
46
+ .logo {
47
+ font-size: 20px;
48
+ font-weight: 700;
49
+ letter-spacing: 0.2px;
50
+ }
51
+ .version {
52
+ margin-left: 8px;
53
+ color: var(--muted);
54
+ }
55
+ .project {
56
+ color: var(--muted);
57
+ overflow-wrap: anywhere;
58
+ }
59
+ .tabs {
60
+ display: flex;
61
+ gap: 4px;
62
+ padding: 0 24px;
63
+ border-bottom: 1px solid var(--line);
64
+ }
65
+ .tab {
66
+ appearance: none;
67
+ border: 0;
68
+ background: none;
69
+ color: var(--muted);
70
+ padding: 10px 14px;
71
+ font: inherit;
72
+ cursor: pointer;
73
+ border-bottom: 2px solid transparent;
74
+ }
75
+ .tab.active {
76
+ color: var(--text);
77
+ border-bottom-color: var(--accent);
78
+ }
79
+ main {
80
+ max-width: 1100px;
81
+ margin: 0 auto;
82
+ padding: 20px 24px 40px;
83
+ }
84
+ .panel {
85
+ display: none;
86
+ }
87
+ .panel.active {
88
+ display: block;
89
+ }
90
+ .grid {
91
+ display: grid;
92
+ gap: 16px;
93
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
94
+ margin-bottom: 16px;
95
+ }
96
+ .card {
97
+ background: var(--card);
98
+ border: 1px solid var(--line);
99
+ border-radius: 12px;
100
+ padding: 16px 18px;
101
+ }
102
+ h2 {
103
+ margin: 0 0 4px;
104
+ font-size: 16px;
105
+ }
106
+ .hint {
107
+ margin: 0 0 12px;
108
+ color: var(--muted);
109
+ font-size: 13px;
110
+ }
111
+ code {
112
+ background: var(--code);
113
+ padding: 1px 6px;
114
+ border-radius: 5px;
115
+ font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
116
+ }
117
+ .list {
118
+ list-style: none;
119
+ margin: 0;
120
+ padding: 0;
121
+ }
122
+ .list li {
123
+ display: flex;
124
+ gap: 10px;
125
+ align-items: baseline;
126
+ padding: 8px 0;
127
+ border-top: 1px solid var(--line);
128
+ }
129
+ .list li:first-child {
130
+ border-top: 0;
131
+ }
132
+ .list .name {
133
+ font-weight: 600;
134
+ min-width: 110px;
135
+ }
136
+ .list .desc {
137
+ color: var(--muted);
138
+ flex: 1;
139
+ }
140
+ .badge {
141
+ font-size: 11px;
142
+ padding: 1px 8px;
143
+ border-radius: 999px;
144
+ border: 1px solid var(--line);
145
+ white-space: nowrap;
146
+ }
147
+ .badge.installed,
148
+ .badge.applied {
149
+ color: var(--good);
150
+ border-color: var(--good);
151
+ }
152
+ .badge.guided {
153
+ color: var(--warn);
154
+ border-color: var(--warn);
155
+ }
156
+ .commands {
157
+ display: grid;
158
+ gap: 2px 24px;
159
+ grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
160
+ }
161
+ .command {
162
+ display: flex;
163
+ gap: 10px;
164
+ align-items: baseline;
165
+ padding: 6px 0;
166
+ border-top: 1px solid var(--line);
167
+ }
168
+ .command .cmd {
169
+ font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
170
+ min-width: 150px;
171
+ }
172
+ .command .desc {
173
+ color: var(--muted);
174
+ font-size: 13px;
175
+ flex: 1;
176
+ }
177
+ .row {
178
+ display: flex;
179
+ gap: 12px;
180
+ align-items: center;
181
+ justify-content: space-between;
182
+ }
183
+ .status {
184
+ font-size: 13px;
185
+ color: var(--muted);
186
+ }
187
+ .status.on {
188
+ color: var(--good);
189
+ }
190
+ .check {
191
+ color: var(--muted);
192
+ font-size: 13px;
193
+ }
194
+ .ghost {
195
+ appearance: none;
196
+ background: none;
197
+ border: 1px solid var(--line);
198
+ color: var(--text);
199
+ border-radius: 8px;
200
+ padding: 4px 12px;
201
+ font: inherit;
202
+ font-size: 13px;
203
+ cursor: pointer;
204
+ }
205
+ .log {
206
+ margin: 10px 0 0;
207
+ height: 60vh;
208
+ overflow: auto;
209
+ background: var(--log-bg);
210
+ color: var(--log-text);
211
+ border-radius: 10px;
212
+ padding: 12px 14px;
213
+ font: 12.5px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
214
+ white-space: pre-wrap;
215
+ overflow-wrap: anywhere;
216
+ }
217
+ footer {
218
+ text-align: center;
219
+ color: var(--muted);
220
+ font-size: 12px;
221
+ padding: 0 0 24px;
222
+ }
223
+ @media (max-width: 600px) {
224
+ .top,
225
+ .tabs,
226
+ main {
227
+ padding-left: 14px;
228
+ padding-right: 14px;
229
+ }
230
+ .list .name,
231
+ .command .cmd {
232
+ min-width: 0;
233
+ }
234
+ }
@@ -0,0 +1,117 @@
1
+ /*
2
+ * Rowork dashboard. No framework, no build step, no network access beyond the
3
+ * server that served this page.
4
+ *
5
+ * Everything that comes from outside (tool output, command names, descriptions) is
6
+ * put on the page with textContent, never as HTML: a hostile line in a log must not
7
+ * be able to run script in this page.
8
+ */
9
+ (() => {
10
+ "use strict";
11
+
12
+ const $ = (id) => document.getElementById(id);
13
+
14
+ /** Builds an element with text only. */
15
+ function el(tag, className, text) {
16
+ const node = document.createElement(tag);
17
+ if (className) node.className = className;
18
+ if (text !== undefined) node.textContent = text;
19
+ return node;
20
+ }
21
+
22
+ function clear(node) {
23
+ while (node.firstChild) node.removeChild(node.firstChild);
24
+ }
25
+
26
+ // ---- tabs
27
+ for (const tab of document.querySelectorAll(".tab")) {
28
+ tab.addEventListener("click", () => {
29
+ for (const other of document.querySelectorAll(".tab")) other.classList.toggle("active", other === tab);
30
+ for (const panel of document.querySelectorAll(".panel")) {
31
+ panel.classList.toggle("active", panel.id === tab.dataset.tab);
32
+ }
33
+ });
34
+ }
35
+
36
+ // ---- overview
37
+ function renderInfo(info) {
38
+ $("version").textContent = `v${info.rowork.version}`;
39
+ $("project").textContent = info.project
40
+ ? `${info.project.name} · ${info.project.root}`
41
+ : "Not inside a Rowork project";
42
+
43
+ const modules = $("modules");
44
+ clear(modules);
45
+ for (const module of info.modules) {
46
+ const item = el("li");
47
+ item.appendChild(el("span", "name", module.title));
48
+ item.appendChild(el("span", "desc", module.description));
49
+ item.appendChild(
50
+ module.installed ? el("span", "badge installed", "installed") : el("code", "", `rowork add ${module.name}`),
51
+ );
52
+ modules.appendChild(item);
53
+ }
54
+
55
+ const integrations = $("integrations");
56
+ clear(integrations);
57
+ for (const integration of info.integrations) {
58
+ const item = el("li");
59
+ item.appendChild(el("span", "name", integration.modules.join(" + ")));
60
+ item.appendChild(el("span", "desc", integration.description));
61
+ item.appendChild(
62
+ integration.applied ? el("span", "badge applied", "wired") : el("span", "badge", "waiting for both"),
63
+ );
64
+ integrations.appendChild(item);
65
+ }
66
+
67
+ const commands = $("commands");
68
+ clear(commands);
69
+ for (const command of info.commands) {
70
+ const item = el("div", "command");
71
+ item.appendChild(el("span", "cmd", `rowork ${command.name}`));
72
+ item.appendChild(el("span", "desc", command.description));
73
+ if (command.guided) item.appendChild(el("span", "badge guided", "guided"));
74
+ commands.appendChild(item);
75
+ }
76
+
77
+ const dev = info.project && info.project.dev;
78
+ const status = $("dev-status");
79
+ status.textContent = dev
80
+ ? `running ${dev.mode === "foreground" ? "in a terminal" : "in the background"} (pid ${dev.pid}, port ${dev.port})`
81
+ : "not running";
82
+ status.className = dev ? "status on" : "status";
83
+ }
84
+
85
+ async function refresh() {
86
+ try {
87
+ const response = await fetch("/api/info", { cache: "no-store" });
88
+ if (response.ok) renderInfo(await response.json());
89
+ } catch {
90
+ // The server stopped: keep what is on screen.
91
+ }
92
+ }
93
+ refresh();
94
+ setInterval(refresh, 3000);
95
+
96
+ // ---- dev output
97
+ const ANSI = /\u001b\[[0-9;?]*[A-Za-z]/g;
98
+ const log = $("log");
99
+ const MAX_LINES = 2000;
100
+
101
+ function addLine(text) {
102
+ log.appendChild(document.createTextNode(`${text.replace(ANSI, "")}\n`));
103
+ while (log.childNodes.length > MAX_LINES) log.removeChild(log.firstChild);
104
+ if ($("follow").checked) log.scrollTop = log.scrollHeight;
105
+ }
106
+
107
+ const stream = new EventSource("/api/dev/logs");
108
+ stream.onmessage = (event) => {
109
+ try {
110
+ addLine(JSON.parse(event.data));
111
+ } catch {
112
+ // Ignore a malformed message rather than break the stream.
113
+ }
114
+ };
115
+
116
+ $("clear").addEventListener("click", () => clear(log));
117
+ })();
@@ -0,0 +1,67 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Rowork dashboard</title>
7
+ <link rel="stylesheet" href="/app.css" />
8
+ </head>
9
+ <body>
10
+ <header class="top">
11
+ <div class="brand">
12
+ <span class="logo">Rowork</span>
13
+ <span class="version" id="version"></span>
14
+ </div>
15
+ <div class="project" id="project">Loading…</div>
16
+ </header>
17
+
18
+ <nav class="tabs" role="tablist">
19
+ <button class="tab active" role="tab" data-tab="overview">Overview</button>
20
+ <button class="tab" role="tab" data-tab="dev">Dev output</button>
21
+ </nav>
22
+
23
+ <main>
24
+ <section id="overview" class="panel active">
25
+ <div class="grid">
26
+ <div class="card">
27
+ <h2>Features</h2>
28
+ <p class="hint">Ready-made features. Add one with <code>rowork add</code>.</p>
29
+ <ul class="list" id="modules"></ul>
30
+ </div>
31
+ <div class="card">
32
+ <h2>Wired together</h2>
33
+ <p class="hint">Glue Rowork generated between features.</p>
34
+ <ul class="list" id="integrations"></ul>
35
+ </div>
36
+ </div>
37
+ <div class="card">
38
+ <h2>Commands</h2>
39
+ <p class="hint">
40
+ <span class="badge guided">guided</span> commands ask their questions in the terminal.
41
+ </p>
42
+ <div class="commands" id="commands"></div>
43
+ </div>
44
+ </section>
45
+
46
+ <section id="dev" class="panel">
47
+ <div class="card">
48
+ <div class="row">
49
+ <h2>Dev</h2>
50
+ <span class="status" id="dev-status"></span>
51
+ </div>
52
+ <p class="hint">
53
+ Output of <code>rowork dev</code>, live, compiler errors included.
54
+ </p>
55
+ <div class="row">
56
+ <label class="check"><input type="checkbox" id="follow" checked /> Follow</label>
57
+ <button class="ghost" id="clear">Clear</button>
58
+ </div>
59
+ <pre class="log" id="log" aria-live="polite"></pre>
60
+ </div>
61
+ </section>
62
+ </main>
63
+
64
+ <footer>Local only. This page is served by <code>rowork dashboard</code> on your computer.</footer>
65
+ <script src="/app.js"></script>
66
+ </body>
67
+ </html>
@@ -0,0 +1,15 @@
1
+ /**
2
+ * An expected, user-facing error: short message plus a resolution hint.
3
+ * Anything else (an internal bug) surfaces as a full stack trace.
4
+ */
5
+ export declare class RoworkError extends Error {
6
+ readonly hint: string | undefined;
7
+ readonly exitCode: number;
8
+ constructor(message: string, options?: {
9
+ hint?: string;
10
+ exitCode?: number;
11
+ cause?: unknown;
12
+ });
13
+ }
14
+ export declare function isRoworkError(error: unknown): error is RoworkError;
15
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * An expected, user-facing error: short message plus a resolution hint.
3
+ * Anything else (an internal bug) surfaces as a full stack trace.
4
+ */
5
+ export class RoworkError extends Error {
6
+ hint;
7
+ exitCode;
8
+ constructor(message, options = {}) {
9
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
10
+ this.name = "RoworkError";
11
+ this.hint = options.hint;
12
+ this.exitCode = options.exitCode ?? 1;
13
+ }
14
+ }
15
+ export function isRoworkError(error) {
16
+ return error instanceof RoworkError;
17
+ }
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,19 @@
1
+ import { Command } from "commander";
2
+ import type { RoworkConfig } from "../plugins/api.js";
3
+ import type { CommandRegistry } from "./registry.js";
4
+ export interface ProgramOptions {
5
+ registry: CommandRegistry;
6
+ cwd: string;
7
+ projectRoot: string | undefined;
8
+ config: RoworkConfig | undefined;
9
+ roworkVersion: string;
10
+ }
11
+ /**
12
+ * Builds the Commander instance from the registry.
13
+ *
14
+ * Core commands and plugin commands go through exactly the same path: the core
15
+ * consumes its own public contract, which guarantees that a regression in the
16
+ * plugin API also breaks the CLI, and is therefore noticed immediately.
17
+ */
18
+ export declare function createProgram(options: ProgramOptions): Command;
19
+ //# sourceMappingURL=program.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { Command } from "commander";
2
+ import pc from "picocolors";
3
+ import { logger } from "../ui/logger.js";
4
+ /**
5
+ * Builds the Commander instance from the registry.
6
+ *
7
+ * Core commands and plugin commands go through exactly the same path: the core
8
+ * consumes its own public contract, which guarantees that a regression in the
9
+ * plugin API also breaks the CLI, and is therefore noticed immediately.
10
+ */
11
+ export function createProgram(options) {
12
+ const program = new Command();
13
+ program
14
+ .name("rowork")
15
+ .description("The meta-framework CLI for Roblox game development.")
16
+ .version(options.roworkVersion, "-v, --version")
17
+ .option("--cwd <dir>", "working directory")
18
+ .option("--verbose", "verbose logging")
19
+ .option("--quiet", "only print errors")
20
+ .option("--no-plugins", "start without loading any plugin")
21
+ .showHelpAfterError()
22
+ .configureHelp({ sortSubcommands: true });
23
+ for (const { definition } of options.registry.all()) {
24
+ const command = program.command(definition.name).description(definition.description);
25
+ for (const alias of definition.aliases ?? [])
26
+ command.alias(alias);
27
+ for (const argument of definition.arguments ?? []) {
28
+ const required = argument.required ?? true;
29
+ const inner = argument.variadic ? `${argument.name}...` : argument.name;
30
+ const token = required ? `<${inner}>` : `[${inner}]`;
31
+ command.argument(token, argument.description, argument.defaultValue);
32
+ }
33
+ for (const option of definition.options ?? []) {
34
+ command.option(option.flags, option.description, option.defaultValue);
35
+ }
36
+ command.action(async (...invocation) => {
37
+ // Commander passes: ...positional arguments, options, then the Command.
38
+ const positional = invocation.slice(0, -2);
39
+ const commandOptions = (invocation.at(-2) ?? {});
40
+ const args = {};
41
+ (definition.arguments ?? []).forEach((argument, index) => {
42
+ args[argument.name] = positional[index];
43
+ });
44
+ const context = {
45
+ args,
46
+ options: commandOptions,
47
+ cwd: options.cwd,
48
+ projectRoot: options.projectRoot,
49
+ config: options.config,
50
+ logger,
51
+ roworkVersion: options.roworkVersion,
52
+ };
53
+ await definition.run(context);
54
+ });
55
+ }
56
+ program.addHelpText("after", `\n${pc.dim("Docs: https://github.com/nokogoat/Rowork")}\n`);
57
+ return program;
58
+ }
59
+ //# sourceMappingURL=program.js.map