@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 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 fix_exports(directory):
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
- # Matches: module.exports = { something };
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
- if file.endswith('.mjs'):
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
- # Fix object exports: module.exports = { foo }; -> export { foo };
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 EXPORTS] {file_path}")
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 .mjs files to fix CommonJS exports (module.exports)...\n")
57
- fix_exports(TARGET_DIR)
62
+ print("Scanning codebase to polyfill CJS/ESM conflicts...\n")
63
+ enhance_esm_compatibility(TARGET_DIR)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobinar/dalus",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Deploy Cobinar projects (Edge Compute, Static Hosting, Object Storage, Document DB, Vault) from the command line.",
5
5
  "bin": {
6
6
  "dalus": "./bin/dalus.mjs"
@@ -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
- // execFile, not exec no shell involved for the process WE spawn. That
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 postJson(urlString, body) {
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(); // this payload is a short code + a small profile, never anywhere near this size
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; // deliberately not finish()/reject() here — a stray or malicious request to this port shouldn't cancel a login still in progress
98
+ return;
173
99
  }
174
100
 
175
101
  try {
176
- const redeemed = await exchangeSsoCode(developersBase, parsed.code);
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
- finish(null, {
180
- token: redeemed.workerToken,
181
- user: parsed.user || { email: redeemed.cobinarEmail, name: redeemed.displayName, picture: redeemed.photoURL },
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, developersBase);
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, exchangeSsoCode, discoverDalusBase };
204
+ export { loginCommand, browserLogin, discoverDalusBase };