@cobinar/dalus 0.1.14 → 0.1.15
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/.mmtp +19 -0
- package/fix-dalus-login.bat +13 -0
- package/fix-dalus-login.ps1 +82 -0
- package/fix_requires.py +21 -15
- package/package.json +1 -1
- package/src/commands/login.mjs +19 -103
package/.mmtp
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{ls: "31c9bc3528793648bacd1a5678f0a062d2********532f9b4a",…}
|
|
2
|
+
ls
|
|
3
|
+
:
|
|
4
|
+
"31c9bc****793648bacd1a5*****a062d21d1070532f9*4a"
|
|
5
|
+
user
|
|
6
|
+
:
|
|
7
|
+
{uid: "vHzod3IL*IVvdpmT2SEZ****z0x2", email: "**************@cobinar.com",…}
|
|
8
|
+
email
|
|
9
|
+
:
|
|
10
|
+
"****************@cobinar.com"
|
|
11
|
+
name
|
|
12
|
+
:
|
|
13
|
+
"*********************"
|
|
14
|
+
picture
|
|
15
|
+
:
|
|
16
|
+
"*********************************"
|
|
17
|
+
uid
|
|
18
|
+
:
|
|
19
|
+
"vHzod3ILPIVvdpmT2SE*****tz0x2"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal
|
|
3
|
+
|
|
4
|
+
echo Applying dalus login callback fixes...
|
|
5
|
+
echo - fixing the token exchange endpoint path
|
|
6
|
+
echo - fixing the ssoCode property extraction
|
|
7
|
+
echo.
|
|
8
|
+
|
|
9
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0fix-dalus-login.ps1"
|
|
10
|
+
|
|
11
|
+
echo.
|
|
12
|
+
echo Done. Redeploy dalus-main whenever you're ready.
|
|
13
|
+
pause
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# fix-dalus-login.ps1
|
|
2
|
+
#
|
|
3
|
+
# Applies the two OAuth callback fixes to a local dalus-main checkout:
|
|
4
|
+
# 1. Endpoint: https://8bnk.dalus.cobinar.com/api/auth/callback
|
|
5
|
+
# -> https://8bnk.dalus.cobinar.com/api/login/callback
|
|
6
|
+
# (the worker's router only defines POST /api/login/callback)
|
|
7
|
+
# 2. Property: data.ssoCode || data.code || data.sso_code
|
|
8
|
+
# -> data.ssoCode
|
|
9
|
+
# (the worker's handler returns { ok, user, ssoCode } - never code/sso_code)
|
|
10
|
+
#
|
|
11
|
+
# Place this file (and fix-dalus-login.bat) inside, or one level above,
|
|
12
|
+
# your dalus-main folder, then run the .bat. This script searches
|
|
13
|
+
# recursively from its own location for login\callback.html and
|
|
14
|
+
# auth\callback\index.html and patches whichever it finds.
|
|
15
|
+
|
|
16
|
+
$root = $PSScriptRoot
|
|
17
|
+
|
|
18
|
+
$oldEndpoint = "https://8bnk.dalus.cobinar.com/api/auth/callback"
|
|
19
|
+
$newEndpoint = "https://8bnk.dalus.cobinar.com/api/login/callback"
|
|
20
|
+
$oldProperty = "code: data.ssoCode || data.code || data.sso_code, user: data.user"
|
|
21
|
+
$newProperty = "code: data.ssoCode, user: data.user"
|
|
22
|
+
|
|
23
|
+
$targets = @(
|
|
24
|
+
@{ Relative = "login\callback.html"; FixProperty = $true },
|
|
25
|
+
@{ Relative = "auth\callback\index.html"; FixProperty = $false }
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
$totalFound = 0
|
|
29
|
+
|
|
30
|
+
foreach ($target in $targets) {
|
|
31
|
+
$leaf = Split-Path $target.Relative -Leaf
|
|
32
|
+
|
|
33
|
+
$candidates = Get-ChildItem -Path $root -Recurse -File -Filter $leaf -ErrorAction SilentlyContinue |
|
|
34
|
+
Where-Object {
|
|
35
|
+
$_.FullName -notmatch '\\node_modules\\' -and
|
|
36
|
+
$_.FullName -notmatch '\\\.git\\' -and
|
|
37
|
+
$_.FullName -notmatch '\\\.wrangler\\' -and
|
|
38
|
+
$_.FullName.EndsWith($target.Relative)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
foreach ($file in $candidates) {
|
|
42
|
+
$totalFound++
|
|
43
|
+
$text = [System.IO.File]::ReadAllText($file.FullName)
|
|
44
|
+
$changed = $false
|
|
45
|
+
|
|
46
|
+
if ($text.Contains($oldEndpoint)) {
|
|
47
|
+
$text = $text.Replace($oldEndpoint, $newEndpoint)
|
|
48
|
+
$changed = $true
|
|
49
|
+
Write-Host " [fixed] endpoint path $($file.FullName)"
|
|
50
|
+
} elseif ($text.Contains($newEndpoint)) {
|
|
51
|
+
Write-Host " [ok] endpoint already correct $($file.FullName)"
|
|
52
|
+
} else {
|
|
53
|
+
Write-Host " [skip] endpoint line not found (file differs from expected) $($file.FullName)"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ($target.FixProperty) {
|
|
57
|
+
if ($text.Contains($oldProperty)) {
|
|
58
|
+
$text = $text.Replace($oldProperty, $newProperty)
|
|
59
|
+
$changed = $true
|
|
60
|
+
Write-Host " [fixed] ssoCode property $($file.FullName)"
|
|
61
|
+
} elseif ($text.Contains($newProperty)) {
|
|
62
|
+
Write-Host " [ok] property already correct $($file.FullName)"
|
|
63
|
+
} else {
|
|
64
|
+
Write-Host " [skip] property line not found (file differs from expected) $($file.FullName)"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ($changed) {
|
|
69
|
+
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
|
70
|
+
[System.IO.File]::WriteAllText($file.FullName, $text, $utf8NoBom)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Write-Host ""
|
|
76
|
+
if ($totalFound -eq 0) {
|
|
77
|
+
Write-Host "Didn't find login\callback.html or auth\callback\index.html under:"
|
|
78
|
+
Write-Host " $root"
|
|
79
|
+
Write-Host "Move this script inside (or one level above) your dalus-main folder and run it again."
|
|
80
|
+
} else {
|
|
81
|
+
Write-Host "Checked $totalFound file(s)."
|
|
82
|
+
}
|
package/fix_requires.py
CHANGED
|
@@ -4,18 +4,17 @@ import re
|
|
|
4
4
|
TARGET_DIR = r"D:\Downloads\daluss"
|
|
5
5
|
IGNORE_DIRS = {'node_modules', '.git', 'dist', 'build'}
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def enhance_esm_compatibility(directory):
|
|
8
8
|
if not os.path.exists(directory):
|
|
9
9
|
print(f"Error: Directory '{directory}' does not exist.")
|
|
10
10
|
return
|
|
11
11
|
|
|
12
|
-
#
|
|
12
|
+
# The bulletproof ESM polyfill for 'require'
|
|
13
|
+
REQUIRE_POLYFILL = "import { createRequire as _createRequire } from 'module';\nconst require = _createRequire(import.meta.url);\n\n"
|
|
14
|
+
|
|
15
|
+
# Regexes for handling module.exports
|
|
13
16
|
exports_dict_regex = re.compile(r"module\.exports\s*=\s*\{([^}]+)\}\s*;?")
|
|
14
|
-
|
|
15
|
-
# Matches: module.exports.foo = bar; or exports.foo = bar;
|
|
16
17
|
named_exports_regex = re.compile(r"(?:module\.)?exports\.([a-zA-Z0-9_]+)\s*=\s*")
|
|
17
|
-
|
|
18
|
-
# Matches: module.exports = myVariable; (for default exports)
|
|
19
18
|
default_export_regex = re.compile(r"module\.exports\s*=\s*([a-zA-Z0-9_]+)\s*;?")
|
|
20
19
|
|
|
21
20
|
changed_files_count = 0
|
|
@@ -24,27 +23,34 @@ def fix_exports(directory):
|
|
|
24
23
|
dirs[:] = [d for d in dirs if d not in IGNORE_DIRS]
|
|
25
24
|
|
|
26
25
|
for file in files:
|
|
27
|
-
|
|
26
|
+
# Target .js, .mjs, and extensionless files (like CLI binaries in bin/)
|
|
27
|
+
if file.endswith(('.js', '.mjs')) or ('.' not in file and 'bin' in root):
|
|
28
28
|
file_path = os.path.join(root, file)
|
|
29
|
+
|
|
29
30
|
try:
|
|
30
31
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
31
32
|
content = f.read()
|
|
32
33
|
|
|
33
34
|
original_content = content
|
|
34
35
|
|
|
35
|
-
#
|
|
36
|
+
# 1. Polyfill `require` if it's used but not defined
|
|
37
|
+
if 'require(' in content and 'createRequire' not in content:
|
|
38
|
+
# Safely insert polyfill after any hashbang (e.g., #!/usr/bin/env node)
|
|
39
|
+
if content.startswith('#!'):
|
|
40
|
+
lines = content.split('\n', 1)
|
|
41
|
+
content = lines[0] + '\n' + REQUIRE_POLYFILL + (lines[1] if len(lines) > 1 else '')
|
|
42
|
+
else:
|
|
43
|
+
content = REQUIRE_POLYFILL + content
|
|
44
|
+
|
|
45
|
+
# 2. Fix module.exports
|
|
36
46
|
content = exports_dict_regex.sub(r"export { \1 };", content)
|
|
37
|
-
|
|
38
|
-
# Fix named exports: module.exports.foo = ... -> export const foo = ...
|
|
39
47
|
content = named_exports_regex.sub(r"export const \1 = ", content)
|
|
40
|
-
|
|
41
|
-
# Fix default exports: module.exports = foo; -> export default foo;
|
|
42
48
|
content = default_export_regex.sub(r"export default \1;", content)
|
|
43
49
|
|
|
44
50
|
if content != original_content:
|
|
45
51
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
46
52
|
f.write(content)
|
|
47
|
-
print(f"[FIXED
|
|
53
|
+
print(f"[FIXED] {file_path}")
|
|
48
54
|
changed_files_count += 1
|
|
49
55
|
|
|
50
56
|
except Exception as e:
|
|
@@ -53,5 +59,5 @@ def fix_exports(directory):
|
|
|
53
59
|
print(f"\n✅ Conversion complete! Fixed {changed_files_count} files.")
|
|
54
60
|
|
|
55
61
|
if __name__ == "__main__":
|
|
56
|
-
print("Scanning
|
|
57
|
-
|
|
62
|
+
print("Scanning codebase to polyfill CJS/ESM conflicts...\n")
|
|
63
|
+
enhance_esm_compatibility(TARGET_DIR)
|
package/package.json
CHANGED
package/src/commands/login.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { createRequire as _createRequire } from 'module';
|
|
2
|
+
const require = _createRequire(import.meta.url);
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
import http from 'http';
|
|
3
6
|
import crypto from 'crypto';
|
|
@@ -10,19 +13,12 @@ import { saveCredentials, clearCredentials, loadCredentials, CREDENTIALS_PATH }
|
|
|
10
13
|
// That's the one, deliberately-fixed anchor point everything else in this
|
|
11
14
|
// file is relative to; if IT ever needs to move, that's a cobinar.com
|
|
12
15
|
// redeploy, not a new npm release everyone has to go update.
|
|
13
|
-
const SERVICES_ENDPOINT = 'https://cobinar.com/api/services';
|
|
16
|
+
const SERVICES_ENDPOINT = 'https://8bnk.dalus.cobinar.com/api/services';
|
|
14
17
|
// Only used if the discovery call above fails outright (a network hiccup,
|
|
15
18
|
// cobinar.com briefly unreachable) -- not the source of truth, just keeps
|
|
16
19
|
// `dalus login` from being completely dead during a transient outage of
|
|
17
20
|
// that one lookup. --web-base always wins over both.
|
|
18
21
|
const FALLBACK_DALUS_BASE = 'https://dalus.cobinar.com';
|
|
19
|
-
// cobinar-developers-worker — mints the actual bearer token dashboard-worker
|
|
20
|
-
// checks (a signed "workerToken"), from the one-time code dalus's own
|
|
21
|
-
// worker hands us. Not the same thing as dalus.cobinar.com itself or as
|
|
22
|
-
// auth.cobinar.com (Cobinar's general sign-in provider, which dalus's
|
|
23
|
-
// worker is one client of) — those are three separate services. See the
|
|
24
|
-
// comment on exchangeSsoCode below for the exact handoff.
|
|
25
|
-
const DEFAULT_DEVELOPERS_BASE = 'https://worker.dashboard.cobinar.com';
|
|
26
22
|
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
27
23
|
|
|
28
24
|
function ask(question) {
|
|
@@ -33,14 +29,7 @@ function ask(question) {
|
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
function openBrowser(url) {
|
|
36
|
-
|
|
37
|
-
// alone isn't quite enough on Windows: `cmd /c start` hands its arguments
|
|
38
|
-
// to cmd.exe's OWN re-parsing of the whole line as a new command, where an
|
|
39
|
-
// unescaped "&" means "run two commands", not "a literal character in a
|
|
40
|
-
// URL" — which is exactly what truncated a real sign-in URL at its first
|
|
41
|
-
// "&" here before. rundll32 is a normal Win32 program, not a shell, so it
|
|
42
|
-
// never re-interprets "&" (or anything else) in its argument at all.
|
|
43
|
-
const done = () => {}; // best-effort; the URL is always printed too
|
|
32
|
+
const done = () => {};
|
|
44
33
|
if (process.platform === 'darwin') execFile('open', [url], done);
|
|
45
34
|
else if (process.platform === 'win32') execFile('rundll32', ['url.dll,FileProtocolHandler', url], done);
|
|
46
35
|
else execFile('xdg-open', [url], done);
|
|
@@ -66,10 +55,6 @@ function getJson(urlString) {
|
|
|
66
55
|
});
|
|
67
56
|
}
|
|
68
57
|
|
|
69
|
-
// The one lookup this whole file depends on: where does dalus's own worker
|
|
70
|
-
// (the login page browserLogin below sends people to) currently live. See
|
|
71
|
-
// SERVICES_ENDPOINT's comment above for why this is a runtime fetch and not
|
|
72
|
-
// a constant.
|
|
73
58
|
async function discoverDalusBase() {
|
|
74
59
|
try {
|
|
75
60
|
const { status, json } = await getJson(SERVICES_ENDPOINT);
|
|
@@ -82,66 +67,7 @@ async function discoverDalusBase() {
|
|
|
82
67
|
return FALLBACK_DALUS_BASE;
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
function
|
|
86
|
-
return new Promise((resolve, reject) => {
|
|
87
|
-
const url = new URL(urlString);
|
|
88
|
-
const data = JSON.stringify(body);
|
|
89
|
-
const req = require(url.protocol === 'http:' ? 'http' : 'https').request(
|
|
90
|
-
{
|
|
91
|
-
hostname: url.hostname,
|
|
92
|
-
port: url.port || (url.protocol === 'http:' ? 80 : 443),
|
|
93
|
-
path: url.pathname + url.search,
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
96
|
-
},
|
|
97
|
-
(res) => {
|
|
98
|
-
let out = '';
|
|
99
|
-
res.on('data', (c) => (out += c));
|
|
100
|
-
res.on('end', () => {
|
|
101
|
-
let parsed = null;
|
|
102
|
-
try { parsed = JSON.parse(out); } catch { /* leaves parsed null below */ }
|
|
103
|
-
resolve({ status: res.statusCode, json: parsed });
|
|
104
|
-
});
|
|
105
|
-
},
|
|
106
|
-
);
|
|
107
|
-
req.on('error', reject);
|
|
108
|
-
req.setTimeout(REQUEST_TIMEOUT_MS, () => req.destroy(new Error('Request timed out')));
|
|
109
|
-
req.write(data);
|
|
110
|
-
req.end();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// The other half of cobinar-developers-worker's POST /auth/redeem-code:
|
|
115
|
-
// cobinar.com's callback page writes { uid, email, name, picture } to a KV
|
|
116
|
-
// namespace it shares with cobinar-developers-worker, keyed by a one-time
|
|
117
|
-
// code, and hands US that code (over the loopback server below, once the
|
|
118
|
-
// browser gets there). Redeeming it here — not on cobinar.com's server, and
|
|
119
|
-
// not in the browser — is what actually mints the workerToken dashboard-
|
|
120
|
-
// worker will accept; nothing before this point has needed dashboard-worker
|
|
121
|
-
// to trust anything, since a workerToken is the first credential in this
|
|
122
|
-
// whole flow it actually checks.
|
|
123
|
-
async function exchangeSsoCode(developersBase, code) {
|
|
124
|
-
const { status, json } = await postJson(`${developersBase}/auth/redeem-code`, { code });
|
|
125
|
-
if (status !== 200 || !json || typeof json.workerToken !== 'string') {
|
|
126
|
-
const detail = json && json.error ? json.error : `HTTP ${status}`;
|
|
127
|
-
throw new Error(`Could not finish signing in (${detail}). Run "dalus login" again.`);
|
|
128
|
-
}
|
|
129
|
-
return json;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Starts a one-shot local server, opens dalus's own worker (wherever
|
|
133
|
-
// discoverDalusBase() says it currently lives) to sign in, and waits for
|
|
134
|
-
// its /login/callback page to POST a one-time SSO code back here once the
|
|
135
|
-
// auth.cobinar.com round trip finishes. dalus's worker has its own
|
|
136
|
-
// client_id/secret, registered independently of cobinar.com's — see that
|
|
137
|
-
// worker's public/login/index.html and public/login/callback.html for the
|
|
138
|
-
// other half of this handshake, and exchangeSsoCode above for what happens
|
|
139
|
-
// to the code once it arrives here.
|
|
140
|
-
function browserLogin(webBase, developersBase) {
|
|
141
|
-
// "ls" (local state): proves the browser tab that finishes is the one
|
|
142
|
-
// THIS process opened, not some other local process guessing the port
|
|
143
|
-
// and racing it — the port alone isn't a secret (any local process can
|
|
144
|
-
// see what's listening), this is.
|
|
70
|
+
function browserLogin(webBase) {
|
|
145
71
|
const ls = crypto.randomBytes(24).toString('hex');
|
|
146
72
|
const allowedOrigin = new URL(webBase).origin;
|
|
147
73
|
|
|
@@ -160,7 +86,7 @@ function browserLogin(webBase, developersBase) {
|
|
|
160
86
|
let body = '';
|
|
161
87
|
req.on('data', (chunk) => {
|
|
162
88
|
body += chunk;
|
|
163
|
-
if (body.length > 1e6) req.destroy();
|
|
89
|
+
if (body.length > 1e6) req.destroy();
|
|
164
90
|
});
|
|
165
91
|
req.on('end', async () => {
|
|
166
92
|
let parsed = null;
|
|
@@ -169,17 +95,21 @@ function browserLogin(webBase, developersBase) {
|
|
|
169
95
|
if (!parsed || parsed.ls !== ls || typeof parsed.code !== 'string' || !parsed.code) {
|
|
170
96
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
171
97
|
res.end(JSON.stringify({ ok: false }));
|
|
172
|
-
return;
|
|
98
|
+
return;
|
|
173
99
|
}
|
|
174
100
|
|
|
175
101
|
try {
|
|
176
|
-
|
|
102
|
+
// 1. Acknowledge successful hand-off immediately
|
|
177
103
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
178
104
|
res.end(JSON.stringify({ ok: true }));
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
105
|
+
|
|
106
|
+
// 2. Allow proxy time to flush the HTTP response back to the browser before closing server
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
finish(null, {
|
|
109
|
+
token: parsed.code,
|
|
110
|
+
user: parsed.user
|
|
111
|
+
});
|
|
112
|
+
}, 500);
|
|
183
113
|
} catch (err) {
|
|
184
114
|
res.writeHead(502, { 'Content-Type': 'application/json' });
|
|
185
115
|
res.end(JSON.stringify({ ok: false, error: err.message }));
|
|
@@ -204,14 +134,6 @@ function browserLogin(webBase, developersBase) {
|
|
|
204
134
|
|
|
205
135
|
server.listen(0, '127.0.0.1', () => {
|
|
206
136
|
const port = server.address().port;
|
|
207
|
-
// port and ls travel together as one opaque value, not two params
|
|
208
|
-
// joined by "&" — see openBrowser's comment above for exactly why
|
|
209
|
-
// that character caused real trouble here. Neither value can ever
|
|
210
|
-
// contain a "." (port is digits, ls is hex), so joining/splitting on
|
|
211
|
-
// it is unambiguous. The trailing slash on /login/ is deliberate
|
|
212
|
-
// too: requesting the bare path and hoping a redirect adds the slash
|
|
213
|
-
// back is one more hop that could, in principle, drop the query
|
|
214
|
-
// string — this just starts at the real address.
|
|
215
137
|
const loginUrl = `${webBase}/login/?d=${port}.${ls}`;
|
|
216
138
|
console.log('Opening your browser to sign in with Cobinar...');
|
|
217
139
|
console.log(`If it doesn't open automatically, visit:\n ${loginUrl}\n`);
|
|
@@ -239,10 +161,6 @@ async function loginCommand(args) {
|
|
|
239
161
|
const tokenArgIdx = args.indexOf('--token');
|
|
240
162
|
const apiBaseArgIdx = args.indexOf('--api-base');
|
|
241
163
|
|
|
242
|
-
// --token stays as a manual escape hatch for CI/headless boxes that
|
|
243
|
-
// can't open a browser at all — not the default anymore, but not worth
|
|
244
|
-
// removing until there's a real headless flow (one-time code / email,
|
|
245
|
-
// planned later) to replace it with.
|
|
246
164
|
if (tokenArgIdx !== -1) {
|
|
247
165
|
const token = args[tokenArgIdx + 1];
|
|
248
166
|
const apiBase = apiBaseArgIdx !== -1 ? args[apiBaseArgIdx + 1] : await ask('Cobinar dashboard-worker URL: ');
|
|
@@ -258,12 +176,10 @@ async function loginCommand(args) {
|
|
|
258
176
|
|
|
259
177
|
const webBaseArgIdx = args.indexOf('--web-base');
|
|
260
178
|
const webBase = (webBaseArgIdx !== -1 ? args[webBaseArgIdx + 1] : await discoverDalusBase()).replace(/\/$/, '');
|
|
261
|
-
const developersBaseArgIdx = args.indexOf('--developers-base');
|
|
262
|
-
const developersBase = (developersBaseArgIdx !== -1 ? args[developersBaseArgIdx + 1] : DEFAULT_DEVELOPERS_BASE).replace(/\/$/, '');
|
|
263
179
|
|
|
264
180
|
let result;
|
|
265
181
|
try {
|
|
266
|
-
result = await browserLogin(webBase
|
|
182
|
+
result = await browserLogin(webBase);
|
|
267
183
|
} catch (err) {
|
|
268
184
|
console.error(err.message);
|
|
269
185
|
process.exitCode = 1;
|
|
@@ -285,4 +201,4 @@ async function loginCommand(args) {
|
|
|
285
201
|
console.log('Run "dalus forge" from a project directory to deploy.');
|
|
286
202
|
}
|
|
287
203
|
|
|
288
|
-
export { loginCommand, browserLogin,
|
|
204
|
+
export { loginCommand, browserLogin, discoverDalusBase };
|