secshare-cli 0.2.0__tar.gz → 0.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: secshare-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: CLI for SecShare — zero-knowledge secret sharing
5
5
  Project-URL: Homepage, https://secshare.link
6
6
  Project-URL: Source, https://github.com/rdney/secshare
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "secshare-cli"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "CLI for SecShare — zero-knowledge secret sharing"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,4 +1,6 @@
1
1
  import json
2
+ import subprocess
3
+ import sys
2
4
  import time
3
5
  import webbrowser
4
6
  from urllib.parse import urlparse
@@ -125,15 +127,62 @@ def whoami():
125
127
  raise click.ClickException(str(e))
126
128
 
127
129
 
130
+ def _copy_to_clipboard(text: str) -> None:
131
+ if sys.platform == "darwin":
132
+ subprocess.run(["pbcopy"], input=text.encode(), check=True)
133
+ elif sys.platform == "win32":
134
+ subprocess.run(["clip"], input=text.encode(), check=True)
135
+ else:
136
+ try:
137
+ subprocess.run(["xclip", "-selection", "clipboard"], input=text.encode(), check=True)
138
+ except FileNotFoundError:
139
+ subprocess.run(["xsel", "--clipboard", "--input"], input=text.encode(), check=True)
140
+
141
+
142
+ def _schedule_clipboard_clear(seconds: int) -> None:
143
+ """Launch a detached process that clears the clipboard after `seconds`. Returns immediately."""
144
+ if sys.platform == "darwin":
145
+ cmd = f"sleep {seconds} && echo -n '' | pbcopy"
146
+ elif sys.platform == "win32":
147
+ cmd = None # handled separately
148
+ else:
149
+ cmd = f"sleep {seconds} && echo -n '' | xclip -selection clipboard 2>/dev/null || echo -n '' | xsel --clipboard --input 2>/dev/null"
150
+
151
+ try:
152
+ if sys.platform == "win32":
153
+ subprocess.Popen(
154
+ ["powershell", "-Command", f"Start-Sleep {seconds}; Set-Clipboard ''"],
155
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
156
+ creationflags=subprocess.DETACHED_PROCESS, # type: ignore[attr-defined]
157
+ )
158
+ else:
159
+ subprocess.Popen(
160
+ ["bash", "-c", cmd],
161
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
162
+ start_new_session=True,
163
+ )
164
+ except Exception:
165
+ pass # best-effort
166
+
167
+
128
168
  @cli.command()
129
169
  @click.argument("link")
130
- def get(link):
170
+ @click.option("--show", is_flag=True, help="Print plaintext to terminal.")
171
+ @click.option("--clip", is_flag=True, help="Copy to clipboard. Clears after 30s.")
172
+ def get(link, show, clip):
131
173
  """Fetch and decrypt a secret.
132
174
 
133
175
  \b
134
- Examples:
135
- secshare get https://secshare.link/s/abc123#KEY
176
+ Inject a .env file into your shell (recommended):
136
177
  eval "$(secshare get https://secshare.link/s/abc123#KEY)"
178
+
179
+ \b
180
+ Copy a secret to clipboard:
181
+ secshare get https://secshare.link/s/abc123#KEY --clip
182
+
183
+ \b
184
+ Print explicitly:
185
+ secshare get https://secshare.link/s/abc123#KEY --show
137
186
  """
138
187
  try:
139
188
  secret_id, key_fragment = _parse_link(link)
@@ -144,16 +193,43 @@ def get(link):
144
193
  except Exception as e:
145
194
  raise click.ClickException(str(e))
146
195
 
147
- # .env payload — output KEY=VALUE lines
196
+ # Detect payload type
197
+ secret_type = "secret"
198
+ content = plaintext
148
199
  try:
149
200
  parsed = json.loads(plaintext)
150
201
  if isinstance(parsed, dict) and parsed.get("__secshare_type") == "env":
151
- click.echo(parsed["content"], nl=False)
152
- return
202
+ secret_type = "env"
203
+ content = parsed["content"]
153
204
  except (json.JSONDecodeError, AttributeError):
154
205
  pass
155
206
 
156
- click.echo(plaintext, nl=False)
207
+ # --clip: copy silently regardless of TTY
208
+ if clip:
209
+ try:
210
+ _copy_to_clipboard(content)
211
+ except Exception as e:
212
+ raise click.ClickException(f"Clipboard unavailable: {e}")
213
+ _schedule_clipboard_clear(30)
214
+ click.echo("Copied to clipboard. Clears in 30s.", err=True)
215
+ return
216
+
217
+ # TTY guard
218
+ if sys.stdout.isatty() and not show:
219
+ if secret_type == "env":
220
+ raise click.ClickException(
221
+ "Refusing to print secret to terminal.\n"
222
+ " To inject into your shell: eval \"$(secshare get <link>)\"\n"
223
+ " To print explicitly: secshare get <link> --show"
224
+ )
225
+ else:
226
+ raise click.ClickException(
227
+ "Refusing to print secret to terminal.\n"
228
+ " To copy to clipboard: secshare get <link> --clip\n"
229
+ " To print explicitly: secshare get <link> --show"
230
+ )
231
+
232
+ click.echo(content, nl=False)
157
233
 
158
234
 
159
235
  @cli.command()
File without changes
File without changes