aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/src/meshbay_cli/commands
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/commands')
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/account.py14
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/chat.py4
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/completion.py10
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/context.py6
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/get.py13
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/groups.py7
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/join.py4
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/login.py4
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/commands/put.py4
9 files changed, 40 insertions, 26 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/account.py b/packages/meshbay-cli/src/meshbay_cli/commands/account.py
index 4499f0b..1fbef13 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/account.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/account.py
@@ -35,7 +35,7 @@ from ..client.passphrase import passphrase_problem, solve_captcha
from ..client.rewrap import Carried, carry
from ..client.store import FILE_KEY_NOTICE, KEY_FILE
from ..errors import AUTH, NOT_FOUND, REFUSED, USAGE, CliError
-from ..output import confirm, err, safe, sentence
+from ..output import confirm, err, safe, sentence, terminal
from .context import Context
HELP = ("your account: show it, register, verify, change or reset the passphrase, "
@@ -102,7 +102,7 @@ async def run(ctx: Context) -> int:
def _ask(prompt: str, given: str | None, what: str) -> str:
if given:
return given.strip()
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
raise CliError(USAGE, f"Name the {what}: there is no terminal to ask on.")
value = input(prompt).strip()
if not value:
@@ -121,7 +121,7 @@ def _read_first_line(path: str) -> str:
def _new_passphrase(given: str | None) -> str:
"""A new passphrase: `given`, else typed twice at a terminal; held to the floor."""
if given is None:
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
raise NoPassphrase("A new passphrase is needed and there is no terminal to "
"ask on.")
given = getpass.getpass("New passphrase: ")
@@ -224,7 +224,7 @@ async def _register(ctx: Context) -> int:
"Keep it somewhere safe, apart from your passphrase. If you forget the "
"passphrase, it is what gives you your groups back.")
del raw
- if not sys.stdin.isatty() or ctx.args.json:
+ if not terminal(sys.stdin) or ctx.args.json:
err(f"A code was sent to {safe(email)}. Then: meshbay-cli account verify CODE "
f"--email {safe(email)}")
return 0
@@ -355,7 +355,7 @@ async def _reset(ctx: Context) -> int:
raise _hub_refusal(e, "No code sent") from e
err(f"If {safe(username)} and {safe(email)} are an account's, a code is on its "
"way there (valid one hour).")
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
err(f"Then: meshbay-cli account reset -u {safe(username)} --code CODE")
return 0
code = _ask("The code: ", None, "code")
@@ -365,7 +365,7 @@ async def _reset(ctx: Context) -> int:
text = (sys.stdin.read() if a.recovery_key_file == "-"
else Path(a.recovery_key_file).read_text(encoding="utf-8"))
recovery = text.strip()
- elif sys.stdin.isatty():
+ elif terminal(sys.stdin):
recovery = input("Your recovery key (Enter if you have none): ").strip()
rkey = None
if recovery:
@@ -429,7 +429,7 @@ async def _email(ctx: Context, account: Account) -> int:
await account.call(account.hub.change_email, address, ak)
except HubError as e:
raise _hub_refusal(e, "Not changed") from e
- if ctx.args.json or not sys.stdin.isatty():
+ if ctx.args.json or not terminal(sys.stdin):
_say(ctx, {"email_verification_required": True, "email": address},
f"A code was sent to {safe(address)}. Then: meshbay-cli account email "
"--code CODE")
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/chat.py b/packages/meshbay-cli/src/meshbay_cli/commands/chat.py
index e410a6a..557592e 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/chat.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/chat.py
@@ -28,7 +28,7 @@ from ..client.groups import my_groups, open_group
from ..client.paths import group_arg, handle, resolve_group
from ..client.roster import RosterCheck
from ..errors import NODE, USAGE, CliError
-from ..output import date, err, human_size, safe, sentence
+from ..output import date, err, human_size, safe, sentence, terminal
from .context import Context
HELP = ("chat: alone, interactively; with a group, its last 10 messages (-N for N), "
@@ -90,7 +90,7 @@ async def run(ctx: Context) -> int:
if a.group is None:
if a.message is not None or a.follow:
raise CliError(USAGE, "Name the group to post to.")
- if not (sys.stdin.isatty() and sys.stdout.isatty()):
+ if not (terminal(sys.stdin) and terminal(sys.stdout)):
raise CliError(USAGE, "The interactive chat needs a terminal; name a group "
"to read it, or to post with -m.")
from ..tui.app import run_interactive
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/completion.py b/packages/meshbay-cli/src/meshbay_cli/commands/completion.py
index 831c012..0fc3f30 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/completion.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/completion.py
@@ -51,7 +51,15 @@ Register-ArgumentCompleter -Native -CommandName 'meshbay-cli', 'meshbay-cli.exe'
$index = $i
}
}
- $found = @(& meshbay-cli __complete powershell $index @words 2>$null)
+ # The client writes UTF-8; Windows PowerShell reads a program's output in the
+ # console's code page, which turns an accented name into another name.
+ $encoding = [Console]::OutputEncoding
+ try { [Console]::OutputEncoding = [Text.Encoding]::UTF8 } catch { }
+ try {
+ $found = @(& meshbay-cli __complete powershell $index @words 2>$null)
+ } finally {
+ try { [Console]::OutputEncoding = $encoding } catch { }
+ }
$quote = {
param($s)
if ($s -match '[\s''"`$&|;(){}@,#]') { "'" + ($s -replace "'", "''") + "'" } else { $s }
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/context.py b/packages/meshbay-cli/src/meshbay_cli/commands/context.py
index 827c101..0f7e842 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/context.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/context.py
@@ -12,7 +12,7 @@ from ..client.hub import DEFAULT_HUB, Hub
from ..client.paths import handle
from ..client.store import Store
from ..errors import USAGE, CliError
-from ..output import safe
+from ..output import safe, terminal
class Context:
@@ -45,7 +45,7 @@ class Context:
if value is None:
value = os.environ.get("MESHBAY_PASSWORD")
if value is None:
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
raise NoPassphrase("A passphrase is needed and there is no terminal to ask "
"on; pass --password-file or set MESHBAY_PASSWORD.")
value = getpass.getpass(f"Passphrase for {self.username or self.stored_username}"
@@ -62,7 +62,7 @@ class Context:
def _device_code(self, group: dict, pending, *, first: bool) -> bool:
"""Show the code once. Wait only where somebody is there to approve it."""
- waiting = sys.stderr.isatty() and not self.args.quiet
+ waiting = terminal(sys.stderr) and not self.args.quiet
if first:
minutes = max(1, int((pending.expires_at - time.time()) // 60))
print(f"This machine is not yet a device of yours on the node hosting "
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/get.py b/packages/meshbay-cli/src/meshbay_cli/commands/get.py
index eec73c4..33a9637 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/get.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/get.py
@@ -12,7 +12,7 @@ import os
from dataclasses import dataclass
from pathlib import Path
-from meshbay_common.paths import fold
+from meshbay_common.paths import fold, long_path
from ..client.groups import my_groups
from ..client.names import UnsafeName, local_path
@@ -147,7 +147,8 @@ async def _run(ctx: Context, account, pool: Pool) -> int:
planned.add(key)
if changed:
renamed.append((shown, target))
- if target.exists() and not target.with_name(target.name + PART_SUFFIX).exists():
+ if os.path.exists(long_path(target)) and not os.path.exists(
+ long_path(target.with_name(target.name + PART_SUFFIX))):
if a.skip:
return
if not a.force:
@@ -181,7 +182,7 @@ async def _run(ctx: Context, account, pool: Pool) -> int:
for problem in problems:
progress.note(f"meshbay-cli: {safe(problem)}")
for d in dirs:
- d.mkdir(parents=True, exist_ok=True)
+ os.makedirs(long_path(d), exist_ok=True)
progress.plan(len(jobs), sum(int(j.entry["size"]) for j in jobs))
failed = len(problems)
@@ -224,12 +225,12 @@ def _destination(dest: str | None, single_file: bool) -> tuple[Path, str | None]
return Path.cwd(), None
path = Path(dest).expanduser()
if dest.endswith(("/", os.sep)):
- path.mkdir(parents=True, exist_ok=True)
+ os.makedirs(long_path(path), exist_ok=True)
return path, None
- if path.is_dir():
+ if os.path.isdir(long_path(path)):
return path, None
if single_file:
- if not path.parent.is_dir():
+ if not os.path.isdir(long_path(path.parent)):
raise CliError(USAGE, f"{path.parent} is not a folder.")
return path.parent, path.name
raise CliError(USAGE, f"{dest} is not a folder (end it with / to create it).")
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/groups.py b/packages/meshbay-cli/src/meshbay_cli/commands/groups.py
index 04395dd..5ee1356 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/groups.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/groups.py
@@ -10,7 +10,7 @@ operator is another notion.
import contextlib
import json
import sys
-from datetime import datetime
+from datetime import UTC, datetime
from ..client.groups import my_groups
from ..client.hub import HubError
@@ -38,9 +38,12 @@ def local_time(iso: str | None) -> str:
if not iso:
return ""
try:
- return datetime.fromisoformat(iso).astimezone().strftime("%Y-%m-%d %H:%M")
+ moment = datetime.fromisoformat(iso)
except ValueError:
return ""
+ if moment.tzinfo is None: # a hub on SQLite sends UTC without saying so
+ moment = moment.replace(tzinfo=UTC)
+ return moment.astimezone().strftime("%Y-%m-%d %H:%M")
async def run(ctx: Context) -> int:
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/join.py b/packages/meshbay-cli/src/meshbay_cli/commands/join.py
index ee75c96..06494dd 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/join.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/join.py
@@ -18,7 +18,7 @@ from ..client.invite import parse_link
from ..client.paths import group_arg, handle, resolve_group
from ..client.session import DeviceLinkPending, NodeRefusal
from ..errors import AUTH, NODE, NOT_FOUND, USAGE, CliError
-from ..output import err, safe, sentence
+from ..output import err, safe, sentence, terminal
from .context import Context
from .invitations import find_invitation
@@ -102,7 +102,7 @@ async def run(ctx: Context) -> int:
except NodeRefusal as e:
if e.refusal_code != "code_required" or code:
raise
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
raise CliError(NODE, f"You are a member of {safe(name)}; its node has not "
"admitted you yet and needs your invitation's code:\n"
f" meshbay-cli join {name} --code CODE") from e
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/login.py b/packages/meshbay-cli/src/meshbay_cli/commands/login.py
index cbd16ef..f26691c 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/login.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/login.py
@@ -8,7 +8,7 @@ from ..client.account import Account
from ..client.groups import my_groups
from ..client.store import FILE_KEY_NOTICE, KEY_FILE
from ..errors import USAGE, CliError
-from ..output import err, safe, sentence
+from ..output import err, safe, sentence, terminal
from .context import Context
from .logout import sign_out_stored
@@ -24,7 +24,7 @@ def add_parser(sub, parent) -> None:
async def run(ctx: Context) -> int:
username = ctx.username or ctx.stored_username
if not username:
- if not sys.stdin.isatty():
+ if not terminal(sys.stdin):
raise CliError(USAGE, "Name the account with --user.")
username = input("Username: ").strip()
ctx.username = username
diff --git a/packages/meshbay-cli/src/meshbay_cli/commands/put.py b/packages/meshbay-cli/src/meshbay_cli/commands/put.py
index 01a7233..cf80e02 100644
--- a/packages/meshbay-cli/src/meshbay_cli/commands/put.py
+++ b/packages/meshbay-cli/src/meshbay_cli/commands/put.py
@@ -18,6 +18,8 @@ import tempfile
import threading
from pathlib import Path
+from meshbay_common.paths import long_path
+
from ..client.groups import my_groups
from ..client.paths import handle, parse, resolve_group
from ..client.pool import Pool
@@ -156,7 +158,7 @@ async def _send(ctx: Context, session, group: dict, folder: str, dirs: list[list
progress.start(safe(name))
lock = threading.Lock()
try:
- with open(job.local, "rb") as fh:
+ with open(long_path(job.local), "rb") as fh:
def read(offset: int, length: int) -> bytes:
with lock:
fh.seek(offset)