@myop/cli 0.1.40 → 0.1.41
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/dist/myop-cli.js +1184 -833
- package/dist/skills/myop-component/SKILL.md +233 -0
- package/dist/skills/myop-component/references/best-practices.md +406 -0
- package/dist/skills/myop-component/references/component-api.md +357 -0
- package/dist/skills/myop-component/references/dev-workflow.md +218 -0
- package/dist/skills/myop-component/references/sizing-and-layout.md +270 -0
- package/dist/skills/myop-component/references/type-definitions.md +270 -0
- package/package.json +2 -2
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Component Public API
|
|
2
|
+
|
|
3
|
+
Myop components communicate with host applications through exactly 2 global functions defined on `window`. Both must be defined synchronously during top-level script execution.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
- [myop_init_interface](#myop_init_interface)
|
|
7
|
+
- [myop_cta_handler](#myop_cta_handler)
|
|
8
|
+
- [Preview Script](#preview-script)
|
|
9
|
+
- [Complete Example](#complete-example)
|
|
10
|
+
- [Host-Side Integration](#host-side-integration)
|
|
11
|
+
|
|
12
|
+
## myop_init_interface
|
|
13
|
+
|
|
14
|
+
**Direction:** Host Application -> Component
|
|
15
|
+
|
|
16
|
+
**Purpose:** Initialize or update the component with data. Also used to read current state.
|
|
17
|
+
|
|
18
|
+
### Dual Signature
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// Setter: Host sends data to component
|
|
22
|
+
window.myop_init_interface(data); // returns void
|
|
23
|
+
|
|
24
|
+
// Getter: Host reads current component state
|
|
25
|
+
const state = window.myop_init_interface(); // returns MyopInitData
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Implementation Pattern
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
(function() {
|
|
32
|
+
var currentState = {};
|
|
33
|
+
|
|
34
|
+
window.myop_init_interface = function(data) {
|
|
35
|
+
// Getter mode: return current state when called without arguments
|
|
36
|
+
if (!data) return currentState;
|
|
37
|
+
|
|
38
|
+
// Setter mode: store state and render synchronously
|
|
39
|
+
currentState = data;
|
|
40
|
+
render(data);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function render(data) {
|
|
44
|
+
var root = document.getElementById('app-root');
|
|
45
|
+
// Build HTML string and set innerHTML in one operation
|
|
46
|
+
root.innerHTML = '<h1>' + data.title + '</h1>';
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Rules
|
|
52
|
+
|
|
53
|
+
1. **MUST be defined at top-level** - The host calls this function immediately after loading the component. If it's not yet defined, the component will not receive data.
|
|
54
|
+
|
|
55
|
+
2. **MUST render synchronously** - When called with data, the component must update the DOM before the function returns. No `setTimeout`, no `requestAnimationFrame`, no `await`.
|
|
56
|
+
|
|
57
|
+
3. **MUST handle getter mode** - When called without arguments (`myop_init_interface()`), return the current state object. The host uses this to read component state.
|
|
58
|
+
|
|
59
|
+
4. **MUST be idempotent** - Can be called multiple times with updated data. Each call should fully re-render.
|
|
60
|
+
|
|
61
|
+
### WRONG Examples
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// WRONG: Defined inside DOMContentLoaded (too late)
|
|
65
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
66
|
+
window.myop_init_interface = function(data) { render(data); };
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// WRONG: Async rendering (host expects synchronous update)
|
|
70
|
+
window.myop_init_interface = async function(data) {
|
|
71
|
+
const extra = await fetch('/api/data'); // NO!
|
|
72
|
+
render(data, extra);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// WRONG: Deferred rendering
|
|
76
|
+
window.myop_init_interface = function(data) {
|
|
77
|
+
setTimeout(() => render(data), 0); // NO!
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// WRONG: Missing getter mode
|
|
81
|
+
window.myop_init_interface = function(data) {
|
|
82
|
+
render(data); // What happens when called with no args?
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## myop_cta_handler
|
|
87
|
+
|
|
88
|
+
**Direction:** Component -> Host Application
|
|
89
|
+
|
|
90
|
+
**Purpose:** Send user actions (clicks, selections, form submissions) from the component to the host application. Also used for standard actions like requesting a size change.
|
|
91
|
+
|
|
92
|
+
### Signature
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
window.myop_cta_handler(action_id, payload);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Parameter | Type | Description |
|
|
99
|
+
|-----------|------|-------------|
|
|
100
|
+
| `action_id` | `string` | Action identifier (kebab-case, e.g. `'item-clicked'`) |
|
|
101
|
+
| `payload` | `object` | Action-specific data |
|
|
102
|
+
|
|
103
|
+
### Implementation Pattern
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
(function() {
|
|
107
|
+
// Define a no-op default (host will override this)
|
|
108
|
+
window.myop_cta_handler = function(action, payload) {
|
|
109
|
+
// Will be replaced by host application
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Call it when user interacts
|
|
113
|
+
document.getElementById('app-root').addEventListener('click', function(e) {
|
|
114
|
+
var button = e.target.closest('button');
|
|
115
|
+
if (button) {
|
|
116
|
+
window.myop_cta_handler('button-clicked', {
|
|
117
|
+
buttonId: button.dataset.id,
|
|
118
|
+
label: button.textContent
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
})();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Standard Actions
|
|
126
|
+
|
|
127
|
+
These action IDs have special meaning and are handled by the Myop SDK:
|
|
128
|
+
|
|
129
|
+
| Action ID | Payload | Purpose |
|
|
130
|
+
|-----------|---------|---------|
|
|
131
|
+
| `'size-requested'` | `{ width?, height?, minWidth?, maxWidth?, minHeight?, maxHeight?, required? }` | Request the host to resize the component container |
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// Request more width
|
|
135
|
+
window.myop_cta_handler('size-requested', {
|
|
136
|
+
width: 400,
|
|
137
|
+
minWidth: 300,
|
|
138
|
+
maxWidth: 600,
|
|
139
|
+
required: true // true = content is clipped without this size
|
|
140
|
+
// false = preference only, host may ignore
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Custom Actions
|
|
145
|
+
|
|
146
|
+
Define any custom actions your component needs. Use kebab-case for action IDs:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// User selects an item
|
|
150
|
+
window.myop_cta_handler('item-selected', { itemId: 'abc-123' });
|
|
151
|
+
|
|
152
|
+
// Form submitted
|
|
153
|
+
window.myop_cta_handler('form-submitted', {
|
|
154
|
+
name: 'John',
|
|
155
|
+
email: 'john@example.com'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Status changed
|
|
159
|
+
window.myop_cta_handler('status-changed', { from: 'draft', to: 'published' });
|
|
160
|
+
|
|
161
|
+
// No-payload action
|
|
162
|
+
window.myop_cta_handler('refresh-requested', {});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Rules
|
|
166
|
+
|
|
167
|
+
1. **Define at top-level** - Even though the host overrides it, defining a no-op default prevents errors if the component fires an action before the host sets up its handler.
|
|
168
|
+
|
|
169
|
+
2. **Use kebab-case** for action IDs: `'item-clicked'` not `'itemClicked'` or `'ITEM_CLICKED'`.
|
|
170
|
+
|
|
171
|
+
3. **Payload must be serializable** - Plain objects only. No DOM elements, functions, or class instances.
|
|
172
|
+
|
|
173
|
+
4. **Document all actions** in the `MyopCtaPayloads` type definition so the host developer knows what to expect.
|
|
174
|
+
|
|
175
|
+
## Preview Script
|
|
176
|
+
|
|
177
|
+
The `<script id="myop_preview">` block provides mock data for development. In production, the Myop SDK replaces this with a real data call.
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<!-- Runs during development, removed in production -->
|
|
181
|
+
<script id="myop_preview">
|
|
182
|
+
window.myop_init_interface({
|
|
183
|
+
title: 'Preview Mode',
|
|
184
|
+
items: [
|
|
185
|
+
{ id: '1', label: 'Sample item 1' },
|
|
186
|
+
{ id: '2', label: 'Sample item 2' },
|
|
187
|
+
{ id: '3', label: 'Sample item 3' }
|
|
188
|
+
]
|
|
189
|
+
});
|
|
190
|
+
</script>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Testing Delayed Load
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<script id="myop_preview">
|
|
197
|
+
// Simulate delayed data arrival
|
|
198
|
+
setTimeout(function() {
|
|
199
|
+
window.myop_init_interface({
|
|
200
|
+
title: 'Loaded after delay',
|
|
201
|
+
items: []
|
|
202
|
+
});
|
|
203
|
+
}, 1000);
|
|
204
|
+
</script>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Complete Example
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<!DOCTYPE html>
|
|
211
|
+
<html lang="en">
|
|
212
|
+
<head>
|
|
213
|
+
<meta charset="UTF-8">
|
|
214
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
215
|
+
<meta name="myop:size" content='{"width":350,"height":"100%","minWidth":250}'>
|
|
216
|
+
<title>Task List</title>
|
|
217
|
+
<script type="myop/types">
|
|
218
|
+
interface MyopInitData {
|
|
219
|
+
tasks: Array<{
|
|
220
|
+
id: string;
|
|
221
|
+
title: string;
|
|
222
|
+
completed: boolean;
|
|
223
|
+
}>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
interface MyopCtaPayloads {
|
|
227
|
+
'task-toggled': { taskId: string; completed: boolean };
|
|
228
|
+
'task-deleted': { taskId: string };
|
|
229
|
+
'size-requested': {
|
|
230
|
+
width?: number | null;
|
|
231
|
+
height?: number | null;
|
|
232
|
+
minWidth?: number | null;
|
|
233
|
+
maxWidth?: number | null;
|
|
234
|
+
minHeight?: number | null;
|
|
235
|
+
maxHeight?: number | null;
|
|
236
|
+
required?: boolean;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare function myop_init_interface(): MyopInitData;
|
|
241
|
+
declare function myop_init_interface(data: MyopInitData): void;
|
|
242
|
+
declare function myop_cta_handler<K extends keyof MyopCtaPayloads>(
|
|
243
|
+
action: K, payload: MyopCtaPayloads[K]
|
|
244
|
+
): void;
|
|
245
|
+
</script>
|
|
246
|
+
<style>
|
|
247
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
248
|
+
html, body { width: 100%; height: 100%; overflow: hidden;
|
|
249
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
250
|
+
color: #333;
|
|
251
|
+
}
|
|
252
|
+
#app-root { width: 100%; height: 100%; display: flex; flex-direction: column; }
|
|
253
|
+
.header { padding: 12px 16px; border-bottom: 1px solid #eee; font-weight: 600; }
|
|
254
|
+
.task-list { flex: 1; overflow-y: auto; min-height: 0; }
|
|
255
|
+
.task { display: flex; align-items: center; padding: 10px 16px;
|
|
256
|
+
border-bottom: 1px solid #f0f0f0; gap: 10px; }
|
|
257
|
+
.task.done .task-title { text-decoration: line-through; color: #999; }
|
|
258
|
+
.task-title { flex: 1; }
|
|
259
|
+
.delete-btn { cursor: pointer; color: #999; border: none;
|
|
260
|
+
background: none; font-size: 16px; }
|
|
261
|
+
.delete-btn:hover { color: #e74c3c; }
|
|
262
|
+
</style>
|
|
263
|
+
</head>
|
|
264
|
+
<body>
|
|
265
|
+
<div id="app-root">
|
|
266
|
+
<div class="header">Tasks</div>
|
|
267
|
+
<div class="task-list"></div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<script>
|
|
271
|
+
(function() {
|
|
272
|
+
var state = { tasks: [] };
|
|
273
|
+
var taskList = document.querySelector('.task-list');
|
|
274
|
+
|
|
275
|
+
function render(data) {
|
|
276
|
+
state = data;
|
|
277
|
+
taskList.innerHTML = data.tasks.map(function(task) {
|
|
278
|
+
return '<div class="task' + (task.completed ? ' done' : '') + '" data-id="' + task.id + '">' +
|
|
279
|
+
'<input type="checkbox"' + (task.completed ? ' checked' : '') + '>' +
|
|
280
|
+
'<span class="task-title">' + task.title + '</span>' +
|
|
281
|
+
'<button class="delete-btn" data-action="delete">×</button>' +
|
|
282
|
+
'</div>';
|
|
283
|
+
}).join('');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
window.myop_init_interface = function(data) {
|
|
287
|
+
if (!data) return state;
|
|
288
|
+
render(data);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
window.myop_cta_handler = function(action, payload) {};
|
|
292
|
+
|
|
293
|
+
// Event delegation for all task interactions
|
|
294
|
+
taskList.addEventListener('click', function(e) {
|
|
295
|
+
var taskEl = e.target.closest('.task');
|
|
296
|
+
if (!taskEl) return;
|
|
297
|
+
var taskId = taskEl.dataset.id;
|
|
298
|
+
|
|
299
|
+
if (e.target.type === 'checkbox') {
|
|
300
|
+
window.myop_cta_handler('task-toggled', {
|
|
301
|
+
taskId: taskId,
|
|
302
|
+
completed: e.target.checked
|
|
303
|
+
});
|
|
304
|
+
} else if (e.target.dataset.action === 'delete') {
|
|
305
|
+
window.myop_cta_handler('task-deleted', { taskId: taskId });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
})();
|
|
309
|
+
</script>
|
|
310
|
+
|
|
311
|
+
<script id="myop_preview">
|
|
312
|
+
window.myop_init_interface({
|
|
313
|
+
tasks: [
|
|
314
|
+
{ id: '1', title: 'Review pull request', completed: false },
|
|
315
|
+
{ id: '2', title: 'Update documentation', completed: true },
|
|
316
|
+
{ id: '3', title: 'Deploy to staging', completed: false }
|
|
317
|
+
]
|
|
318
|
+
});
|
|
319
|
+
</script>
|
|
320
|
+
</body>
|
|
321
|
+
</html>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Host-Side Integration
|
|
325
|
+
|
|
326
|
+
For context on how the host application uses these functions (useful when debugging):
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// React host example using @myop/sdk
|
|
330
|
+
import { getHostModule } from '@myop/sdk';
|
|
331
|
+
|
|
332
|
+
const { hostSDK } = await getHostModule();
|
|
333
|
+
const component = await hostSDK.loadComponent(componentConfig, containerElement);
|
|
334
|
+
|
|
335
|
+
// Send data to component
|
|
336
|
+
component.props.myop_init_interface({
|
|
337
|
+
tasks: fetchedTasks
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Listen for component actions
|
|
341
|
+
component.props.myop_cta_handler = (action, payload) => {
|
|
342
|
+
switch (action) {
|
|
343
|
+
case 'task-toggled':
|
|
344
|
+
updateTask(payload.taskId, { completed: payload.completed });
|
|
345
|
+
break;
|
|
346
|
+
case 'task-deleted':
|
|
347
|
+
deleteTask(payload.taskId);
|
|
348
|
+
break;
|
|
349
|
+
case 'size-requested':
|
|
350
|
+
resizeContainer(payload);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// Read current component state
|
|
356
|
+
const currentState = component.props.myop_init_interface();
|
|
357
|
+
```
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Development Workflow & CLI Commands
|
|
2
|
+
|
|
3
|
+
The Myop CLI provides commands for creating, developing, and deploying components.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
- [CLI Installation](#cli-installation)
|
|
7
|
+
- [Commands Reference](#commands-reference)
|
|
8
|
+
- [Create Command](#create-command)
|
|
9
|
+
- [Dev Command](#dev-command)
|
|
10
|
+
- [Sync Command](#sync-command)
|
|
11
|
+
- [Authentication](#authentication)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
|
|
14
|
+
## CLI Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @myop/cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
After installation, the `myop` command is available globally.
|
|
21
|
+
|
|
22
|
+
## Commands Reference
|
|
23
|
+
|
|
24
|
+
| Command | Description |
|
|
25
|
+
|---------|-------------|
|
|
26
|
+
| `myop create` | Create a new component (interactive, starts dev server after) |
|
|
27
|
+
| `myop dev` | Start dev server with file watching and HMR |
|
|
28
|
+
| `myop push` | Upload component to Myop platform |
|
|
29
|
+
| `myop sync` | Build and upload component to Myop platform |
|
|
30
|
+
| `myop login` | Authenticate with Myop (opens browser for OAuth) |
|
|
31
|
+
| `myop logout` | Clear stored credentials |
|
|
32
|
+
| `myop whoami` | Show current authenticated user |
|
|
33
|
+
| `myop --ci` | Output status as JSON (for CI/CD) |
|
|
34
|
+
| `myop -m` / `myop --monorepo` | Monorepo mode: scan and dev multiple components |
|
|
35
|
+
|
|
36
|
+
## Create Command
|
|
37
|
+
|
|
38
|
+
Creates a new Myop component in the current directory.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
myop create
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**What it does:**
|
|
45
|
+
1. Prompts for component name (defaults to directory name)
|
|
46
|
+
2. Checks that `index.html` and `myop.config.json` don't already exist
|
|
47
|
+
3. Creates `myop.config.json` with component metadata
|
|
48
|
+
4. Creates `index.html` with a complete component template
|
|
49
|
+
5. Automatically starts the dev server
|
|
50
|
+
|
|
51
|
+
**Files created (single-file mode):**
|
|
52
|
+
```
|
|
53
|
+
./index.html # Complete Myop component
|
|
54
|
+
./myop.config.json # Component metadata
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Files created (full project scaffold, when no package.json exists):**
|
|
58
|
+
```
|
|
59
|
+
./index.html
|
|
60
|
+
./myop.config.json
|
|
61
|
+
./package.json
|
|
62
|
+
./build.js # esbuild bundler
|
|
63
|
+
./src/index.js
|
|
64
|
+
./src/modules/app.js
|
|
65
|
+
./src/modules/myop.js
|
|
66
|
+
./src/styles/index.css
|
|
67
|
+
./src/styles/main.css
|
|
68
|
+
./.gitignore
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Important:** If `index.html` or `myop.config.json` already exists, the command exits with an error. Use `myop dev` for existing components.
|
|
72
|
+
|
|
73
|
+
## Dev Command
|
|
74
|
+
|
|
75
|
+
Starts a development server with file watching.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
myop dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Features:**
|
|
82
|
+
- Main server on **port 9292**
|
|
83
|
+
- Management dashboard on **port 9293**
|
|
84
|
+
- Watches `.js` and `.css` files for changes
|
|
85
|
+
- Auto-rebuilds when files change (if `build.js` or `package.json` build script exists)
|
|
86
|
+
- Hot Module Replacement for instant preview updates
|
|
87
|
+
- Single HTML file mode (no build step needed for `index.html`-only components)
|
|
88
|
+
|
|
89
|
+
**URLs during development:**
|
|
90
|
+
| URL | Purpose |
|
|
91
|
+
|-----|---------|
|
|
92
|
+
| `http://localhost:9292` | Dev server dashboard |
|
|
93
|
+
| `http://localhost:9292/view/<componentId>/` | Preview your component |
|
|
94
|
+
|
|
95
|
+
**Monorepo mode:**
|
|
96
|
+
```bash
|
|
97
|
+
myop dev -m
|
|
98
|
+
```
|
|
99
|
+
Scans for all `myop.config.json` files in nested directories and lets you select which components to run simultaneously.
|
|
100
|
+
|
|
101
|
+
### Build Triggering
|
|
102
|
+
|
|
103
|
+
The dev server detects changes and triggers builds automatically:
|
|
104
|
+
- If `build.js` exists: runs `node build.js`
|
|
105
|
+
- If `package.json` has a `build` script: runs `npm run build`
|
|
106
|
+
- If only `index.html` exists (single-file mode): serves directly, no build needed
|
|
107
|
+
|
|
108
|
+
## Push Command
|
|
109
|
+
|
|
110
|
+
Uploads the component directly to the Myop platform (no build step).
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
myop push
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**What it does:**
|
|
117
|
+
1. Reads `myop.config.json`
|
|
118
|
+
2. Finds the HTML file to upload (`index.html` in root for single-file mode, or `dist/index.html`)
|
|
119
|
+
3. Authenticates (prompts for login if needed)
|
|
120
|
+
4. Uploads HTML to Myop via presigned URL
|
|
121
|
+
5. Confirms upload
|
|
122
|
+
6. Updates `myop.config.json` with `componentId` (first push only)
|
|
123
|
+
|
|
124
|
+
**After first push:**
|
|
125
|
+
- `myop.config.json` gets a real `componentId` (UUID)
|
|
126
|
+
- `organization` field is added
|
|
127
|
+
- Component appears on the dashboard
|
|
128
|
+
|
|
129
|
+
**Dashboard URL after push:**
|
|
130
|
+
```
|
|
131
|
+
https://dashboard.myop.dev/dashboard/2.0/component/<componentId>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Sync Command
|
|
135
|
+
|
|
136
|
+
Builds and uploads the component to the Myop platform (for multi-file projects with a build step).
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
myop sync
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**What it does:**
|
|
143
|
+
1. Reads `myop.config.json`
|
|
144
|
+
2. Runs `npm run build`
|
|
145
|
+
3. Verifies `dist/index.html` exists
|
|
146
|
+
4. Authenticates (prompts for login if needed)
|
|
147
|
+
5. Uploads `dist/index.html` to Myop via presigned URL
|
|
148
|
+
6. Confirms upload
|
|
149
|
+
7. Updates `myop.config.json` with `componentId` (first sync only)
|
|
150
|
+
|
|
151
|
+
## Authentication
|
|
152
|
+
|
|
153
|
+
### Login
|
|
154
|
+
```bash
|
|
155
|
+
myop login
|
|
156
|
+
```
|
|
157
|
+
Opens a browser window for OAuth authentication. Tokens are stored locally at `~/.myop/credentials.json`.
|
|
158
|
+
|
|
159
|
+
### Logout
|
|
160
|
+
```bash
|
|
161
|
+
myop logout
|
|
162
|
+
```
|
|
163
|
+
Clears stored credentials.
|
|
164
|
+
|
|
165
|
+
### Check Status
|
|
166
|
+
```bash
|
|
167
|
+
myop whoami
|
|
168
|
+
```
|
|
169
|
+
Displays the email of the currently authenticated user, or indicates if not logged in.
|
|
170
|
+
|
|
171
|
+
### Token Storage
|
|
172
|
+
- Location: `~/.myop/credentials.json`
|
|
173
|
+
- Permissions: `0o600` (owner read/write only)
|
|
174
|
+
- Tokens refresh automatically when expired
|
|
175
|
+
|
|
176
|
+
## Configuration
|
|
177
|
+
|
|
178
|
+
### myop.config.json
|
|
179
|
+
|
|
180
|
+
| Field | Type | Description |
|
|
181
|
+
|-------|------|-------------|
|
|
182
|
+
| `name` | `string` | Component display name |
|
|
183
|
+
| `componentId` | `string` | `"DEV"` initially, UUID after first sync |
|
|
184
|
+
| `type` | `string` | Always `"html"` |
|
|
185
|
+
| `author` | `string` | `"@myop-cli"` by default |
|
|
186
|
+
| `HMR` | `boolean` | Hot Module Replacement enabled |
|
|
187
|
+
| `organization` | `string` | Organization ID (added after sync) |
|
|
188
|
+
|
|
189
|
+
### Environment Variables
|
|
190
|
+
|
|
191
|
+
| Variable | Description |
|
|
192
|
+
|----------|-------------|
|
|
193
|
+
| `MYOP_MCP_URL` | MCP server URL (default: `https://mcp.myop.dev`) |
|
|
194
|
+
|
|
195
|
+
### CI/CD Mode
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
myop --ci
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Outputs JSON with version, config status, and auth status. No interactive prompts.
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"version": "0.1.40",
|
|
206
|
+
"config": {
|
|
207
|
+
"found": true,
|
|
208
|
+
"path": "./myop.config.json",
|
|
209
|
+
"name": "My Component",
|
|
210
|
+
"componentId": "abc-123",
|
|
211
|
+
"organization": "org-456"
|
|
212
|
+
},
|
|
213
|
+
"auth": {
|
|
214
|
+
"loggedIn": true,
|
|
215
|
+
"email": "user@example.com"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|