officecli-sdk 0.1.2__tar.gz → 0.1.3__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: officecli-sdk
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Thin Python SDK for the officecli resident pipe — forwards officecli commands to a running resident, no per-command process spawn.
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://officecli.ai
@@ -45,6 +45,7 @@ import json
45
45
  import time
46
46
  import socket
47
47
  import hashlib
48
+ import shutil
48
49
  import threading
49
50
  import subprocess
50
51
 
@@ -66,7 +67,8 @@ _builtin_open = open # preserved; this module defines its own open() below
66
67
  # the missing-CLI error points users at it / at install().
67
68
  _INSTALL_URL = "https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh"
68
69
  _MISSING_CLI = (
69
- "officecli CLI not found: {bin!r} is not on PATH. This SDK only forwards "
70
+ "officecli CLI not found: {bin!r} is not on PATH nor in the default install "
71
+ "location (~/.local/bin, or %LOCALAPPDATA%\\OfficeCLI on Windows). This SDK only forwards "
70
72
  "commands to the officecli binary, which must be installed separately. Install it:\n"
71
73
  " python -m officecli install # runs the official installer\n"
72
74
  " # or: curl -fsSL " + _INSTALL_URL + " | bash\n"
@@ -229,6 +231,42 @@ def _serves(ping_path, full_path, timeout=1.0):
229
231
  return a == full_path or ((_IS_MAC or _IS_WIN) and a.lower() == full_path.lower())
230
232
 
231
233
 
234
+ def _install_dir_candidate(name):
235
+ """Where the official installer (install.sh / install.ps1) drops the binary:
236
+ ~/.local/bin on macOS/Linux, %LOCALAPPDATA%\\OfficeCLI on Windows. Used only
237
+ as a PATH-miss fallback (see _resolve_binary)."""
238
+ if _IS_WIN:
239
+ base = os.environ.get("LOCALAPPDATA")
240
+ if not base:
241
+ return None
242
+ exe = name if name.lower().endswith(".exe") else name + ".exe"
243
+ return os.path.join(base, "OfficeCLI", exe)
244
+ return os.path.join(os.path.expanduser("~"), ".local", "bin", name)
245
+
246
+
247
+ def _resolve_binary(binary):
248
+ """Resolve the officecli binary to invoke. Order: explicit path (a value with
249
+ a path separator) is trusted as-is; otherwise a bare name is looked up on
250
+ PATH; if PATH misses, fall back to the official installer's known location.
251
+
252
+ Why the fallback: the installer adds its dir to PATH via the shell rc file, so
253
+ a bare 'officecli' resolves in an interactive terminal — but NOT in processes
254
+ that never sourced that rc (IDE-spawned Python, cron, systemd, CI). The binary
255
+ is still sitting at the known install path; find it there instead of failing.
256
+
257
+ Idempotent: an already-resolved absolute path passes straight through, so it's
258
+ safe to call at every entry point (create + Document)."""
259
+ if os.sep in binary or (os.altsep and os.altsep in binary):
260
+ return binary # explicit path: trust the caller
261
+ found = shutil.which(binary)
262
+ if found:
263
+ return found # on PATH: normal case
264
+ cand = _install_dir_candidate(binary) # PATH miss: try the known install dir
265
+ if cand and os.path.isfile(cand) and os.access(cand, os.X_OK):
266
+ return cand
267
+ return binary # give up; _run_cli raises the helpful error
268
+
269
+
232
270
  def _run_cli(binary, argv):
233
271
  """Run `binary <argv...>` (capturing output). A missing binary surfaces as a
234
272
  clear OfficeCliError with install guidance, not a raw FileNotFoundError."""
@@ -242,7 +280,7 @@ def _run_cli(binary, argv):
242
280
  class Document:
243
281
  def __init__(self, path, binary="officecli", timeout=30.0):
244
282
  self.path = os.path.abspath(path)
245
- self.bin = binary
283
+ self.bin = _resolve_binary(binary)
246
284
  self.timeout = timeout # connect timeout (s); the reply read blocks
247
285
  self._main, self._ping = pipe_paths(self.path)
248
286
  self._restart_lock = threading.Lock() # serialize dead-resident restarts
@@ -390,6 +428,7 @@ def create(path, *args, binary="officecli", timeout=30.0):
390
428
  another owner's active session.
391
429
  • file exists without --force → file_exists (pass "--force" to overwrite)."""
392
430
  full = os.path.abspath(path)
431
+ binary = _resolve_binary(binary)
393
432
  r = _run_cli(binary, ["create", full, *args])
394
433
  if r.returncode != 0:
395
434
  raise OfficeCliError(r.returncode, r.stderr or r.stdout)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: officecli-sdk
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Thin Python SDK for the officecli resident pipe — forwards officecli commands to a running resident, no per-command process spawn.
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://officecli.ai
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
  # (`import officecli`), like `pip install pillow` → `import PIL`. PyPI rejects
8
8
  # the bare name "officecli" as too similar to the unrelated "office-cli" project.
9
9
  name = "officecli-sdk"
10
- version = "0.1.2"
10
+ version = "0.1.3"
11
11
  description = "Thin Python SDK for the officecli resident pipe — forwards officecli commands to a running resident, no per-command process spawn."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.8"
File without changes
File without changes