@runeya/apps-cli 0.1.1 ā 0.1.2
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/agent/index.js +5303 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js +8 -0
- package/dist/agent-manager-3S4FUNJS-MYT5X6XQ.js.map +1 -0
- package/dist/chunk-32HTB3XS.js +350 -0
- package/dist/chunk-32HTB3XS.js.map +1 -0
- package/dist/chunk-ERJIU7R4.js +195 -0
- package/dist/chunk-ERJIU7R4.js.map +1 -0
- package/dist/chunk-XZPCEGWS.js +2452 -0
- package/dist/chunk-XZPCEGWS.js.map +1 -0
- package/dist/dist-MFM5N25P.js +10985 -0
- package/dist/dist-MFM5N25P.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js +14 -0
- package/dist/kanban-commands-VEVMJTFS-OIYWEHUP.js.map +1 -0
- package/dist/package.json +36 -0
- package/dist/runeya +4 -0
- package/dist/web/assets/AddressBar.vue-dB66q4DS.js +1 -0
- package/dist/web/assets/AgentPage-B3wrH3gG.js +2 -0
- package/dist/web/assets/AgentPage-BTWkJvqr.css +1 -0
- package/dist/web/assets/AgentScalarChatInterface.vue-CJVuZIB9.js +13 -0
- package/dist/web/assets/AgentTerminalPage-Cj5fRr-j.js +36 -0
- package/dist/web/assets/AgentTerminalPage-EX_n6P3x.css +1 -0
- package/dist/web/assets/AiChatPage-Dhsx3B9B.js +1 -0
- package/dist/web/assets/ApiDocsPage-BddLJuUF.js +5 -0
- package/dist/web/assets/ApiDocsPage-D2Z0wqGZ.css +1 -0
- package/dist/web/assets/AppLayout-AfhaeKlp.js +1 -0
- package/dist/web/assets/AppLayout-sngalCCj.css +1 -0
- package/dist/web/assets/AuditPage-BBV9FW9y.css +1 -0
- package/dist/web/assets/AuditPage-DqycuiVb.js +1 -0
- package/dist/web/assets/CodeInput.vue-Dyew7ml-.js +1 -0
- package/dist/web/assets/Collection.vue-DePdVAHM.js +1 -0
- package/dist/web/assets/CollectionAuthentication.vue-DagSEg45.js +1 -0
- package/dist/web/assets/CollectionCookies.vue-vMKPCgg1.js +1 -0
- package/dist/web/assets/CollectionEnvironment.vue-B0JkZ6GC.js +1 -0
- package/dist/web/assets/CollectionOverview.vue-Bm7OsMWM.js +1 -0
- package/dist/web/assets/CollectionScripts.vue-CZiBq0rT.js +1 -0
- package/dist/web/assets/CollectionServers.vue-bziJbP-5.js +1 -0
- package/dist/web/assets/CollectionSettings.vue-D4oBu-fT.js +1 -0
- package/dist/web/assets/CollectionSync.vue-BG50-Lo4.js +1 -0
- package/dist/web/assets/CommandActionInput.vue-DmZld2w7.js +1 -0
- package/dist/web/assets/Cookies.vue-DjxMLL79.js +1 -0
- package/dist/web/assets/DashboardPage-QjYCF0Kd.js +1 -0
- package/dist/web/assets/DashboardPage-gZJ4_s8T.css +1 -0
- package/dist/web/assets/DataTable.vue-BV9IuUNB.js +1 -0
- package/dist/web/assets/DataTableCheckbox.vue-Cx0exaNK.js +1 -0
- package/dist/web/assets/DataTableHeader.vue-ZtUdyiLV.js +1 -0
- package/dist/web/assets/DataTableInput.vue-C4NF8kJn.js +1 -0
- package/dist/web/assets/DeleteSidebarListElement.vue-CcAW4Pdn.js +1 -0
- package/dist/web/assets/EditSidebarListElement.vue-Crbagha4.js +1 -0
- package/dist/web/assets/EditorIcon-BaDORp4k.js +15 -0
- package/dist/web/assets/EditorIcon-zS3bHXe1.css +1 -0
- package/dist/web/assets/EmptyState.vue-2hJbVxAi.js +22 -0
- package/dist/web/assets/Environment.vue-CcERFnQq.js +1 -0
- package/dist/web/assets/EnvironmentForm-DSKxdDGW.js +108 -0
- package/dist/web/assets/EnvironmentForm-DVMc303P.css +1 -0
- package/dist/web/assets/EnvironmentFormPage-BDhR07CG.js +1 -0
- package/dist/web/assets/EnvironmentFormPage-OaDJvyrc.css +1 -0
- package/dist/web/assets/EnvironmentListPage-BDqcug66.css +1 -0
- package/dist/web/assets/EnvironmentListPage-BzdxgZdS.js +1 -0
- package/dist/web/assets/EnvironmentModal.vue-DvsPa6V9.js +1 -0
- package/dist/web/assets/EnvironmentSelector-B9SZvB3I.css +1 -0
- package/dist/web/assets/EnvironmentSelector-BWljvJWP.js +1 -0
- package/dist/web/assets/Form.vue-BxhIukSx.js +1 -0
- package/dist/web/assets/IconSelector.vue-SxFpYToZ.js +1 -0
- package/dist/web/assets/KanbanBoardPage-7nOYpwHn.css +1 -0
- package/dist/web/assets/KanbanBoardPage-Bbyn-iLC.js +141 -0
- package/dist/web/assets/KanbanListPage-BEX3MKW0.js +1 -0
- package/dist/web/assets/KanbanListPage-DbdjvTCx.css +1 -0
- package/dist/web/assets/LaunchProjectPage-DB_XBa86.js +1 -0
- package/dist/web/assets/LaunchProjectPage-_L_i2xo0.css +1 -0
- package/dist/web/assets/LaunchServicesPage-BABQz-ey.css +1 -0
- package/dist/web/assets/LaunchServicesPage-fmsU-_p0.js +1 -0
- package/dist/web/assets/LogViewer-CJcXcTP0.css +1 -0
- package/dist/web/assets/LogViewer-CV9OmAb1.js +3 -0
- package/dist/web/assets/ProcessControls-CKmB6sB9.css +1 -0
- package/dist/web/assets/ProcessControls-L_hld0kN.js +1 -0
- package/dist/web/assets/ProcessDetailPage-B0qGKtvQ.css +1 -0
- package/dist/web/assets/ProcessDetailPage-CW0F6wvO.js +1 -0
- package/dist/web/assets/ProcessMetrics-BgEFMdnW.css +1 -0
- package/dist/web/assets/ProcessMetrics-D2vCK15o.js +1 -0
- package/dist/web/assets/ProjectDetailPage-Bo04MsdE.js +1 -0
- package/dist/web/assets/ProjectDetailPage-CZBB6F-t.css +1 -0
- package/dist/web/assets/ProjectListPage-BGU6SKAr.css +1 -0
- package/dist/web/assets/ProjectListPage-CG-r82FQ.js +1 -0
- package/dist/web/assets/RandomJobTestPage-C1mgTTSY.css +1 -0
- package/dist/web/assets/RandomJobTestPage-COnuE_dj.js +2 -0
- package/dist/web/assets/Request.vue-CeAzdpVl.js +1 -0
- package/dist/web/assets/RequestAuth.vue-DABBKM9M.js +1 -0
- package/dist/web/assets/RequestRoot.vue-CRaiGtgb.js +19 -0
- package/dist/web/assets/ScalarAsciiArt.vue-mR3JflbN.js +2 -0
- package/dist/web/assets/ScalarIconEyeSlash.vue-JQCpS43o.js +1 -0
- package/dist/web/assets/ScalarIconTrash.vue-D7zGeCPC.js +1 -0
- package/dist/web/assets/ScenarioEditorPage-BVnDp-fS.js +14 -0
- package/dist/web/assets/ScenarioEditorPage-CfBKebp6.css +1 -0
- package/dist/web/assets/ServerVariablesForm.vue-DbVG31Dx.js +1 -0
- package/dist/web/assets/ServiceControlsBar-Cp2VUifX.js +1 -0
- package/dist/web/assets/ServiceControlsBar-D_S_sGQj.css +1 -0
- package/dist/web/assets/ServiceDetailPage-BriHTCpT.css +1 -0
- package/dist/web/assets/ServiceDetailPage-DMoXJliU.js +2 -0
- package/dist/web/assets/ServiceFormPage-BXTTwyWP.js +1 -0
- package/dist/web/assets/ServiceFormPage-CyY-14jH.css +1 -0
- package/dist/web/assets/Settings.vue-BtjfBYMB.js +1 -0
- package/dist/web/assets/SettingsPage-BFqYvm6R.js +508 -0
- package/dist/web/assets/SettingsPage-vGhb_tlY.css +1 -0
- package/dist/web/assets/SetupKeyPage-CrmikLTx.js +1 -0
- package/dist/web/assets/SetupKeyPage-DoY1Ua1s.css +1 -0
- package/dist/web/assets/SetupPage-CRuL5LXc.js +1 -0
- package/dist/web/assets/SetupPage-_KXpCQSU.css +1 -0
- package/dist/web/assets/Sidebar.vue-CLpz7l3s.js +1 -0
- package/dist/web/assets/SidebarButton.vue-DAvh-OoW.js +1 -0
- package/dist/web/assets/SidebarListElement.vue-CfQ22Epo.js +1 -0
- package/dist/web/assets/UnlockPage-Cx3PYgUl.css +1 -0
- package/dist/web/assets/UnlockPage-UxunGumn.js +1 -0
- package/dist/web/assets/VariablesTable-BtdK5olT.css +1 -0
- package/dist/web/assets/VariablesTable-CKe8mApI.js +1 -0
- package/dist/web/assets/ViewLayout.vue-D30IFsIP.js +1 -0
- package/dist/web/assets/ViewLayoutContent.vue-B4BXKBtQ.js +1 -0
- package/dist/web/assets/ViewLayoutSection.vue-BGkXXzBQ.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-C7ztIXtE.js +1 -0
- package/dist/web/assets/abap-C4tHUUCU.js +1 -0
- package/dist/web/assets/active-entities-DMSK8Kvo.js +1 -0
- package/dist/web/assets/apex-6cG6aG-w.js +1 -0
- package/dist/web/assets/api-reference-DUBKG98-.js +1 -0
- package/dist/web/assets/autocomplete-Bx5M86A5.js +308 -0
- package/dist/web/assets/azcli-51BngrSU.js +1 -0
- package/dist/web/assets/base64-uZi92-DX.js +1 -0
- package/dist/web/assets/bat-BDcdVSIt.js +1 -0
- package/dist/web/assets/bicep-Cg0BCcky.js +2 -0
- package/dist/web/assets/cameligo-C3ekVui9.js +1 -0
- package/dist/web/assets/card-6r6P_U-q.js +32 -0
- package/dist/web/assets/checkbox-CQpllPXd.js +141 -0
- package/dist/web/assets/chip-gp-_w98f.js +54 -0
- package/dist/web/assets/clojure-Ci5xNgh_.js +1 -0
- package/dist/web/assets/codicon-DCmgc-ay.ttf +0 -0
- package/dist/web/assets/coffee-xsRz4GKy.js +1 -0
- package/dist/web/assets/column-CE6TbiAC.js +763 -0
- package/dist/web/assets/core-CLjfGkKM.js +1 -0
- package/dist/web/assets/cpp-B2gW4e23.js +1 -0
- package/dist/web/assets/csharp-SRujvHqy.js +1 -0
- package/dist/web/assets/csp-Cr-ll6gP.js +1 -0
- package/dist/web/assets/css-BYTJZCno.js +3 -0
- package/dist/web/assets/cssMode-Bl7Y9u0B.js +4 -0
- package/dist/web/assets/cypher-AmQ0R_D3.js +1 -0
- package/dist/web/assets/dart-Du01a0J5.js +1 -0
- package/dist/web/assets/dist-CNJKy0Au.js +192 -0
- package/dist/web/assets/dist-DGrDIHmK.js +1 -0
- package/dist/web/assets/dist-DkaYBczY.js +1 -0
- package/dist/web/assets/dist-E9g7EMme.js +1 -0
- package/dist/web/assets/dist-wHtavuXf.js +33 -0
- package/dist/web/assets/divider-J1wJyhb6.js +82 -0
- package/dist/web/assets/dockerfile-Dtlk9wqf.js +1 -0
- package/dist/web/assets/ecl-CpmVnLkK.js +1 -0
- package/dist/web/assets/editor.store-CIIP294U.js +1 -0
- package/dist/web/assets/editor.worker-Di51weDy.js +12 -0
- package/dist/web/assets/elixir-B3MVAC8k.js +1 -0
- package/dist/web/assets/fieldset-BRuU3VW2.js +88 -0
- package/dist/web/assets/flow9-Chkly0RN.js +1 -0
- package/dist/web/assets/freemarker2-C-5i2Fhr.js +3 -0
- package/dist/web/assets/fsharp-CZvMkppk.js +1 -0
- package/dist/web/assets/go-dT2iT7lt.js +1 -0
- package/dist/web/assets/graphql-DmObWyBD.js +1 -0
- package/dist/web/assets/handlebars-Bq-uEMhK.js +1 -0
- package/dist/web/assets/hcl-D1x5Oyp4.js +1 -0
- package/dist/web/assets/html-B-mweV_C.js +1 -0
- package/dist/web/assets/htmlMode-BlnuAGDY.js +4 -0
- package/dist/web/assets/index-BRDI9Dib.css +1 -0
- package/dist/web/assets/index-gfmcW9Sb.js +4220 -0
- package/dist/web/assets/ini-Bg2o1S3s.js +1 -0
- package/dist/web/assets/java-XviKSSn5.js +1 -0
- package/dist/web/assets/javascript-D61EWPTf.js +1 -0
- package/dist/web/assets/jsonMode-DmSJWG69.js +10 -0
- package/dist/web/assets/julia-DfTIwUGh.js +1 -0
- package/dist/web/assets/keyboard-nBda2f6m.js +4 -0
- package/dist/web/assets/kotlin-CNaIBh9d.js +1 -0
- package/dist/web/assets/less-D7o76tXd.js +2 -0
- package/dist/web/assets/lexon-Dd374QjV.js +1 -0
- package/dist/web/assets/lib-CegStMAw.js +5 -0
- package/dist/web/assets/library-BwIJAfg0.js +1 -0
- package/dist/web/assets/liquid-BRXADGhu.js +1 -0
- package/dist/web/assets/lua-DZKuiFbY.js +1 -0
- package/dist/web/assets/m3-CbwRx9Bb.js +1 -0
- package/dist/web/assets/markdown-DwLB5Rib.js +1 -0
- package/dist/web/assets/mdx-DXlefD6G.js +1 -0
- package/dist/web/assets/mediaTypes-BtSbXDUA.js +2 -0
- package/dist/web/assets/message-EfwB0RVQ.js +316 -0
- package/dist/web/assets/minus-DCEEn9ly.js +2 -0
- package/dist/web/assets/mips-DLbIq1yM.js +1 -0
- package/dist/web/assets/msdax-D4IjoZ6m.js +1 -0
- package/dist/web/assets/multiselect-BMOAw1qo.js +251 -0
- package/dist/web/assets/mysql-DQL_jy6H.js +1 -0
- package/dist/web/assets/objective-c-D74wM5bv.js +1 -0
- package/dist/web/assets/pascal-5tRlZk4O.js +1 -0
- package/dist/web/assets/pascaligo-CAE4rAEt.js +1 -0
- package/dist/web/assets/password-B0kJu8RE.js +101 -0
- package/dist/web/assets/perl-DREAbuQT.js +1 -0
- package/dist/web/assets/pgsql-DASKdyAW.js +1 -0
- package/dist/web/assets/php-C37ZWqoO.js +1 -0
- package/dist/web/assets/pla-v5u9R8i1.js +1 -0
- package/dist/web/assets/plus-CaknAInj.js +197 -0
- package/dist/web/assets/postiats-Szt5m-7G.js +1 -0
- package/dist/web/assets/powerquery-BzVrUSu3.js +1 -0
- package/dist/web/assets/powershell-BdbPbX_X.js +1 -0
- package/dist/web/assets/pretty-ms-CG-W6pSW.js +202 -0
- package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
- package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
- package/dist/web/assets/primeicons-Dr5RGzOO.svg +345 -0
- package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
- package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
- package/dist/web/assets/process.store-DxDr1XKZ.js +1 -0
- package/dist/web/assets/project.store-CUclo-tf.js +1 -0
- package/dist/web/assets/protobuf-qqDa50tu.js +2 -0
- package/dist/web/assets/pug-Cu20UQ5w.js +1 -0
- package/dist/web/assets/python-BeJzJfgr.js +1 -0
- package/dist/web/assets/qsharp-DcbHL5X7.js +1 -0
- package/dist/web/assets/r-Bvig6Flk.js +1 -0
- package/dist/web/assets/radiobutton-Dpp9kpvR.js +145 -0
- package/dist/web/assets/razor-C8GjTcdm.js +1 -0
- package/dist/web/assets/redis-BxHDcW3-.js +1 -0
- package/dist/web/assets/redshift-B8dvlzjH.js +1 -0
- package/dist/web/assets/restructuredtext-npodtAFM.js +1 -0
- package/dist/web/assets/router-BAq_YY48.js +1 -0
- package/dist/web/assets/ruby-DiofbcJD.js +1 -0
- package/dist/web/assets/rust-BjYid3-m.js +1 -0
- package/dist/web/assets/sb-CzcPAP71.js +1 -0
- package/dist/web/assets/scala-Ci4wFrYi.js +1 -0
- package/dist/web/assets/scheme-D3iWhvP7.js +1 -0
- package/dist/web/assets/scss-DdlqDgmu.js +3 -0
- package/dist/web/assets/settings.store-RtV37pb_.js +1 -0
- package/dist/web/assets/shell-Bawb9gbH.js +1 -0
- package/dist/web/assets/solidity-Z2Sq8PBC.js +1 -0
- package/dist/web/assets/sophia--yD-H1Ow.js +1 -0
- package/dist/web/assets/sparql-0vv3-wLi.js +1 -0
- package/dist/web/assets/sql-0gdWKhmJ.js +1 -0
- package/dist/web/assets/st-rE-x-DTZ.js +1 -0
- package/dist/web/assets/store-BSUCffsu.js +1840 -0
- package/dist/web/assets/style-4Pk2icOw.css +1 -0
- package/dist/web/assets/swift-CSKeG8Ne.js +1 -0
- package/dist/web/assets/systemverilog-CblNM3Im.js +1 -0
- package/dist/web/assets/tabpanel-CXo9Yhvl.js +150 -0
- package/dist/web/assets/tag-BsP7PdQ9.js +54 -0
- package/dist/web/assets/tcl-BYnzj-GZ.js +1 -0
- package/dist/web/assets/toggleswitch-C0fuTzJt.js +115 -0
- package/dist/web/assets/trpc-DgyerAaK.js +1 -0
- package/dist/web/assets/ts.worker-DHQ2j8_R.js +51339 -0
- package/dist/web/assets/tsMode-CaqfXb40.js +11 -0
- package/dist/web/assets/twig-DiGWiEtp.js +1 -0
- package/dist/web/assets/typescript-D23_zyMA.js +1 -0
- package/dist/web/assets/typespec-a6xMcpXw.js +1 -0
- package/dist/web/assets/urls-D4kou9RH.js +12 -0
- package/dist/web/assets/use-tree-walker-BebEauYQ.js +1 -0
- package/dist/web/assets/useAiChatDrawer-DDBZQU71.js +1 -0
- package/dist/web/assets/useClipboard-BgE-EP5h.js +1 -0
- package/dist/web/assets/useLayout-BtC8xLAa.js +1 -0
- package/dist/web/assets/usePluginManager-MFUURiUn.js +1 -0
- package/dist/web/assets/useSidebar-DC34K6Vb.js +1 -0
- package/dist/web/assets/uuid-B9GFVXPm.js +1 -0
- package/dist/web/assets/vb---51HVMC.js +1 -0
- package/dist/web/assets/vue-router-CCwpUNtv.js +1 -0
- package/dist/web/assets/w3c-keyname-DDCGDLuI.js +1 -0
- package/dist/web/assets/wgsl-2A8z4pT5.js +298 -0
- package/dist/web/assets/xml-e0SmIpRt.js +1 -0
- package/dist/web/assets/yaml-ChhAbF6s.js +1 -0
- package/dist/web/index.html +14 -0
- package/package.json +7 -7
- package/Dockerfile +0 -54
- package/scripts/post-build.mjs +0 -61
- package/src/__tests__/cli-port.test.ts +0 -93
- package/src/index.ts +0 -298
- package/src/port-resolution.ts +0 -19
- package/tsconfig.json +0 -9
- package/tsup.config.bundled_lh0xis3zdq.mjs +0 -10
- package/tsup.config.bundled_qj1zdvku09a.mjs +0 -10
- package/tsup.config.ts +0 -6
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the CLI port resolution utility (port-resolution.ts).
|
|
3
|
-
*
|
|
4
|
-
* These tests are GREEN as soon as port-resolution.ts exists with the right
|
|
5
|
-
* implementation. They also serve as the contract that the CLI's index.ts
|
|
6
|
-
* must satisfy when it calls resolvePort() to determine the listen port.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import { resolvePort } from '../port-resolution.js';
|
|
10
|
-
|
|
11
|
-
describe('resolvePort', () => {
|
|
12
|
-
const managedKeys = ['RUNEYA_HTTP_PORT', 'PORT'] as const;
|
|
13
|
-
const saved: Record<string, string | undefined> = {};
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
for (const k of managedKeys) {
|
|
17
|
-
saved[k] = process.env[k];
|
|
18
|
-
delete process.env[k];
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
for (const k of managedKeys) {
|
|
24
|
-
if (saved[k] === undefined) delete process.env[k];
|
|
25
|
-
else process.env[k] = saved[k];
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// --- defaults ---
|
|
30
|
-
|
|
31
|
-
it('returns 4000 when no port env vars are set', () => {
|
|
32
|
-
expect(resolvePort()).toBe(4000);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// --- PORT only (backwards compat) ---
|
|
36
|
-
|
|
37
|
-
it('uses PORT when only PORT is set', () => {
|
|
38
|
-
process.env.PORT = '3333';
|
|
39
|
-
expect(resolvePort()).toBe(3333);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// --- RUNEYA_HTTP_PORT ---
|
|
43
|
-
|
|
44
|
-
it('uses RUNEYA_HTTP_PORT when only RUNEYA_HTTP_PORT is set', () => {
|
|
45
|
-
process.env.RUNEYA_HTTP_PORT = '3333';
|
|
46
|
-
expect(resolvePort()).toBe(3333);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('RUNEYA_HTTP_PORT wins over PORT when both are set', () => {
|
|
50
|
-
process.env.RUNEYA_HTTP_PORT = '3333';
|
|
51
|
-
process.env.PORT = '9999';
|
|
52
|
-
expect(resolvePort()).toBe(3333);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// --- edge: extreme valid ports ---
|
|
56
|
-
|
|
57
|
-
it('accepts the minimum valid port (1)', () => {
|
|
58
|
-
process.env.RUNEYA_HTTP_PORT = '1';
|
|
59
|
-
expect(resolvePort()).toBe(1);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('accepts the maximum valid port (65535)', () => {
|
|
63
|
-
process.env.RUNEYA_HTTP_PORT = '65535';
|
|
64
|
-
expect(resolvePort()).toBe(65535);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// --- validation errors ---
|
|
68
|
-
|
|
69
|
-
it('throws for RUNEYA_HTTP_PORT=0 (below minimum)', () => {
|
|
70
|
-
process.env.RUNEYA_HTTP_PORT = '0';
|
|
71
|
-
expect(() => resolvePort()).toThrow();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('throws for RUNEYA_HTTP_PORT=99999 (above maximum)', () => {
|
|
75
|
-
process.env.RUNEYA_HTTP_PORT = '99999';
|
|
76
|
-
expect(() => resolvePort()).toThrow();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('throws for RUNEYA_HTTP_PORT=abc (non-numeric)', () => {
|
|
80
|
-
process.env.RUNEYA_HTTP_PORT = 'abc';
|
|
81
|
-
expect(() => resolvePort()).toThrow();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('throws for PORT=abc (non-numeric, no RUNEYA_HTTP_PORT set)', () => {
|
|
85
|
-
process.env.PORT = 'abc';
|
|
86
|
-
expect(() => resolvePort()).toThrow();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('throws for a decimal value like 80.5', () => {
|
|
90
|
-
process.env.RUNEYA_HTTP_PORT = '80.5';
|
|
91
|
-
expect(() => resolvePort()).toThrow();
|
|
92
|
-
});
|
|
93
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import type { Server } from 'node:http';
|
|
3
|
-
import type { WebSocketServer } from 'ws';
|
|
4
|
-
import type { Request, Response } from 'express';
|
|
5
|
-
import { fileURLToPath, URL as NodeURL } from 'node:url';
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
-
import { readFileSync } from 'node:fs';
|
|
8
|
-
import express from 'express';
|
|
9
|
-
import dotenv from 'dotenv'
|
|
10
|
-
import open from 'open';
|
|
11
|
-
import { resolvePort } from './port-resolution.js';
|
|
12
|
-
|
|
13
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
|
|
15
|
-
const { version } = JSON.parse(
|
|
16
|
-
readFileSync(new NodeURL('../package.json', import.meta.url), 'utf8'),
|
|
17
|
-
) as { version: string };
|
|
18
|
-
|
|
19
|
-
function printStartupTable(port: number, appUrl: string): void {
|
|
20
|
-
const rows: [string, string][] = [
|
|
21
|
-
['Version', version],
|
|
22
|
-
['Port', String(port)],
|
|
23
|
-
['Url', appUrl],
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const col0 = Math.max(...rows.map(([k]) => k.length));
|
|
27
|
-
const col1 = Math.max(...rows.map(([, v]) => v.length));
|
|
28
|
-
|
|
29
|
-
const pad = (s: string, n: number) => s.padEnd(n);
|
|
30
|
-
const hr = (l: string, m: string, r: string, f: string) =>
|
|
31
|
-
l + f.repeat(col0 + 2) + m + f.repeat(col1 + 2) + r;
|
|
32
|
-
|
|
33
|
-
console.log('');
|
|
34
|
-
console.log(hr('ā', 'ā¬', 'ā', 'ā'));
|
|
35
|
-
for (const [key, value] of rows) {
|
|
36
|
-
console.log(`ā ${pad(key, col0)} ā ${pad(value, col1)} ā`);
|
|
37
|
-
}
|
|
38
|
-
console.log(hr('ā', 'ā“', 'ā', 'ā'));
|
|
39
|
-
console.log('');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Parse CLI flags
|
|
43
|
-
const args = process.argv.slice(2);
|
|
44
|
-
|
|
45
|
-
// Resolve target directory (first positional arg, skipping flag values)
|
|
46
|
-
const flagsWithValues = new Set(['--host', '--port', '-e', '--environment', '-s', '--service']);
|
|
47
|
-
const targetDir = args.find((a, i) => !a.startsWith('-') && !flagsWithValues.has(args[i - 1] ?? ''));
|
|
48
|
-
if (targetDir) {
|
|
49
|
-
const resolved = resolve(targetDir);
|
|
50
|
-
process.chdir(resolved);
|
|
51
|
-
console.log(`[cli] Working directory: ${resolved}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
dotenv.config();
|
|
55
|
-
|
|
56
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
57
|
-
console.log(`Usage: runeya [path-to-your-stack] [options]
|
|
58
|
-
|
|
59
|
-
Options:
|
|
60
|
-
--version Show version number
|
|
61
|
-
--pe, --pull-env Print env vars for a service to stdout (requires -e and -s)
|
|
62
|
-
-e, --environment <n> Environment name or id
|
|
63
|
-
-s, --service <name> Service name or id
|
|
64
|
-
--lan Expose server on the local network (shortcut for --host 0.0.0.0)
|
|
65
|
-
--host <address> Host to bind to (default: 127.0.0.1)
|
|
66
|
-
--no-open Do not open browser on startup
|
|
67
|
-
--agent Run as agent only (no server, no UI) ā for remote machines
|
|
68
|
-
-h, --help Show this help message
|
|
69
|
-
`);
|
|
70
|
-
process.exit(0);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (args.includes('--version')) {
|
|
74
|
-
console.log(version);
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const noOpen =
|
|
79
|
-
args.includes('--no-open') ||
|
|
80
|
-
!!process.env['VITEST'] ||
|
|
81
|
-
!!process.env['CI'] ||
|
|
82
|
-
process.env['NODE_ENV'] === 'test';
|
|
83
|
-
|
|
84
|
-
// --pull-env flag ā detect early so we can silence stdout before any log
|
|
85
|
-
const isPullEnv = args.includes('--pull-env') || args.includes('--pe');
|
|
86
|
-
|
|
87
|
-
// In pull-env mode, redirect console.log/info to stderr so only KEY=VALUE goes to stdout
|
|
88
|
-
if (isPullEnv) {
|
|
89
|
-
const toStderr = (...a: unknown[]) => process.stderr.write(a.join(' ') + '\n');
|
|
90
|
-
console.log = toStderr;
|
|
91
|
-
console.info = toStderr;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function getFlagValue(flags: string[]): string | undefined {
|
|
95
|
-
for (const flag of flags) {
|
|
96
|
-
const idx = args.indexOf(flag);
|
|
97
|
-
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
98
|
-
}
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const serviceArg = getFlagValue(['-s', '--service']);
|
|
103
|
-
const environmentArg = getFlagValue(['-e', '--environment']);
|
|
104
|
-
|
|
105
|
-
// Configure environment BEFORE importing server (env is parsed at module load)
|
|
106
|
-
// RUNEYA_HTTP_PORT takes priority over PORT
|
|
107
|
-
if (process.env.RUNEYA_HTTP_PORT && !process.env.PORT) {
|
|
108
|
-
process.env.PORT = process.env.RUNEYA_HTTP_PORT;
|
|
109
|
-
}
|
|
110
|
-
// AGENT_PORT is an alias for RUNEYA_AGENT_PORT (used by e2e test-server and legacy tooling)
|
|
111
|
-
if (process.env.AGENT_PORT && !process.env.RUNEYA_AGENT_PORT) {
|
|
112
|
-
process.env.RUNEYA_AGENT_PORT = process.env.AGENT_PORT;
|
|
113
|
-
}
|
|
114
|
-
// --lan shortcut sets host to 0.0.0.0; --host flag takes priority over HOST env var
|
|
115
|
-
const isLan = args.includes('--lan');
|
|
116
|
-
const hostFlag = getFlagValue(['--host']);
|
|
117
|
-
const isLanProxy = isLan || process.env.RUNEYA_LAN_PROXY === 'true' || process.env.RUNEYA_LAN_PROXY === '1';
|
|
118
|
-
if (hostFlag) {
|
|
119
|
-
process.env.HOST = hostFlag;
|
|
120
|
-
} else if (isLanProxy) {
|
|
121
|
-
process.env.HOST = '0.0.0.0';
|
|
122
|
-
}
|
|
123
|
-
if (isLanProxy) {
|
|
124
|
-
process.env.RUNEYA_LAN_PROXY = 'true';
|
|
125
|
-
}
|
|
126
|
-
// Agent binary path (embedded in CLI dist during build)
|
|
127
|
-
process.env.AGENT_BINARY_PATH = join(__dirname, 'agent/index.js');
|
|
128
|
-
|
|
129
|
-
// --agent mode: run as standalone agent only (no server, no UI)
|
|
130
|
-
const isAgentOnly = args.includes('--agent');
|
|
131
|
-
if (isAgentOnly) {
|
|
132
|
-
const agentPort = getFlagValue(['--port']);
|
|
133
|
-
const agentHost = getFlagValue(['--host']);
|
|
134
|
-
if (agentPort) process.env.RUNEYA_AGENT_PORT = agentPort;
|
|
135
|
-
// --host in agent mode binds the agent (default 127.0.0.1 ā use --host 0.0.0.0 for remote access)
|
|
136
|
-
// --lan also applies: sets agent host to 0.0.0.0 unless --host overrides it
|
|
137
|
-
if (agentHost) {
|
|
138
|
-
process.env.RUNEYA_AGENT_HOST = agentHost;
|
|
139
|
-
} else if (isLanProxy) {
|
|
140
|
-
process.env.RUNEYA_AGENT_HOST = '0.0.0.0';
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Data directory defaults to CWD/.runeya (server env.ts default)
|
|
144
|
-
// so Runeya data lives alongside the target project.
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Listen with retry for EADDRINUSE ā handles the race condition when the CLI
|
|
148
|
-
* restarts the server before the old process has fully released the port.
|
|
149
|
-
*
|
|
150
|
-
* The `wss` parameter is needed because the `ws` library re-emits HTTP server
|
|
151
|
-
* errors on the WebSocketServer ā without a handler the error becomes unhandled.
|
|
152
|
-
*/
|
|
153
|
-
function listenWithRetry(
|
|
154
|
-
server: Server,
|
|
155
|
-
port: number,
|
|
156
|
-
host: string,
|
|
157
|
-
wss?: WebSocketServer,
|
|
158
|
-
maxRetries = 20,
|
|
159
|
-
delay = 500,
|
|
160
|
-
): Promise<void> {
|
|
161
|
-
return new Promise((resolve, reject) => {
|
|
162
|
-
let attempts = 0;
|
|
163
|
-
|
|
164
|
-
// Absorb EADDRINUSE errors re-emitted by the ws library on the WSS
|
|
165
|
-
// during the retry window. Without this, Node.js crashes on unhandled error.
|
|
166
|
-
const wssErrorHandler = () => { /* absorbed ā handled via server 'error' */ };
|
|
167
|
-
wss?.on('error', wssErrorHandler);
|
|
168
|
-
|
|
169
|
-
const tryListen = () => {
|
|
170
|
-
const onError = (err: NodeJS.ErrnoException) => {
|
|
171
|
-
// Clean up the 'listening' callback that server.listen() registered
|
|
172
|
-
server.removeAllListeners('listening');
|
|
173
|
-
|
|
174
|
-
if (err.code === 'EADDRINUSE' && attempts < maxRetries) {
|
|
175
|
-
attempts++;
|
|
176
|
-
console.log(`[cli] Port ${port} in use, retrying in ${delay}ms (${attempts}/${maxRetries})...`);
|
|
177
|
-
setTimeout(tryListen, delay);
|
|
178
|
-
} else {
|
|
179
|
-
wss?.removeListener('error', wssErrorHandler);
|
|
180
|
-
reject(err);
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
server.once('error', onError);
|
|
185
|
-
server.listen(port, host, () => {
|
|
186
|
-
server.removeListener('error', onError);
|
|
187
|
-
wss?.removeListener('error', wssErrorHandler);
|
|
188
|
-
resolve();
|
|
189
|
-
});
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
tryListen();
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function main() {
|
|
197
|
-
// Dynamic import ensures env is parsed AFTER we configure it
|
|
198
|
-
const { createLocalServer, pullEnv, PullEnvError } = await import('@runeya/apps-server');
|
|
199
|
-
|
|
200
|
-
// Handle --pull-env mode: resolve env vars and print, then exit
|
|
201
|
-
if (isPullEnv) {
|
|
202
|
-
if (!serviceArg) {
|
|
203
|
-
console.error('Error: --service (-s) is required with --pull-env');
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
206
|
-
if (!environmentArg) {
|
|
207
|
-
console.error('Error: --environment (-e) is required with --pull-env');
|
|
208
|
-
process.exit(1);
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
const output = await pullEnv({ service: serviceArg, environment: environmentArg });
|
|
212
|
-
if (output) process.stdout.write(output + '\n');
|
|
213
|
-
process.exit(0);
|
|
214
|
-
} catch (err: unknown) {
|
|
215
|
-
if (err instanceof PullEnvError) {
|
|
216
|
-
console.error(`Error: ${err.message}`);
|
|
217
|
-
process.exit(err.exitCode);
|
|
218
|
-
}
|
|
219
|
-
throw err;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// --agent mode: standalone agent, no server, no UI
|
|
224
|
-
if (isAgentOnly) {
|
|
225
|
-
const { spawn } = await import('node:child_process');
|
|
226
|
-
const agentBin = join(__dirname, 'agent/index.js');
|
|
227
|
-
const child = spawn(process.execPath, [agentBin], { stdio: 'inherit' });
|
|
228
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
|
229
|
-
// SIGINT is already delivered to the child by the terminal (same process group).
|
|
230
|
-
// Intercept it here only to suppress Node's default exit so we stay alive
|
|
231
|
-
// until the child finishes its graceful shutdown.
|
|
232
|
-
process.on('SIGINT', () => { /* wait for child to exit */ });
|
|
233
|
-
process.on('SIGTERM', () => child.kill('SIGTERM'));
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
console.log('[cli] Starting Runeya...');
|
|
238
|
-
|
|
239
|
-
// Create server (automatically spawns local agent)
|
|
240
|
-
const { app, server, wss, shutdown } = await createLocalServer();
|
|
241
|
-
|
|
242
|
-
// Serve static files from web frontend (embedded in CLI dist during build)
|
|
243
|
-
const webDistPath = join(__dirname, 'web');
|
|
244
|
-
app.use(express.static(webDistPath));
|
|
245
|
-
|
|
246
|
-
// Fallback to index.html for client-side routing (Vue Router)
|
|
247
|
-
// Express 5 requires explicit path patterns (not '*')
|
|
248
|
-
app.use((_req: Request, res: Response) => {
|
|
249
|
-
res.sendFile(join(webDistPath, 'index.html'));
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// Start server ā RUNEYA_HTTP_PORT takes priority over PORT
|
|
253
|
-
const port = resolvePort();
|
|
254
|
-
const host = process.env.HOST ?? '127.0.0.1';
|
|
255
|
-
|
|
256
|
-
await listenWithRetry(server, port, host, wss);
|
|
257
|
-
|
|
258
|
-
const appUrl = `http://${host}:${port}`;
|
|
259
|
-
|
|
260
|
-
if (!noOpen) {
|
|
261
|
-
await open(appUrl);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
console.log('\nš Runeya is running!');
|
|
265
|
-
if (isLanProxy) {
|
|
266
|
-
console.log('š LAN mode: server and service ports exposed on local network');
|
|
267
|
-
}
|
|
268
|
-
printStartupTable(port, appUrl);
|
|
269
|
-
console.log('[cli] Press Ctrl+C to stop');
|
|
270
|
-
|
|
271
|
-
let shuttingDown = false;
|
|
272
|
-
const gracefulShutdown = async (signal: string) => {
|
|
273
|
-
if (shuttingDown) {
|
|
274
|
-
console.log(`[cli] Forced exit (second ${signal})`);
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
shuttingDown = true;
|
|
278
|
-
console.log(`\n[cli] Received ${signal}, shutting down gracefully...`);
|
|
279
|
-
|
|
280
|
-
// In dev, exit immediately ā ports are freed on process death,
|
|
281
|
-
// and the 'exit' handler in agent-spawner SIGKILLs the agent.
|
|
282
|
-
if (process.env.NODE_ENV === 'development') {
|
|
283
|
-
process.exit(0);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
await shutdown();
|
|
287
|
-
console.log('[cli] Shutdown complete');
|
|
288
|
-
process.exit(0);
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
292
|
-
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
main().catch((err) => {
|
|
296
|
-
console.error('[cli] Fatal error:', err);
|
|
297
|
-
process.exit(1);
|
|
298
|
-
});
|
package/src/port-resolution.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve the HTTP port Runeya should listen on.
|
|
3
|
-
*
|
|
4
|
-
* Priority: RUNEYA_HTTP_PORT > PORT > 4000 (default)
|
|
5
|
-
*
|
|
6
|
-
* Throws if the resolved value is not an integer in [1, 65535].
|
|
7
|
-
*/
|
|
8
|
-
export function resolvePort(): number {
|
|
9
|
-
const raw = process.env.RUNEYA_HTTP_PORT ?? process.env.PORT;
|
|
10
|
-
if (raw === undefined) return 4000;
|
|
11
|
-
|
|
12
|
-
const port = Number(raw);
|
|
13
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
14
|
-
throw new Error(
|
|
15
|
-
`Invalid port: ${JSON.stringify(raw)}. Must be an integer between 1 and 65535.`,
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
return port;
|
|
19
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// tsup.config.ts
|
|
2
|
-
import { defineConfig } from "tsup";
|
|
3
|
-
import { getAppConfig } from "@runeya/scripts-tsup-config";
|
|
4
|
-
var tsup_config_default = defineConfig(getAppConfig({
|
|
5
|
-
entry: ["src/index.ts"]
|
|
6
|
-
}));
|
|
7
|
-
export {
|
|
8
|
-
tsup_config_default as default
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidHN1cC5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvY29jby9Qcm9qZWN0cy9ydW5leWEtcnVzdC9hcHBzL2NsaS90c3VwLmNvbmZpZy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9jb2NvL1Byb2plY3RzL3J1bmV5YS1ydXN0L2FwcHMvY2xpXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2NvY28vUHJvamVjdHMvcnVuZXlhLXJ1c3QvYXBwcy9jbGkvdHN1cC5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd0c3VwJztcbmltcG9ydCB7IGdldEFwcENvbmZpZyB9IGZyb20gJ0BydW5leWEvc2NyaXB0cy10c3VwLWNvbmZpZyc7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyhnZXRBcHBDb25maWcoe1xuICBlbnRyeTogWydzcmMvaW5kZXgudHMnXSxcbn0pKTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBc1EsU0FBUyxvQkFBb0I7QUFDblMsU0FBUyxvQkFBb0I7QUFFN0IsSUFBTyxzQkFBUSxhQUFhLGFBQWE7QUFBQSxFQUN2QyxPQUFPLENBQUMsY0FBYztBQUN4QixDQUFDLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// tsup.config.ts
|
|
2
|
-
import { defineConfig } from "tsup";
|
|
3
|
-
import { getAppConfig } from "@runeya/scripts-tsup-config";
|
|
4
|
-
var tsup_config_default = defineConfig(getAppConfig({
|
|
5
|
-
entry: ["src/index.ts"]
|
|
6
|
-
}));
|
|
7
|
-
export {
|
|
8
|
-
tsup_config_default as default
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidHN1cC5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL2hvbWUvY29jby9Qcm9qZWN0cy9ydW5leWEtcnVzdC9hcHBzL2NsaS90c3VwLmNvbmZpZy50c1wiO2NvbnN0IF9faW5qZWN0ZWRfZGlybmFtZV9fID0gXCIvaG9tZS9jb2NvL1Byb2plY3RzL3J1bmV5YS1ydXN0L2FwcHMvY2xpXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9ob21lL2NvY28vUHJvamVjdHMvcnVuZXlhLXJ1c3QvYXBwcy9jbGkvdHN1cC5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd0c3VwJztcbmltcG9ydCB7IGdldEFwcENvbmZpZyB9IGZyb20gJ0BydW5leWEvc2NyaXB0cy10c3VwLWNvbmZpZyc7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyhnZXRBcHBDb25maWcoe1xuICBlbnRyeTogWydzcmMvaW5kZXgudHMnXSxcbn0pKTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBc1EsU0FBUyxvQkFBb0I7QUFDblMsU0FBUyxvQkFBb0I7QUFFN0IsSUFBTyxzQkFBUSxhQUFhLGFBQWE7QUFBQSxFQUN2QyxPQUFPLENBQUMsY0FBYztBQUN4QixDQUFDLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
|