@juuno-sdk/cli 1.0.12 → 2.0.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.
- package/README.md +5 -3
- package/bin/cli-router.js +54 -72
- package/bin/cli.js +2 -3
- package/dist/assets/main-GRbDNSIa.css +1 -0
- package/dist/assets/main-TDlmPBlt.js +1 -0
- package/dist/assets/main-rFvAfxK4.js +1 -0
- package/dist/cli/src/auth/index.js +47 -34
- package/dist/cli/src/deploy/index.js +209 -115
- package/dist/cli/src/dev/index.js +46 -40
- package/dist/cli/src/info/index.js +12 -36
- package/dist/cli/src/list/index.js +12 -36
- package/dist/cli/src/login/index.js +37 -42
- package/dist/cli/src/logout/index.js +34 -33
- package/dist/cli/src/simulator/index.js +54 -39
- package/dist/cli/src/utils/api.js +45 -0
- package/dist/cli/src/utils/error.js +9 -0
- package/dist/cli/src/utils/is-record.js +11 -0
- package/dist/cli/src/whoami/index.js +37 -33
- package/dist/cli/tsconfig.build.tsbuildinfo +1 -1
- package/dist/index.html +5 -2
- package/dist/types/src/auth/index.d.ts +17 -0
- package/dist/types/src/auth/index.d.ts.map +1 -1
- package/dist/types/src/deploy/index.d.ts +12 -0
- package/dist/types/src/deploy/index.d.ts.map +1 -1
- package/dist/types/src/dev/index.d.ts.map +1 -1
- package/dist/types/src/info/index.d.ts.map +1 -1
- package/dist/types/src/list/index.d.ts.map +1 -1
- package/dist/types/src/login/index.d.ts.map +1 -1
- package/dist/types/src/logout/index.d.ts.map +1 -1
- package/dist/types/src/simulator/index.d.ts.map +1 -1
- package/dist/types/src/utils/api.d.ts +27 -0
- package/dist/types/src/utils/api.d.ts.map +1 -0
- package/dist/types/src/utils/error.d.ts +8 -0
- package/dist/types/src/utils/error.d.ts.map +1 -0
- package/dist/types/src/utils/is-record.d.ts +10 -0
- package/dist/types/src/utils/is-record.d.ts.map +1 -0
- package/dist/types/src/whoami/index.d.ts.map +1 -1
- package/package.json +28 -26
package/README.md
CHANGED
|
@@ -478,12 +478,14 @@ If you get authentication errors:
|
|
|
478
478
|
|
|
479
479
|
## Example
|
|
480
480
|
|
|
481
|
-
See [
|
|
481
|
+
See [`SKILL.md`](../app-sdk/SKILL.md) in `@juuno-sdk/app-sdk` for the full
|
|
482
|
+
workflow: creating an app, publishing a version, and upgrading the contract
|
|
483
|
+
version, with the preflight checks and failure modes for each.
|
|
482
484
|
|
|
483
485
|
## Related Packages
|
|
484
486
|
|
|
485
|
-
- **@juuno-sdk/app-sdk**: SDK for building external apps
|
|
486
|
-
|
|
487
|
+
- **@juuno-sdk/app-sdk**: SDK for building external apps, and the `SKILL.md`
|
|
488
|
+
walkthrough of the whole lifecycle
|
|
487
489
|
|
|
488
490
|
## Package Exports
|
|
489
491
|
|
package/bin/cli-router.js
CHANGED
|
@@ -22,6 +22,56 @@ export function parseAuthOptions(args) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Per-command dispatch table. adding a command is one entry.
|
|
27
|
+
*/
|
|
28
|
+
const COMMANDS = {
|
|
29
|
+
login: async (args, importPrefix) => {
|
|
30
|
+
const { login } = await import(`${importPrefix}login/index.js`);
|
|
31
|
+
const auth = parseAuthOptions(args);
|
|
32
|
+
await login({
|
|
33
|
+
email: auth.email,
|
|
34
|
+
password: auth.password,
|
|
35
|
+
profile: auth.profile,
|
|
36
|
+
apiUrl: auth.apiUrl,
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
logout: async (args, importPrefix) => {
|
|
40
|
+
const { logout } = await import(`${importPrefix}logout/index.js`);
|
|
41
|
+
const auth = parseAuthOptions(args);
|
|
42
|
+
await logout({ profile: auth.profile, all: args.includes('--all') });
|
|
43
|
+
},
|
|
44
|
+
whoami: async (args, importPrefix) => {
|
|
45
|
+
const { whoami } = await import(`${importPrefix}whoami/index.js`);
|
|
46
|
+
const auth = parseAuthOptions(args);
|
|
47
|
+
await whoami({ profile: auth.profile, all: args.includes('--all') });
|
|
48
|
+
},
|
|
49
|
+
deploy: async (args, importPrefix) => {
|
|
50
|
+
const { deployApp } = await import(`${importPrefix}deploy/index.js`);
|
|
51
|
+
const buildDirIndex = args.indexOf('--build-dir');
|
|
52
|
+
const buildDir = buildDirIndex !== -1 ? args[buildDirIndex + 1] : './dist';
|
|
53
|
+
await deployApp({
|
|
54
|
+
buildDir,
|
|
55
|
+
skipIfDeployed: args.includes('--skip-if-deployed'),
|
|
56
|
+
...parseAuthOptions(args),
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
list: async (args, importPrefix) => {
|
|
60
|
+
const { listApps } = await import(`${importPrefix}list/index.js`);
|
|
61
|
+
await listApps(parseAuthOptions(args));
|
|
62
|
+
},
|
|
63
|
+
info: async (args, importPrefix) => {
|
|
64
|
+
const { getAppInfo } = await import(`${importPrefix}info/index.js`);
|
|
65
|
+
const appId = args[1];
|
|
66
|
+
if (!appId || appId.startsWith('--')) {
|
|
67
|
+
console.error('❌ App ID is required');
|
|
68
|
+
console.error(' Usage: juuno-cli info <app-id> [options]');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
await getAppInfo({ appId, ...parseAuthOptions(args) });
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
25
75
|
/**
|
|
26
76
|
* Execute a CLI command with the given import prefix.
|
|
27
77
|
*
|
|
@@ -31,78 +81,10 @@ export function parseAuthOptions(args) {
|
|
|
31
81
|
* @returns {Promise<number>} Exit code (0 for success, 1 for error)
|
|
32
82
|
*/
|
|
33
83
|
export async function runCommand(command, args, importPrefix) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (command === 'login') {
|
|
37
|
-
const { login } = await import(`${importPrefix}login/index.js`);
|
|
38
|
-
const authOptions = parseAuthOptions(args);
|
|
39
|
-
await login({
|
|
40
|
-
email: authOptions.email,
|
|
41
|
-
password: authOptions.password,
|
|
42
|
-
profile: authOptions.profile,
|
|
43
|
-
apiUrl: authOptions.apiUrl,
|
|
44
|
-
});
|
|
45
|
-
return 0;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Handle logout command.
|
|
49
|
-
if (command === 'logout') {
|
|
50
|
-
const { logout } = await import(`${importPrefix}logout/index.js`);
|
|
51
|
-
const authOptions = parseAuthOptions(args);
|
|
52
|
-
const all = args.includes('--all');
|
|
53
|
-
await logout({ profile: authOptions.profile, all });
|
|
54
|
-
return 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Handle whoami command.
|
|
58
|
-
if (command === 'whoami') {
|
|
59
|
-
const { whoami } = await import(`${importPrefix}whoami/index.js`);
|
|
60
|
-
const authOptions = parseAuthOptions(args);
|
|
61
|
-
const all = args.includes('--all');
|
|
62
|
-
await whoami({ profile: authOptions.profile, all });
|
|
63
|
-
return 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Handle deploy command.
|
|
67
|
-
if (command === 'deploy') {
|
|
68
|
-
const { deployApp } = await import(`${importPrefix}deploy/index.js`);
|
|
69
|
-
const buildDirIndex = args.indexOf('--build-dir');
|
|
70
|
-
const buildDir =
|
|
71
|
-
buildDirIndex !== -1 ? args[buildDirIndex + 1] : './dist';
|
|
72
|
-
const skipIfDeployed = args.includes('--skip-if-deployed');
|
|
73
|
-
const authOptions = parseAuthOptions(args);
|
|
74
|
-
await deployApp({ buildDir, skipIfDeployed, ...authOptions });
|
|
75
|
-
return 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Handle list command.
|
|
79
|
-
if (command === 'list') {
|
|
80
|
-
const { listApps } = await import(`${importPrefix}list/index.js`);
|
|
81
|
-
const authOptions = parseAuthOptions(args);
|
|
82
|
-
await listApps(authOptions);
|
|
83
|
-
return 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Handle info command.
|
|
87
|
-
if (command === 'info') {
|
|
88
|
-
const { getAppInfo } = await import(`${importPrefix}info/index.js`);
|
|
89
|
-
const appId = args[1];
|
|
90
|
-
|
|
91
|
-
if (!appId || appId.startsWith('--')) {
|
|
92
|
-
console.error('Error: App ID is required');
|
|
93
|
-
console.error('Usage: juuno-cli info <app-id> [options]');
|
|
94
|
-
return 1;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const authOptions = parseAuthOptions(args);
|
|
98
|
-
await getAppInfo({ appId, ...authOptions });
|
|
99
|
-
return 0;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Command not recognized - return error code.
|
|
84
|
+
const handler = COMMANDS[command];
|
|
85
|
+
if (!handler) {
|
|
103
86
|
return 1;
|
|
104
|
-
} catch (error) {
|
|
105
|
-
// Let errors propagate to the caller for proper handling.
|
|
106
|
-
throw error;
|
|
107
87
|
}
|
|
88
|
+
await handler(args, importPrefix);
|
|
89
|
+
return 0;
|
|
108
90
|
}
|
package/bin/cli.js
CHANGED
|
@@ -164,9 +164,8 @@ if (appUrlIndex !== -1) {
|
|
|
164
164
|
process.exit(1);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
const { launchSimulator } =
|
|
168
|
-
'../dist/cli/src/simulator/index.js'
|
|
169
|
-
);
|
|
167
|
+
const { launchSimulator } =
|
|
168
|
+
await import('../dist/cli/src/simulator/index.js');
|
|
170
169
|
launchSimulator(appUrl, port, simulatorDistPath);
|
|
171
170
|
} else {
|
|
172
171
|
// Unified dev mode: auto-detect app and start everything.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[data-v-d3c29943]{box-sizing:border-box}.simulator-layout[data-v-d3c29943]{color:#1a1a2e;background-color:#f8f9fa;flex-direction:column;min-height:100vh;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;transition:background-color .2s,color .2s;display:flex}.simulator-layout.dark-mode[data-v-d3c29943]{color:#e8e8e8;background-color:#1a1a2e}.header[data-v-d3c29943]{color:#fff;background:linear-gradient(135deg,#6366f1 0%,#8b5cf6 100%);justify-content:space-between;align-items:center;padding:.75rem 1.5rem;display:flex}.header-left[data-v-d3c29943]{align-items:center;gap:1.5rem;display:flex}.header h1[data-v-d3c29943]{letter-spacing:-.02em;margin:0;font-size:1.125rem;font-weight:600}.app-info[data-v-d3c29943]{background:#ffffff26;border-radius:4px;align-items:center;gap:.5rem;padding:.25rem .75rem;font-size:.8125rem;display:flex}.app-name[data-v-d3c29943]{font-weight:500}.app-version[data-v-d3c29943]{opacity:.8;font-size:.75rem}.header-right[data-v-d3c29943]{align-items:center;gap:.75rem;display:flex}.status-badge[data-v-d3c29943]{border-radius:999px;align-items:center;gap:.375rem;padding:.25rem .625rem;font-size:.75rem;font-weight:500;display:flex}.status-badge.live[data-v-d3c29943]{color:#86efac;background:#22c55e33}.status-dot[data-v-d3c29943]{background:#22c55e;border-radius:50%;width:6px;height:6px;animation:2s infinite pulse-d3c29943}@keyframes pulse-d3c29943{0%,to{opacity:1}50%{opacity:.5}}.icon-btn[data-v-d3c29943]{color:#fff;cursor:pointer;background:#ffffff1a;border:none;border-radius:6px;justify-content:center;align-items:center;width:32px;height:32px;transition:background .15s;display:flex}.icon-btn[data-v-d3c29943]:hover{background:#fff3}.loading-state[data-v-d3c29943],.error-state[data-v-d3c29943]{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;padding:3rem;display:flex}.spinner[data-v-d3c29943]{border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;width:40px;height:40px;animation:.8s linear infinite spin-d3c29943}.dark-mode .spinner[data-v-d3c29943]{border-color:#8b5cf6 #374151 #374151}@keyframes spin-d3c29943{to{transform:rotate(360deg)}}.url-info[data-v-d3c29943]{color:#6b7280;margin-top:.75rem;font-family:SF Mono,Monaco,monospace;font-size:.8125rem}.error-icon[data-v-d3c29943]{color:#dc2626;background:#fef2f2;border-radius:50%;justify-content:center;align-items:center;width:48px;height:48px;margin-bottom:1rem;font-size:1.5rem;font-weight:700;display:flex}.dark-mode .error-icon[data-v-d3c29943]{background:#450a0a}.error-message[data-v-d3c29943]{color:#dc2626;margin:.5rem 0;font-weight:500}.error-details[data-v-d3c29943]{background:#fff;border-radius:8px;margin-top:1.5rem;padding:1rem;font-size:.875rem}.dark-mode .error-details[data-v-d3c29943]{background:#1f2937}.retry-btn[data-v-d3c29943]{color:#fff;cursor:pointer;background:#6366f1;border:none;border-radius:6px;margin-top:1rem;padding:.5rem 1rem;font-weight:500;transition:background .15s}.retry-btn[data-v-d3c29943]:hover{background:#4f46e5}.main-content[data-v-d3c29943]{flex-direction:column;flex:1;gap:1rem;padding:1rem;display:flex}.tabs[data-v-d3c29943]{background:#fff;border-radius:8px;align-items:center;gap:.25rem;padding:.25rem;display:flex;box-shadow:0 1px 2px #0000000d}.dark-mode .tabs[data-v-d3c29943]{background:#1f2937}.tab[data-v-d3c29943]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:6px;padding:.5rem 1rem;font-size:.875rem;font-weight:500;transition:all .15s}.tab[data-v-d3c29943]:hover{color:#1f2937}.dark-mode .tab[data-v-d3c29943]:hover{color:#e5e7eb}.tab.active[data-v-d3c29943]{color:#fff;background:#6366f1}.tab-controls[data-v-d3c29943]{gap:.25rem;margin-left:auto;display:flex}.control-btn[data-v-d3c29943]{color:#9ca3af;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:36px;height:32px;transition:all .15s;display:flex}.control-btn[data-v-d3c29943]:hover{color:#4b5563;background:#f3f4f6}.dark-mode .control-btn[data-v-d3c29943]:hover{color:#d1d5db;background:#374151}.control-btn.active[data-v-d3c29943]{color:#6366f1;background:#ede9fe}.dark-mode .control-btn.active[data-v-d3c29943]{color:#c4b5fd;background:#4c1d95}.tab-panels[data-v-d3c29943]{flex-direction:column;flex:1;display:flex}.tab-panel[data-v-d3c29943]{background:#fff;border-radius:8px;flex:1;overflow:auto;box-shadow:0 1px 3px #0000000d}.dark-mode .tab-panel[data-v-d3c29943]{background:#1f2937}.player-panel[data-v-d3c29943]{background:#111827;flex-direction:column;display:flex}.player-panel.fullscreen[data-v-d3c29943]{z-index:1000;border-radius:0;padding:0;position:fixed;inset:0}.player-container[data-v-d3c29943]{flex:1;transition:all .3s;display:flex}.player-container.aspect-landscape[data-v-d3c29943]{aspect-ratio:16/9}.player-container.aspect-portrait[data-v-d3c29943]{aspect-ratio:9/16}.fullscreen .player-container[data-v-d3c29943]{width:100%;height:100%}.fullscreen .player-container.aspect-portrait[data-v-d3c29943]{width:auto;height:100%}.player-frame[data-v-d3c29943]{background:#000;flex:1;position:relative;overflow:hidden}.player-frame[data-v-d3c29943]>*{width:100%;height:100%;position:absolute;inset:0}.exit-fullscreen[data-v-d3c29943]{color:#fff;cursor:pointer;z-index:1001;background:#000000bf;border:none;border-radius:6px;padding:.5rem 1rem;font-size:.875rem;position:fixed;top:1rem;right:1rem}.exit-fullscreen[data-v-d3c29943]:hover{background:#000000e6}.player-controls[data-v-d3c29943]{background:#ffffff1a;justify-content:center;align-items:center;gap:1.5rem;padding:.75rem 1rem;display:flex}.fullscreen .player-controls[data-v-d3c29943]{z-index:1001;background:#000000bf;position:fixed;bottom:1rem;left:50%;transform:translate(-50%)}.controls-group[data-v-d3c29943]{gap:.5rem;display:flex}.player-btn[data-v-d3c29943]{color:#e5e7eb;cursor:pointer;background:#ffffff1a;border:none;border-radius:8px;justify-content:center;align-items:center;width:40px;height:40px;transition:all .15s;display:flex}.player-btn[data-v-d3c29943]:hover{color:#fff;background:#fff3}.player-btn.active[data-v-d3c29943]{color:#fff;background:#6366f1}.controls-info[data-v-d3c29943]{gap:.5rem;display:flex}.state-badge[data-v-d3c29943]{color:#86efac;background:#22c55e33;border-radius:4px;padding:.25rem .5rem;font-size:.75rem;font-weight:500}.state-badge.inactive[data-v-d3c29943]{color:#fca5a5;background:#ef444433}.config-panel[data-v-d3c29943]{padding:1.5rem;overflow-y:auto}.meta-panel[data-v-d3c29943]{padding:1rem}.meta-header[data-v-d3c29943]{justify-content:space-between;align-items:center;margin-bottom:.75rem;display:flex}.meta-header h3[data-v-d3c29943]{color:#6b7280;margin:0;font-size:.875rem;font-weight:600}.copy-btn[data-v-d3c29943]{color:#4b5563;cursor:pointer;background:#f3f4f6;border:none;border-radius:4px;padding:.25rem .5rem;font-size:.75rem}.dark-mode .copy-btn[data-v-d3c29943]{color:#d1d5db;background:#374151}.copy-btn[data-v-d3c29943]:hover{background:#e5e7eb}.dark-mode .copy-btn[data-v-d3c29943]:hover{background:#4b5563}.meta-code[data-v-d3c29943]{white-space:pre-wrap;word-break:break-word;background:#f9fafb;border-radius:6px;margin:0;padding:1rem;font-family:SF Mono,Monaco,Courier New,monospace;font-size:.8125rem;line-height:1.6;overflow-x:auto}.dark-mode .meta-code[data-v-d3c29943]{color:#e5e7eb;background:#111827}.placeholder[data-v-d3c29943]{color:#9ca3af;justify-content:center;align-items:center;height:100%;min-height:200px;font-style:italic;display:flex}@media (width<=640px){.header[data-v-d3c29943]{padding:.5rem 1rem}.header h1[data-v-d3c29943]{font-size:1rem}.app-info[data-v-d3c29943]{display:none}.tabs[data-v-d3c29943]{flex-wrap:wrap}.tab-controls[data-v-d3c29943]{justify-content:center;width:100%;margin-top:.5rem;margin-left:0}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{computed as e,createApp as t,createBlock as n,createCommentVNode as r,createElementBlock as i,createElementVNode as a,createTextVNode as o,defineComponent as s,markRaw as c,normalizeClass as l,onMounted as u,onUnmounted as d,openBlock as f,reactive as p,ref as m,resolveDynamicComponent as h,shallowRef as g,toDisplayString as _,unref as v,vShow as y,withDirectives as b}from"vue";import{provideAppConfigContext as x}from"@juuno-sdk/app-sdk";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var S={class:`header`},C={class:`header-left`},w={class:`app-info`},T={class:`app-name`},E={key:0,class:`app-version`},D={class:`header-right`},ee={key:0,class:`status-badge live`},O={key:0,width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},te={key:1,width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},ne={key:0,class:`loading-state`},re={class:`url-info`},ie={key:1,class:`error-state`},ae={class:`error-message`},oe={class:`error-details`},se={key:2,class:`main-content`},ce={class:`tabs`},le={key:0,class:`tab-controls`},k={class:`tab-panels`},A={class:`player-frame`},j={key:1,class:`placeholder`},M={class:`player-controls`},N={class:`controls-group`},P=[`title`],F={key:0,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`currentColor`},I={key:1,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`currentColor`},L=[`title`],R={key:0,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},z={key:1,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},B=[`title`],V={key:0,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},H={key:1,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},U={class:`controls-info`},ue={class:`tab-panel config-panel`},de={key:1,class:`placeholder`},fe={class:`tab-panel meta-panel`},pe={class:`meta-code`},W=((e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n})(s({name:`SimulatorLayout`,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:[`reload`],setup(t,{emit:s}){let c=t,p=s,g=m(`player`),v=m(`landscape`),x=m(!1),W=m(!1),G=m(!0),K=m(!0),q=m(!1),J=m(3e4),Y=e(()=>c.manifest?.name||`External App`),X=e(()=>c.manifest?.version||``),me=e(()=>v.value===`landscape`?`aspect-landscape`:`aspect-portrait`),Z=e(()=>{try{return JSON.stringify(c.scene?.meta||{},null,2)}catch{return`{}`}});function Q(){p(`reload`)}async function he(){try{await navigator.clipboard.writeText(Z.value)}catch{console.warn(`Failed to copy to clipboard`)}}function $(e){e.key===`Escape`&&x.value&&(x.value=!1)}return u(()=>{window.addEventListener(`keydown`,$)}),d(()=>{window.removeEventListener(`keydown`,$)}),(e,s)=>(f(),i(`div`,{class:l([`simulator-layout`,{"dark-mode":W.value}])},[a(`header`,S,[a(`div`,C,[s[11]||=a(`h1`,null,`Juuno Simulator`,-1),a(`div`,w,[a(`span`,T,_(Y.value),1),X.value?(f(),i(`span`,E,`v`+_(X.value),1)):r(``,!0)])]),a(`div`,D,[t.liveReloadActive?(f(),i(`div`,ee,[...s[12]||=[a(`span`,{class:`status-dot`},null,-1),o(` Live `,-1)]])):r(``,!0),a(`button`,{class:`icon-btn`,title:`Reload app`,onClick:Q},[...s[13]||=[a(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[a(`path`,{d:`M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15`})],-1)]]),a(`button`,{class:`icon-btn`,title:`Toggle dark mode`,onClick:s[0]||=e=>W.value=!W.value},[W.value?(f(),i(`svg`,te,[...s[15]||=[a(`path`,{d:`M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z`},null,-1)]])):(f(),i(`svg`,O,[...s[14]||=[a(`circle`,{cx:`12`,cy:`12`,r:`5`},null,-1),a(`path`,{d:`M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42`},null,-1)]]))])])]),t.isLoading?(f(),i(`div`,ne,[s[16]||=a(`div`,{class:`spinner`},null,-1),s[17]||=a(`p`,null,`Loading app...`,-1),a(`p`,re,_(t.appUrl),1)])):t.error?(f(),i(`div`,ie,[s[19]||=a(`div`,{class:`error-icon`},`!`,-1),s[20]||=a(`h2`,null,`Failed to Load App`,-1),a(`p`,ae,_(t.error),1),a(`div`,oe,[a(`p`,null,[s[18]||=a(`strong`,null,`App URL:`,-1),o(` `+_(t.appUrl),1)]),a(`button`,{class:`retry-btn`,onClick:Q},`Try Again`)])])):(f(),i(`div`,se,[a(`div`,ce,[a(`button`,{class:l([`tab`,{active:g.value===`player`}]),onClick:s[1]||=e=>g.value=`player`},` Player `,2),a(`button`,{class:l([`tab`,{active:g.value===`config`}]),onClick:s[2]||=e=>g.value=`config`},` Config `,2),a(`button`,{class:l([`tab`,{active:g.value===`meta`}]),onClick:s[3]||=e=>g.value=`meta`},` Scene Meta `,2),g.value===`player`?(f(),i(`div`,le,[a(`button`,{class:l([`control-btn`,{active:v.value===`landscape`}]),title:`Landscape (16:9)`,onClick:s[4]||=e=>v.value=`landscape`},[...s[21]||=[a(`svg`,{width:`18`,height:`12`,viewBox:`0 0 18 12`,fill:`currentColor`},[a(`rect`,{x:`0.5`,y:`0.5`,width:`17`,height:`11`,rx:`1`,fill:`none`,stroke:`currentColor`})],-1)]],2),a(`button`,{class:l([`control-btn`,{active:v.value===`portrait`}]),title:`Portrait (9:16)`,onClick:s[5]||=e=>v.value=`portrait`},[...s[22]||=[a(`svg`,{width:`12`,height:`18`,viewBox:`0 0 12 18`,fill:`currentColor`},[a(`rect`,{x:`0.5`,y:`0.5`,width:`11`,height:`17`,rx:`1`,fill:`none`,stroke:`currentColor`})],-1)]],2),a(`button`,{class:l([`control-btn`,{active:x.value}]),title:`Fullscreen (Esc to exit)`,onClick:s[6]||=e=>x.value=!x.value},[...s[23]||=[a(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[a(`path`,{d:`M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3`})],-1)]],2)])):r(``,!0)]),a(`div`,k,[b(a(`div`,{class:l([`tab-panel player-panel`,{fullscreen:x.value}])},[a(`div`,{class:l([`player-container`,me.value])},[a(`div`,A,[t.playerComponent?(f(),n(h(t.playerComponent),{key:t.scene.idx,scene:t.scene,playing:G.value,"is-visible":K.value,"duration-ms":J.value,muted:q.value},null,8,[`scene`,`playing`,`is-visible`,`duration-ms`,`muted`])):(f(),i(`div`,j,`No player component`))])],2),a(`div`,M,[a(`div`,N,[a(`button`,{class:l([`player-btn`,{active:G.value}]),title:G.value?`Pause`:`Play`,onClick:s[7]||=e=>G.value=!G.value},[G.value?(f(),i(`svg`,F,[...s[24]||=[a(`rect`,{x:`6`,y:`4`,width:`4`,height:`16`},null,-1),a(`rect`,{x:`14`,y:`4`,width:`4`,height:`16`},null,-1)]])):(f(),i(`svg`,I,[...s[25]||=[a(`polygon`,{points:`5,3 19,12 5,21`},null,-1)]]))],10,P),a(`button`,{class:l([`player-btn`,{active:!K.value}]),title:K.value?`Hide (simulate off-screen)`:`Show`,onClick:s[8]||=e=>K.value=!K.value},[K.value?(f(),i(`svg`,R,[...s[26]||=[a(`path`,{d:`M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z`},null,-1),a(`circle`,{cx:`12`,cy:`12`,r:`3`},null,-1)]])):(f(),i(`svg`,z,[...s[27]||=[a(`path`,{d:`M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24`},null,-1),a(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`},null,-1)]]))],10,L),a(`button`,{class:l([`player-btn`,{active:q.value}]),title:q.value?`Unmute`:`Mute`,onClick:s[9]||=e=>q.value=!q.value},[q.value?(f(),i(`svg`,H,[...s[29]||=[a(`polygon`,{points:`11,5 6,9 2,9 2,15 6,15 11,19`},null,-1),a(`line`,{x1:`23`,y1:`9`,x2:`17`,y2:`15`},null,-1),a(`line`,{x1:`17`,y1:`9`,x2:`23`,y2:`15`},null,-1)]])):(f(),i(`svg`,V,[...s[28]||=[a(`polygon`,{points:`11,5 6,9 2,9 2,15 6,15 11,19`},null,-1),a(`path`,{d:`M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07`},null,-1)]]))],10,B)]),a(`div`,U,[a(`span`,{class:l([`state-badge`,{inactive:!G.value}])},_(G.value?`Playing`:`Paused`),3),a(`span`,{class:l([`state-badge`,{inactive:!K.value}])},_(K.value?`Visible`:`Hidden`),3),a(`span`,{class:l([`state-badge`,{inactive:!q.value}])},_(q.value?`Muted`:`Sound On`),3)])]),x.value?(f(),i(`button`,{key:0,class:`exit-fullscreen`,onClick:s[10]||=e=>x.value=!1},` Exit Fullscreen `)):r(``,!0)],2),[[y,g.value===`player`]]),b(a(`div`,ue,[t.configComponent?(f(),n(h(t.configComponent),{key:`config-`+t.scene.idx})):(f(),i(`div`,de,`No config component`))],512),[[y,g.value===`config`]]),b(a(`div`,fe,[a(`div`,{class:`meta-header`},[s[30]||=a(`h3`,null,`scene.meta`,-1),a(`button`,{class:`copy-btn`,onClick:he},`Copy`)]),a(`pre`,pe,_(Z.value),1)],512),[[y,g.value===`meta`]])])]))],2))}}),[[`__scopeId`,`data-v-d3c29943`]]);function G(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var K=`modulepreload`,q=function(e){return`/`+e},J={},Y=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=q(t,n),t=s(t),t in J)return;J[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:K,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};t(s({name:`JuunoCLI`,setup(e){let t=m(window.__JUUNO_EXTERNAL_APP_URL__||`http://localhost:5002/index.js`),r=m(!1),i=m(null),a=[],o=g(null),s=g(null),l=m(!0),d=m(null),h=p({idx:`simulator-scene-1`,name:`Simulator Test Scene`,app:null,app_idx:`simulator-app-1`,created_at:new Date().toISOString(),display_time:5e3,language:`en`,order:0,playlist_idx:`simulator-playlist-1`,updated_at:new Date().toISOString(),translations:{},meta:{title:`Test App`,backgroundColor:`#2c3e50`,textColor:`#ecf0f1`},mediaAssets:[]}),_=x({async save(e){console.log(`Simulator: Scene updated`,e),Object.assign(h,e)}},{scene:h});async function y(){try{let e=await fetch(`${t.value}/manifest.json`);if(e.ok){let t=await e.json();return i.value=t,t}}catch{}return i.value=null,null}function b(e){a.forEach(e=>e.remove()),a.length=0,e.forEach(e=>{let n=document.createElement(`link`);n.rel=`stylesheet`,n.href=new URL(e,t.value).toString(),document.head.appendChild(n),a.push(n)})}function S(e){if(!G(e))return null;let t=e;return t.default?t.default:typeof t.render==`function`?t.render():null}function C(e){let t=[];for(let n of[e?.resources?.player,e?.resources?.config])n&&t.push(...n.filter(e=>e.endsWith(`.css`)));return t}async function w(){let e=S(await Y(()=>import(`external-app/player`),[]));if(!e)throw Error(`External app must export a default component or render function from /player`);o.value=c(e)}async function T(){try{let e=S(await Y(()=>import(`external-app/config`),[]));s.value=e?c(e):null}catch(e){console.warn(`Optional config module could not be loaded:`,e),s.value=null}}async function E(){try{l.value=!0,d.value=null,console.log(`Loading external app from: ${t.value}`);let e=C(await y());e.length>0&&(console.log(`Loading CSS files:`,e),b(e)),await w(),await T(),_.load(h),console.log(`✅ External app loaded successfully`),l.value=!1}catch(e){console.error(`Failed to load external app:`,e),d.value=e instanceof Error?e.message:`Failed to load external app`,l.value=!1}}function D(){console.log(`🔄 Reloading app...`),window.location.reload()}return u(E),(e,a)=>(f(),n(v(W),{"player-component":o.value,"config-component":s.value,scene:h,"app-url":t.value,"is-loading":l.value,error:d.value,manifest:i.value,"live-reload-active":r.value,onReload:D},null,8,[`player-component`,`config-component`,`scene`,`app-url`,`is-loading`,`error`,`manifest`,`live-reload-active`]))}})).mount(`#app`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defineComponent as I,ref as g,computed as A,onMounted as J,onUnmounted as z,createElementBlock as u,openBlock as c,normalizeClass as p,createElementVNode as t,createCommentVNode as P,toDisplayString as C,createTextVNode as T,withDirectives as R,createBlock as $,resolveDynamicComponent as F,vShow as B,shallowRef as N,reactive as H,unref as K,markRaw as j,createApp as W}from"vue";import{provideAppConfigContext as X}from"@juuno-sdk/app-sdk";(function(){const f=document.createElement("link").relList;if(f&&f.supports&&f.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))w(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&w(r)}).observe(document,{childList:!0,subtree:!0});function m(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function w(n){if(n.ep)return;n.ep=!0;const i=m(n);fetch(n.href,i)}})();const G="modulepreload",Q=function(l){return"/"+l},V={},D=function(f,m,w){let n=Promise.resolve();if(m&&m.length>0){let r=function(s){return Promise.all(s.map(d=>Promise.resolve(d).then(k=>({status:"fulfilled",value:k}),k=>({status:"rejected",reason:k}))))};document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),v=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));n=r(m.map(s=>{if(s=Q(s),s in V)return;V[s]=!0;const d=s.endsWith(".css"),k=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${k}`))return;const h=document.createElement("link");if(h.rel=d?"stylesheet":G,d||(h.as="script"),h.crossOrigin="",h.href=s,v&&h.setAttribute("nonce",v),document.head.appendChild(h),d)return new Promise((x,_)=>{h.addEventListener("load",x),h.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${s}`)))})}))}function i(r){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=r,window.dispatchEvent(a),!a.defaultPrevented)throw r}return n.then(r=>{for(const a of r||[])a.status==="rejected"&&i(a.reason);return f().catch(i)})},Y={class:"header"},Z={class:"header-left"},ee={class:"app-info"},te={class:"app-name"},oe={key:0,class:"app-version"},ne={class:"header-right"},le={key:0,class:"status-badge live"},se={key:0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},ie={key:1,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},re={key:0,class:"loading-state"},ae={class:"url-info"},ce={key:1,class:"error-state"},ue={class:"error-message"},de={class:"error-details"},ve={key:2,class:"main-content"},pe={class:"tabs"},fe={key:0,class:"tab-controls"},me={class:"tab-panels"},ye={class:"player-frame"},ge={key:1,class:"placeholder"},he={class:"player-controls"},we={class:"controls-group"},ke=["title"],Ce={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},be={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},xe=["title"],_e={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Ee={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Le=["title"],Se={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Ae={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Pe={class:"controls-info"},Me={class:"tab-panel config-panel"},Re={key:1,class:"placeholder"},Be={class:"tab-panel meta-panel"},$e={class:"meta-code"},Oe={name:"SimulatorLayout"},Ue=I({...Oe,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:["reload"],setup(l,{emit:f}){const m=l,w=f,n=g("player"),i=g("landscape"),r=g(!1),a=g(!1),v=g(!0),s=g(!0),d=g(!1),k=g(3e4),h=A(()=>{var y;return((y=m.manifest)==null?void 0:y.name)||"External App"}),x=A(()=>{var y;return((y=m.manifest)==null?void 0:y.version)||""}),_=A(()=>i.value==="landscape"?"aspect-landscape":"aspect-portrait"),E=A(()=>{var y;try{return JSON.stringify(((y=m.scene)==null?void 0:y.meta)||{},null,2)}catch{return"{}"}});function L(){w("reload")}async function M(){try{await navigator.clipboard.writeText(E.value)}catch{console.warn("Failed to copy to clipboard")}}function S(y){y.key==="Escape"&&r.value&&(r.value=!1)}return J(()=>{window.addEventListener("keydown",S)}),z(()=>{window.removeEventListener("keydown",S)}),(y,e)=>(c(),u("div",{class:p(["simulator-layout",{"dark-mode":a.value}])},[t("header",Y,[t("div",Z,[e[11]||(e[11]=t("h1",null,"Juuno Simulator",-1)),t("div",ee,[t("span",te,C(h.value),1),x.value?(c(),u("span",oe,"v"+C(x.value),1)):P("",!0)])]),t("div",ne,[l.liveReloadActive?(c(),u("div",le,[...e[12]||(e[12]=[t("span",{class:"status-dot"},null,-1),T(" Live ",-1)])])):P("",!0),t("button",{class:"icon-btn",title:"Reload app",onClick:L},[...e[13]||(e[13]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"})],-1)])]),t("button",{class:"icon-btn",title:"Toggle dark mode",onClick:e[0]||(e[0]=o=>a.value=!a.value)},[a.value?(c(),u("svg",ie,[...e[15]||(e[15]=[t("path",{d:"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"},null,-1)])])):(c(),u("svg",se,[...e[14]||(e[14]=[t("circle",{cx:"12",cy:"12",r:"5"},null,-1),t("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"},null,-1)])]))])])]),l.isLoading?(c(),u("div",re,[e[16]||(e[16]=t("div",{class:"spinner"},null,-1)),e[17]||(e[17]=t("p",null,"Loading app...",-1)),t("p",ae,C(l.appUrl),1)])):l.error?(c(),u("div",ce,[e[19]||(e[19]=t("div",{class:"error-icon"},"!",-1)),e[20]||(e[20]=t("h2",null,"Failed to Load App",-1)),t("p",ue,C(l.error),1),t("div",de,[t("p",null,[e[18]||(e[18]=t("strong",null,"App URL:",-1)),T(" "+C(l.appUrl),1)]),t("button",{class:"retry-btn",onClick:L},"Try Again")])])):(c(),u("div",ve,[t("div",pe,[t("button",{class:p(["tab",{active:n.value==="player"}]),onClick:e[1]||(e[1]=o=>n.value="player")}," Player ",2),t("button",{class:p(["tab",{active:n.value==="config"}]),onClick:e[2]||(e[2]=o=>n.value="config")}," Config ",2),t("button",{class:p(["tab",{active:n.value==="meta"}]),onClick:e[3]||(e[3]=o=>n.value="meta")}," Scene Meta ",2),n.value==="player"?(c(),u("div",fe,[t("button",{class:p(["control-btn",{active:i.value==="landscape"}]),title:"Landscape (16:9)",onClick:e[4]||(e[4]=o=>i.value="landscape")},[...e[21]||(e[21]=[t("svg",{width:"18",height:"12",viewBox:"0 0 18 12",fill:"currentColor"},[t("rect",{x:"0.5",y:"0.5",width:"17",height:"11",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),t("button",{class:p(["control-btn",{active:i.value==="portrait"}]),title:"Portrait (9:16)",onClick:e[5]||(e[5]=o=>i.value="portrait")},[...e[22]||(e[22]=[t("svg",{width:"12",height:"18",viewBox:"0 0 12 18",fill:"currentColor"},[t("rect",{x:"0.5",y:"0.5",width:"11",height:"17",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),t("button",{class:p(["control-btn",{active:r.value}]),title:"Fullscreen (Esc to exit)",onClick:e[6]||(e[6]=o=>r.value=!r.value)},[...e[23]||(e[23]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3"})],-1)])],2)])):P("",!0)]),t("div",me,[R(t("div",{class:p(["tab-panel player-panel",{fullscreen:r.value}])},[t("div",{class:p(["player-container",_.value])},[t("div",ye,[l.playerComponent?(c(),$(F(l.playerComponent),{key:l.scene.idx,scene:l.scene,playing:v.value,"is-visible":s.value,"duration-ms":k.value,muted:d.value},null,8,["scene","playing","is-visible","duration-ms","muted"])):(c(),u("div",ge,"No player component"))])],2),t("div",he,[t("div",we,[t("button",{class:p(["player-btn",{active:v.value}]),title:v.value?"Pause":"Play",onClick:e[7]||(e[7]=o=>v.value=!v.value)},[v.value?(c(),u("svg",Ce,[...e[24]||(e[24]=[t("rect",{x:"6",y:"4",width:"4",height:"16"},null,-1),t("rect",{x:"14",y:"4",width:"4",height:"16"},null,-1)])])):(c(),u("svg",be,[...e[25]||(e[25]=[t("polygon",{points:"5,3 19,12 5,21"},null,-1)])]))],10,ke),t("button",{class:p(["player-btn",{active:!s.value}]),title:s.value?"Hide (simulate off-screen)":"Show",onClick:e[8]||(e[8]=o=>s.value=!s.value)},[s.value?(c(),u("svg",_e,[...e[26]||(e[26]=[t("path",{d:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"},null,-1),t("circle",{cx:"12",cy:"12",r:"3"},null,-1)])])):(c(),u("svg",Ee,[...e[27]||(e[27]=[t("path",{d:"M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24"},null,-1),t("line",{x1:"1",y1:"1",x2:"23",y2:"23"},null,-1)])]))],10,xe),t("button",{class:p(["player-btn",{active:d.value}]),title:d.value?"Unmute":"Mute",onClick:e[9]||(e[9]=o=>d.value=!d.value)},[d.value?(c(),u("svg",Ae,[...e[29]||(e[29]=[t("polygon",{points:"11,5 6,9 2,9 2,15 6,15 11,19"},null,-1),t("line",{x1:"23",y1:"9",x2:"17",y2:"15"},null,-1),t("line",{x1:"17",y1:"9",x2:"23",y2:"15"},null,-1)])])):(c(),u("svg",Se,[...e[28]||(e[28]=[t("polygon",{points:"11,5 6,9 2,9 2,15 6,15 11,19"},null,-1),t("path",{d:"M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07"},null,-1)])]))],10,Le)]),t("div",Pe,[t("span",{class:p(["state-badge",{inactive:!v.value}])},C(v.value?"Playing":"Paused"),3),t("span",{class:p(["state-badge",{inactive:!s.value}])},C(s.value?"Visible":"Hidden"),3),t("span",{class:p(["state-badge",{inactive:!d.value}])},C(d.value?"Muted":"Sound On"),3)])]),r.value?(c(),u("button",{key:0,class:"exit-fullscreen",onClick:e[10]||(e[10]=o=>r.value=!1)}," Exit Fullscreen ")):P("",!0)],2),[[B,n.value==="player"]]),R(t("div",Me,[l.configComponent?(c(),$(F(l.configComponent),{key:"config-"+l.scene.idx})):(c(),u("div",Re,"No config component"))],512),[[B,n.value==="config"]]),R(t("div",Be,[t("div",{class:"meta-header"},[e[30]||(e[30]=t("h3",null,"scene.meta",-1)),t("button",{class:"copy-btn",onClick:M},"Copy")]),t("pre",$e,C(E.value),1)],512),[[B,n.value==="meta"]])])]))],2))}}),Te=(l,f)=>{const m=l.__vccOpts||l;for(const[w,n]of f)m[w]=n;return m},Fe=Te(Ue,[["__scopeId","data-v-d3c29943"]]);function Ne(l){return typeof l=="object"&&l!==null&&!Array.isArray(l)}const je={name:"JuunoCLI"},Ve=I({...je,setup(l){const f=g(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),m=g(!1),w=g(null),n=[],i=N(null),r=N(null),a=g(!0),v=g(null),s=H({idx:"simulator-scene-1",name:"Simulator Test Scene",app:null,app_idx:"simulator-app-1",created_at:new Date().toISOString(),display_time:5e3,language:"en",order:0,playlist_idx:"simulator-playlist-1",updated_at:new Date().toISOString(),translations:{},meta:{title:"Test App",backgroundColor:"#2c3e50",textColor:"#ecf0f1"},mediaAssets:[]}),k=X({async save(e){console.log("Simulator: Scene updated",e),Object.assign(s,e)}},{scene:s});async function h(){try{const e=await fetch(`${f.value}/manifest.json`);if(e.ok){const o=await e.json();return w.value=o,o}}catch{}return w.value=null,null}function x(e){n.forEach(o=>o.remove()),n.length=0,e.forEach(o=>{const b=document.createElement("link");b.rel="stylesheet",b.href=new URL(o,f.value).toString(),document.head.appendChild(b),n.push(b)})}function _(e){if(!Ne(e))return null;const o=e;return o.default?o.default:typeof o.render=="function"?o.render():null}function E(e){var b,O;const o=[];for(const U of[(b=e==null?void 0:e.resources)==null?void 0:b.player,(O=e==null?void 0:e.resources)==null?void 0:O.config])U&&o.push(...U.filter(q=>q.endsWith(".css")));return o}async function L(){const e=await D(()=>import("external-app/player"),[]),o=_(e);if(!o)throw new Error("External app must export a default component or render function from /player");i.value=j(o)}async function M(){try{const e=await D(()=>import("external-app/config"),[]),o=_(e);r.value=o?j(o):null}catch(e){console.warn("Optional config module could not be loaded:",e),r.value=null}}async function S(){try{a.value=!0,v.value=null,console.log(`Loading external app from: ${f.value}`);const e=await h(),o=E(e);o.length>0&&(console.log("Loading CSS files:",o),x(o)),await L(),await M(),k.load(s),console.log("✅ External app loaded successfully"),a.value=!1}catch(e){console.error("Failed to load external app:",e),v.value=e instanceof Error?e.message:"Failed to load external app",a.value=!1}}function y(){console.log("🔄 Reloading app..."),window.location.reload()}return J(S),(e,o)=>(c(),$(K(Fe),{"player-component":i.value,"config-component":r.value,scene:s,"app-url":f.value,"is-loading":a.value,error:v.value,manifest:w.value,"live-reload-active":m.value,onReload:y},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),De=W(Ve);De.mount("#app");
|
|
@@ -2,6 +2,8 @@ import inquirer from 'inquirer';
|
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, } from 'fs';
|
|
5
|
+
import { wrapError } from '../utils/api.js';
|
|
6
|
+
import { isRecord } from '../utils/is-record.js';
|
|
5
7
|
export const DEFAULT_PROFILE = 'default';
|
|
6
8
|
const DEFAULT_API_URL = 'https://api.juuno.co';
|
|
7
9
|
/**
|
|
@@ -19,28 +21,28 @@ function ensureCredentialsDir() {
|
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* Parse a raw credentials file body into the modern profiles format.
|
|
25
|
+
* - Modern shape ({ profiles: {...} }) passes through.
|
|
26
|
+
* - Legacy single-profile shape ({ token, api_url, ... }) migrates to a
|
|
27
|
+
* default-profile entry.
|
|
28
|
+
* - Anything else is treated as empty so the CLI starts in a clean state.
|
|
29
|
+
*
|
|
30
|
+
* Pure. Exported for testing.
|
|
24
31
|
*/
|
|
25
|
-
function
|
|
26
|
-
if (
|
|
27
|
-
return
|
|
32
|
+
export function parseCredentialsContent(parsed) {
|
|
33
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
34
|
+
return { profiles: {} };
|
|
28
35
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const parsed = JSON.parse(fileContent);
|
|
36
|
-
if (parsed && typeof parsed === 'object' && 'profiles' in parsed) {
|
|
37
|
-
cachedFile = parsed;
|
|
38
|
-
return cachedFile;
|
|
36
|
+
if ('profiles' in parsed) {
|
|
37
|
+
const candidate = parsed.profiles;
|
|
38
|
+
if (!isRecord(candidate)) {
|
|
39
|
+
return { profiles: {} };
|
|
40
|
+
}
|
|
41
|
+
return { profiles: candidate };
|
|
39
42
|
}
|
|
40
|
-
|
|
41
|
-
if (parsed && typeof parsed === 'object' && 'token' in parsed) {
|
|
43
|
+
if ('token' in parsed) {
|
|
42
44
|
const legacy = parsed;
|
|
43
|
-
|
|
45
|
+
return {
|
|
44
46
|
profiles: {
|
|
45
47
|
[DEFAULT_PROFILE]: {
|
|
46
48
|
token: legacy.token,
|
|
@@ -49,9 +51,23 @@ function readCredentialsFile() {
|
|
|
49
51
|
},
|
|
50
52
|
},
|
|
51
53
|
};
|
|
54
|
+
}
|
|
55
|
+
return { profiles: {} };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Reads and parses the credentials file. Migrates legacy single-profile
|
|
59
|
+
* files to the new profiles format transparently.
|
|
60
|
+
*/
|
|
61
|
+
function readCredentialsFile() {
|
|
62
|
+
if (cachedFile) {
|
|
52
63
|
return cachedFile;
|
|
53
64
|
}
|
|
54
|
-
|
|
65
|
+
const credentialsPath = getCredentialsPath();
|
|
66
|
+
if (!existsSync(credentialsPath)) {
|
|
67
|
+
cachedFile = { profiles: {} };
|
|
68
|
+
return cachedFile;
|
|
69
|
+
}
|
|
70
|
+
cachedFile = parseCredentialsContent(JSON.parse(readFileSync(credentialsPath, 'utf-8')));
|
|
55
71
|
return cachedFile;
|
|
56
72
|
}
|
|
57
73
|
function writeCredentialsFile(file) {
|
|
@@ -158,16 +174,18 @@ export function getApiUrl(options = {}) {
|
|
|
158
174
|
const profile = getProfile(resolveProfile(options));
|
|
159
175
|
return profile?.api_url || DEFAULT_API_URL;
|
|
160
176
|
}
|
|
177
|
+
function buildNotLoggedInError(profile) {
|
|
178
|
+
const profileFlag = formatProfileFlag(profile);
|
|
179
|
+
const suffix = profile === DEFAULT_PROFILE ? '' : ` for profile "${profile}"`;
|
|
180
|
+
return new Error(`Not logged in${suffix}. Please run "juuno-cli login${profileFlag}" first or provide credentials via --email and --password`);
|
|
181
|
+
}
|
|
161
182
|
/**
|
|
162
183
|
* Gets the authentication token from options, environment, or stored profile.
|
|
163
184
|
*/
|
|
164
185
|
export async function getAuthToken(options) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const envToken = process.env.JUUNO_API_TOKEN;
|
|
169
|
-
if (envToken) {
|
|
170
|
-
return envToken;
|
|
186
|
+
const explicit = options.token || process.env.JUUNO_API_TOKEN;
|
|
187
|
+
if (explicit) {
|
|
188
|
+
return explicit;
|
|
171
189
|
}
|
|
172
190
|
const profile = resolveProfile(options);
|
|
173
191
|
const storedToken = loadToken(profile);
|
|
@@ -178,9 +196,7 @@ export async function getAuthToken(options) {
|
|
|
178
196
|
const credentials = await getCredentials(options);
|
|
179
197
|
return await loginWithCredentials(credentials, getApiUrl(options));
|
|
180
198
|
}
|
|
181
|
-
|
|
182
|
-
const suffix = profile === DEFAULT_PROFILE ? '' : ` for profile "${profile}"`;
|
|
183
|
-
throw new Error(`Not logged in${suffix}. Please run "juuno-cli login${profileFlag}" first or provide credentials via --email and --password`);
|
|
199
|
+
throw buildNotLoggedInError(profile);
|
|
184
200
|
}
|
|
185
201
|
/**
|
|
186
202
|
* Gets credentials from options or prompts the user.
|
|
@@ -228,18 +244,15 @@ export async function loginWithCredentials(credentials, apiUrl) {
|
|
|
228
244
|
});
|
|
229
245
|
if (!response.ok) {
|
|
230
246
|
const errorText = await response.text();
|
|
231
|
-
throw new Error(
|
|
247
|
+
throw new Error(`${response.status} ${response.statusText}\n${errorText}`);
|
|
232
248
|
}
|
|
233
249
|
const data = (await response.json());
|
|
234
250
|
if (!data.success || !data.token) {
|
|
235
|
-
throw new Error('
|
|
251
|
+
throw new Error('Invalid response from server');
|
|
236
252
|
}
|
|
237
253
|
return data.token;
|
|
238
254
|
}
|
|
239
255
|
catch (error) {
|
|
240
|
-
|
|
241
|
-
throw new Error(`Authentication failed: ${error.message}`);
|
|
242
|
-
}
|
|
243
|
-
throw new Error('Authentication failed: Unknown error');
|
|
256
|
+
throw wrapError('Authentication failed', error);
|
|
244
257
|
}
|
|
245
258
|
}
|