@owdproject/cli 0.1.0 → 0.2.1
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.
- package/README.md +7 -7
- package/bin/bridge.js +4 -2
- package/bin/cli.js +63 -5
- package/bin/cp.js +42 -23
- package/bin/lib/catalog.js +3 -0
- package/bin/lib/config.js +51 -8
- package/bin/lib/cpFormat.js +2 -2
- package/bin/lib/create.js +246 -0
- package/bin/lib/desktopConfig.js +48 -8
- package/bin/lib/generateTemplate.js +2 -1
- package/bin/lib/install.js +17 -21
- package/bin/lib/installWizardFlow.js +90 -24
- package/bin/lib/packageSources.js +10 -0
- package/bin/lib/status.js +2 -0
- package/bin/lib/validateModule.js +112 -2
- package/bin/lib/workspace.js +69 -5
- package/go/bridge/bridge.go +20 -0
- package/go/desktop-go +0 -0
- package/go/go.mod +17 -10
- package/go/go.sum +34 -19
- package/go/main.go +7 -3
- package/go/owd-cli +0 -0
- package/go/tui/engine.go +472 -0
- package/go/tui/engine_test.go +40 -0
- package/go/tui/handlers.go +604 -0
- package/go/tui/helpers.go +916 -0
- package/go/tui/helpers_test.go +97 -0
- package/go/tui/modals.go +423 -0
- package/go/tui/resolver.go +377 -0
- package/go/tui/resolver_test.go +86 -0
- package/go/tui/styles.go +153 -0
- package/go/tui/tasks.go +850 -0
- package/go/tui/tui.go +451 -2596
- package/go/tui/tui_test.go +69 -0
- package/go/tui/types.go +230 -0
- package/go/tui/view.go +812 -0
- package/go/tui/workqueue.go +179 -0
- package/go/tui/workqueue_test.go +90 -0
- package/package.json +2 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package tui
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
"owd-cli/bridge"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func TestNewModelEngineState(t *testing.T) {
|
|
10
|
+
model := NewModel("test-root")
|
|
11
|
+
if model.workQueue == nil {
|
|
12
|
+
t.Error("Expected workQueue to be initialized")
|
|
13
|
+
}
|
|
14
|
+
if model.enginePhase != PhaseIdle {
|
|
15
|
+
t.Errorf("Expected enginePhase idle, got %s", model.enginePhase)
|
|
16
|
+
}
|
|
17
|
+
if model.runtime.engineResume == nil {
|
|
18
|
+
t.Error("Expected engineResume channel to be initialized")
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func TestEngineQueueMessages(t *testing.T) {
|
|
23
|
+
model := NewModel("test-root")
|
|
24
|
+
|
|
25
|
+
q := NewWorkQueue()
|
|
26
|
+
q.AppendUnique(WorkItem{Name: "a", ShortName: "a", Status: StatusPending})
|
|
27
|
+
q.AppendUnique(WorkItem{Name: "b", ShortName: "b", Status: StatusCompleted})
|
|
28
|
+
|
|
29
|
+
m2, _ := (&model).Update(engineQueueUpdatedMsg{Queue: q.Clone()})
|
|
30
|
+
updated := m2.(*TuiModel)
|
|
31
|
+
if updated.workQueue == nil || len(updated.workQueue.Items) != 2 {
|
|
32
|
+
t.Fatalf("expected queue with 2 items, got %+v", updated.workQueue)
|
|
33
|
+
}
|
|
34
|
+
if updated.setupStep != 1 || updated.setupTotalSteps != 2 {
|
|
35
|
+
t.Errorf("expected progress 1/2, got %d/%d", updated.setupStep, updated.setupTotalSteps)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
m3, _ := updated.Update(enginePhaseMsg{Phase: PhaseProcess})
|
|
39
|
+
phaseModel := m3.(*TuiModel)
|
|
40
|
+
if phaseModel.enginePhase != PhaseProcess {
|
|
41
|
+
t.Errorf("expected PhaseProcess, got %s", phaseModel.enginePhase)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func TestBuildConfigFromPending(t *testing.T) {
|
|
46
|
+
theme := "@owdproject/theme-nova"
|
|
47
|
+
ctx := &bridge.WorkspaceContext{
|
|
48
|
+
Config: bridge.Config{
|
|
49
|
+
Theme: &theme,
|
|
50
|
+
Apps: []string{"@owdproject/app-about"},
|
|
51
|
+
Modules: []string{},
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
catalog := &bridge.CatalogResponse{
|
|
55
|
+
Entries: []bridge.CatalogEntry{
|
|
56
|
+
{Name: "@owdproject/app-todo", ShortName: "app-todo", Kind: "app"},
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
pending := map[string]bool{
|
|
60
|
+
"@owdproject/app-todo": true,
|
|
61
|
+
}
|
|
62
|
+
config, removes := buildConfigFromPending(ctx, catalog, pending, nil)
|
|
63
|
+
if len(removes) != 0 {
|
|
64
|
+
t.Fatalf("expected no removals, got %v", removes)
|
|
65
|
+
}
|
|
66
|
+
if !containsStr(config.Apps, "@owdproject/app-todo") {
|
|
67
|
+
t.Fatalf("expected app-todo in apps, got %v", config.Apps)
|
|
68
|
+
}
|
|
69
|
+
}
|
package/go/tui/types.go
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
package tui
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"sync"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"owd-cli/bridge"
|
|
10
|
+
|
|
11
|
+
"github.com/charmbracelet/bubbles/textinput"
|
|
12
|
+
tea "github.com/charmbracelet/bubbletea"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// ─────────────────────────────────────────────
|
|
16
|
+
// Msg types
|
|
17
|
+
// ─────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
type contextLoadedMsg struct {
|
|
20
|
+
Ctx *bridge.WorkspaceContext
|
|
21
|
+
Err error
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type catalogLoadedMsg struct {
|
|
25
|
+
Cat *bridge.CatalogResponse
|
|
26
|
+
Err error
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type logLineMsg string
|
|
30
|
+
type clearLogsMsg struct{}
|
|
31
|
+
|
|
32
|
+
type taskFinishedMsg struct {
|
|
33
|
+
Success bool
|
|
34
|
+
Err error
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type serverStatusMsg struct {
|
|
38
|
+
Running bool
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type tickMsg time.Time
|
|
42
|
+
|
|
43
|
+
type memTickMsg struct {
|
|
44
|
+
MemMB int
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type GitChanges struct {
|
|
48
|
+
Added int
|
|
49
|
+
Modified int
|
|
50
|
+
Deleted int
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type UpdateInfo struct {
|
|
54
|
+
LocalGit bool
|
|
55
|
+
BehindCount int
|
|
56
|
+
Npm bool
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// localChangesMsg is the result of the fast local-only git status check (no network).
|
|
60
|
+
type localChangesMsg struct {
|
|
61
|
+
GitChanges map[string]GitChanges
|
|
62
|
+
LocalGitDirs map[string]bool // shortName -> has .git folder
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// remoteUpdatesMsg is the result of the slow network check (git behind + npm versions).
|
|
66
|
+
type remoteUpdatesMsg struct {
|
|
67
|
+
Updates map[string]UpdateInfo
|
|
68
|
+
Err error
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type setupProgressMsg struct {
|
|
72
|
+
Step int
|
|
73
|
+
Total int
|
|
74
|
+
Label string
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type engineNeedsPromptMsg struct {
|
|
78
|
+
Item WorkItem
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type engineQueueUpdatedMsg struct {
|
|
82
|
+
Queue WorkQueue
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type enginePhaseMsg struct {
|
|
86
|
+
Phase EnginePhase
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type workspaceGitStatusMsg struct {
|
|
90
|
+
Branch string
|
|
91
|
+
Changes string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type InstallMethod struct {
|
|
95
|
+
Name string
|
|
96
|
+
Label string
|
|
97
|
+
Desc string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─────────────────────────────────────────────
|
|
101
|
+
// Prompt states
|
|
102
|
+
// ─────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
type PromptType int
|
|
105
|
+
|
|
106
|
+
const (
|
|
107
|
+
PromptNone PromptType = iota
|
|
108
|
+
PromptInstallMethod
|
|
109
|
+
PromptUninstallConfirm
|
|
110
|
+
PromptManagePackage
|
|
111
|
+
PromptForceReinstallConfirm
|
|
112
|
+
PromptSettings
|
|
113
|
+
PromptSetupProgress
|
|
114
|
+
PromptResolveDependency
|
|
115
|
+
PromptWipeWorkspaceConfirm
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
type ActiveTask int
|
|
119
|
+
|
|
120
|
+
const (
|
|
121
|
+
TaskNone ActiveTask = iota
|
|
122
|
+
TaskSetup
|
|
123
|
+
TaskWipe
|
|
124
|
+
TaskServe
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// RuntimeState holds references to mutable OS/runtime resources.
|
|
128
|
+
type RuntimeState struct {
|
|
129
|
+
serverCmd *exec.Cmd
|
|
130
|
+
serverMu sync.Mutex
|
|
131
|
+
logCancel context.CancelFunc
|
|
132
|
+
msgChan chan tea.Msg
|
|
133
|
+
engineResume chan string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─────────────────────────────────────────────
|
|
137
|
+
// Menu constants and actions
|
|
138
|
+
// ─────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
var ManagePackageActions = []string{
|
|
141
|
+
"Update package",
|
|
142
|
+
"Switch to NPM registry",
|
|
143
|
+
"Switch to Git (HTTPS)",
|
|
144
|
+
"Switch to Git (SSH)",
|
|
145
|
+
"Force Re-download (wipe changes)",
|
|
146
|
+
"Back to catalog",
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const (
|
|
150
|
+
SettingsFieldCount = 5 // Install Mode, Catalog Sort, Trusted Orgs, GitHub User, Reset Workspace
|
|
151
|
+
SettingsActionCount = 2 // Save, Cancel
|
|
152
|
+
SettingsTotalOptions = SettingsFieldCount + SettingsActionCount
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
// ─────────────────────────────────────────────
|
|
156
|
+
// Model
|
|
157
|
+
// ─────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
type TuiModel struct {
|
|
162
|
+
workspaceRoot string
|
|
163
|
+
ctx *bridge.WorkspaceContext
|
|
164
|
+
catalog *bridge.CatalogResponse
|
|
165
|
+
runtime *RuntimeState
|
|
166
|
+
|
|
167
|
+
ExitCode int
|
|
168
|
+
activeTab int // 0: Internal, 1: Apps, 2: Modules, 3: Themes
|
|
169
|
+
err error
|
|
170
|
+
loading bool
|
|
171
|
+
statusMsg string
|
|
172
|
+
logLines []string
|
|
173
|
+
memHistory []int
|
|
174
|
+
|
|
175
|
+
// Selection
|
|
176
|
+
selectedIndex int
|
|
177
|
+
scrollOffset int
|
|
178
|
+
|
|
179
|
+
// Prompts/Modals
|
|
180
|
+
activePrompt PromptType
|
|
181
|
+
promptPkg *bridge.CatalogEntry
|
|
182
|
+
promptSel int
|
|
183
|
+
|
|
184
|
+
promptPkgDeps []string
|
|
185
|
+
promptPkgHasPrimevue bool
|
|
186
|
+
promptPkgHasTailwind bool
|
|
187
|
+
|
|
188
|
+
gitChangesMap map[string]GitChanges
|
|
189
|
+
updatesMap map[string]UpdateInfo
|
|
190
|
+
localGitDirs map[string]bool // shortName -> has own .git folder
|
|
191
|
+
|
|
192
|
+
settingsSel int
|
|
193
|
+
settingsInstallMode string
|
|
194
|
+
settingsCatalogSort string
|
|
195
|
+
settingsOrgsInput textinput.Model
|
|
196
|
+
settingsUserInput textinput.Model
|
|
197
|
+
|
|
198
|
+
// Server status
|
|
199
|
+
serverRunning bool
|
|
200
|
+
activeTask ActiveTask
|
|
201
|
+
checkingUpdates bool
|
|
202
|
+
|
|
203
|
+
// Animation
|
|
204
|
+
blink bool
|
|
205
|
+
tickCount int
|
|
206
|
+
|
|
207
|
+
// Layout
|
|
208
|
+
termWidth int
|
|
209
|
+
termHeight int
|
|
210
|
+
|
|
211
|
+
lastInstallMethod string
|
|
212
|
+
logTailerStarted bool
|
|
213
|
+
|
|
214
|
+
// Queued changes
|
|
215
|
+
pendingPackages map[string]bool // pkgName -> install(true)/uninstall(false)
|
|
216
|
+
pendingTheme *string // pointer to theme name to activate
|
|
217
|
+
|
|
218
|
+
// Wizard engine
|
|
219
|
+
workQueue *WorkQueue
|
|
220
|
+
enginePhase EnginePhase
|
|
221
|
+
promptItem *WorkItem
|
|
222
|
+
|
|
223
|
+
startServerAfterSetup bool
|
|
224
|
+
setupStep int
|
|
225
|
+
setupTotalSteps int
|
|
226
|
+
setupLabel string
|
|
227
|
+
workspaceBranch string
|
|
228
|
+
workspaceChanges string
|
|
229
|
+
clientStars int
|
|
230
|
+
}
|