pipulate 2.2__tar.gz → 2.4__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.
- {pipulate-2.2/pipulate.egg-info → pipulate-2.4}/PKG-INFO +1 -1
- {pipulate-2.2 → pipulate-2.4}/__init__.py +2 -2
- {pipulate-2.2 → pipulate-2.4}/cli.py +12 -1
- {pipulate-2.2 → pipulate-2.4}/imports/voice_synthesis.py +61 -6
- {pipulate-2.2 → pipulate-2.4/pipulate.egg-info}/PKG-INFO +1 -1
- {pipulate-2.2 → pipulate-2.4}/pyproject.toml +1 -1
- {pipulate-2.2 → pipulate-2.4}/LICENSE +0 -0
- {pipulate-2.2 → pipulate-2.4}/MANIFEST.in +0 -0
- {pipulate-2.2 → pipulate-2.4}/README.md +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/010_introduction.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/015_config.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/020_profiles.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/025_aspect.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/030_roles.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/040_hello_workflow.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/050_documentation.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/060_tasks.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/070_history.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/110_parameter_buster.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/120_link_graph.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/130_gap_analysis.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/200_workflow_genesis.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/210_widget_examples.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/220_roadmap.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/230_dev_assistant.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/240_simon_mcp.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/300_blank_placeholder.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/400_botify_trifecta.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/440_browser_automation.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/450_stream_simulator.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/510_text_field.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/520_text_area.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/530_dropdown.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/540_checkboxes.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/550_radios.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/560_range.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/570_switch.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/580_upload.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/610_markdown.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/620_mermaid.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/630_prism.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/640_javascript.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/710_pandas.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/720_rich.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/730_matplotlib.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/810_webbrowser.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/820_selenium.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/apps/830_pico_slider.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/config.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/__init__.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/ai_dictdb.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/ai_tool_discovery_simple_parser.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/append_only_conversation.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/ascii_displays.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/botify/__init__.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/botify/code_generators.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/botify/true_schema_discoverer.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/botify_code_generation.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/crud.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/database_safety_wrapper.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/dom_processing/__init__.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/dom_processing/ai_dom_beautifier.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/dom_processing/enhanced_dom_processor.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/durable_backup_system.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/mcp_orchestrator.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/server_logging.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/imports/stream_orchestrator.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate/__init__.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate/core.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate.egg-info/SOURCES.txt +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate.egg-info/dependency_links.txt +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate.egg-info/entry_points.txt +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate.egg-info/requires.txt +0 -0
- {pipulate-2.2 → pipulate-2.4}/pipulate.egg-info/top_level.txt +0 -0
- {pipulate-2.2 → pipulate-2.4}/server.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/setup.cfg +0 -0
- {pipulate-2.2 → pipulate-2.4}/tests/test_mck_rep2.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/__init__.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/advanced_automation_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/botify_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/conversation_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/dom_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/keychain_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/llm_optics.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/mcp_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/scraper_tools.py +0 -0
- {pipulate-2.2 → pipulate-2.4}/tools/system_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipulate
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4
|
|
4
4
|
Summary: AI-readiness for the agentic web — local-first, Nix-reproducible workflows. The successor to AI SEO software.
|
|
5
5
|
Author-email: Mike Levin <pipulate@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -12,8 +12,8 @@ Usage:
|
|
|
12
12
|
pipulate
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
__version__ = "2.
|
|
16
|
-
__version_description__ = "
|
|
15
|
+
__version__ = "2.04"
|
|
16
|
+
__version_description__ = "So'wI' chu'"
|
|
17
17
|
# SPDX expression, single source of truth, synced into pyproject.toml by
|
|
18
18
|
# scripts/release/version_sync.py. "-or-later" (not bare AGPL-3.0, which is
|
|
19
19
|
# deprecated SPDX) because the header below grants "any later version".
|
|
@@ -478,7 +478,18 @@ def main():
|
|
|
478
478
|
sys.exit(1)
|
|
479
479
|
except Exception as e:
|
|
480
480
|
console.print(f"❌ [bold red]Unexpected error:[/bold red] {e}")
|
|
481
|
-
|
|
481
|
+
sys.exit(1)
|
|
482
|
+
# THE DISCRIMINATION QUESTION, APPLIED TO AN EXIT CODE (convicted
|
|
483
|
+
# 2026-08-03): this line sat at THIS indentation, outside every
|
|
484
|
+
# handler, so success fell through to it -- and so did failure, and
|
|
485
|
+
# interrupt, and exception. Four worlds, one printout. The console
|
|
486
|
+
# printed a green checkmark while the shell read 1, which means the
|
|
487
|
+
# EXIT-CODE PROTOCOL RULE's whole premise ("a program invoked for a
|
|
488
|
+
# decision speaks through its exit code") was unavailable here, and
|
|
489
|
+
# any `cli.py call X && echo GREEN` could never print GREEN in any
|
|
490
|
+
# world. Sibling of THE SUCCESS-ONLY WITNESS: an instrument that
|
|
491
|
+
# cannot report the outcome is the thing hiding it. Success exits 0.
|
|
492
|
+
sys.exit(0)
|
|
482
493
|
|
|
483
494
|
if __name__ == "__main__":
|
|
484
495
|
main()
|
|
@@ -47,6 +47,7 @@ class ChipVoiceSystem:
|
|
|
47
47
|
self.voice = None
|
|
48
48
|
self.voice_ready = False
|
|
49
49
|
self.current_process = None # Track the running audio process
|
|
50
|
+
self.last_error = None # Populated by synthesize_and_play on failure
|
|
50
51
|
|
|
51
52
|
if VOICE_SYNTHESIS_AVAILABLE:
|
|
52
53
|
self.setup_voice_model()
|
|
@@ -173,7 +174,19 @@ class ChipVoiceSystem:
|
|
|
173
174
|
# rare audible overlap over a permanent silent deadlock if a player hangs.
|
|
174
175
|
import fcntl
|
|
175
176
|
import time
|
|
176
|
-
|
|
177
|
+
# PER-USER LOCK PATH (convicted 2026-08-02, cold-start ride). This
|
|
178
|
+
# was a FIXED path in a shared world-readable directory, so the
|
|
179
|
+
# first user to speak created it 0644 and every OTHER user on the
|
|
180
|
+
# machine got EACCES on open(..., "w") forever. Receipt:
|
|
181
|
+
# `-rw-r--r-- 1 mike users 0 /tmp/pipulate_voice.lock` while a
|
|
182
|
+
# second account's ride printed Errno 13 at every single stop.
|
|
183
|
+
# A mode change is the WRONG fix: 0666 in /tmp is a shared-write
|
|
184
|
+
# target for anyone on the box, and the lock has no cross-user job
|
|
185
|
+
# anyway -- two users are two audio sessions. The uid suffix makes
|
|
186
|
+
# collision unrepresentable instead of merely permitted, which is
|
|
187
|
+
# THE DERIVED-PATH RULE applied to a lock file.
|
|
188
|
+
lock_path = f"/tmp/pipulate_voice.lock.{os.getuid()}"
|
|
189
|
+
lock_file = open(lock_path, "w")
|
|
177
190
|
_deadline = time.monotonic() + 30
|
|
178
191
|
while True:
|
|
179
192
|
try:
|
|
@@ -209,15 +222,34 @@ class ChipVoiceSystem:
|
|
|
209
222
|
# Use Popen instead of run to allow interruption
|
|
210
223
|
self.current_process = subprocess.Popen(
|
|
211
224
|
play_cmd,
|
|
212
|
-
stderr=subprocess.
|
|
225
|
+
stderr=subprocess.PIPE,
|
|
213
226
|
stdout=subprocess.DEVNULL
|
|
214
227
|
)
|
|
215
228
|
|
|
216
229
|
# Keep logging the original 'text' so the terminal output matches the Markdown
|
|
217
230
|
logger.info(f"🎤 Speaking (PID {self.current_process.pid}): {text[:50]}...")
|
|
218
231
|
|
|
219
|
-
#
|
|
220
|
-
|
|
232
|
+
# THE SUCCESS-ONLY WITNESS (convicted 2026-08-02, cold-start ride
|
|
233
|
+
# five): this called wait() and DISCARDED the exit code while
|
|
234
|
+
# stderr went to DEVNULL, so a player that could not open an
|
|
235
|
+
# audio device exited nonzero, printed its reason into the void,
|
|
236
|
+
# and this function returned True anyway. speak_text reported
|
|
237
|
+
# success, mother_cat printed nothing, and the human heard
|
|
238
|
+
# silence beside a green console. Mirror image of
|
|
239
|
+
# REFUSAL-ONLY WITNESS: that rule names a guard observed only
|
|
240
|
+
# refusing; this is a claim observed only succeeding, and the
|
|
241
|
+
# two are indistinguishable from broken-shut and cannot-fail
|
|
242
|
+
# respectively. Also the missing third suspect from the
|
|
243
|
+
# 2026-07-26 THE DEMO WENT SILENT todo, which named transport
|
|
244
|
+
# and engine and never looked at the subprocess exit status.
|
|
245
|
+
_stderr = self.current_process.communicate()[1]
|
|
246
|
+
rc = self.current_process.returncode
|
|
247
|
+
if rc != 0:
|
|
248
|
+
detail = (_stderr or b"").decode("utf-8", "replace").strip()
|
|
249
|
+
detail = detail.splitlines()[-1] if detail else f"exit {rc}"
|
|
250
|
+
logger.error(f"🎤 Audio playback failed (exit {rc}): {detail}")
|
|
251
|
+
self.last_error = f"playback exit {rc}: {detail}"
|
|
252
|
+
return False
|
|
221
253
|
|
|
222
254
|
return True
|
|
223
255
|
|
|
@@ -231,7 +263,16 @@ class ChipVoiceSystem:
|
|
|
231
263
|
stdout=subprocess.DEVNULL
|
|
232
264
|
)
|
|
233
265
|
logger.info(f"🎤 Speaking via nix-shell (PID {self.current_process.pid}): {text[:50]}...")
|
|
234
|
-
self.current_process.wait()
|
|
266
|
+
rc = self.current_process.wait()
|
|
267
|
+
if rc != 0:
|
|
268
|
+
# Same false green as the primary path. stderr stays
|
|
269
|
+
# DEVNULL here on purpose: this branch fires only when
|
|
270
|
+
# `play` is missing entirely, so the exit code alone is
|
|
271
|
+
# enough to stop it lying, and the DEVNULL line carries
|
|
272
|
+
# trailing whitespace that the patch transport mangles.
|
|
273
|
+
logger.error(f"🎤 Audio playback failed via nix-shell (exit {rc}).")
|
|
274
|
+
self.last_error = f"nix-shell playback exit {rc}"
|
|
275
|
+
return False
|
|
235
276
|
return True
|
|
236
277
|
except Exception as nix_e:
|
|
237
278
|
logger.error(f"🎤 Audio playback failed on {system} (nix fallback): {nix_e}")
|
|
@@ -241,7 +282,14 @@ class ChipVoiceSystem:
|
|
|
241
282
|
return False
|
|
242
283
|
|
|
243
284
|
except Exception as e:
|
|
285
|
+
# CARRY THE MESSAGE, NOT JUST THE LOG (convicted 2026-08-02): this
|
|
286
|
+
# branch returned a bare False, so speak_text's result dict had no
|
|
287
|
+
# 'error' key and mother_cat's .get("error", "unknown error") fell
|
|
288
|
+
# to the default -- printing "unknown error" one line BELOW a log
|
|
289
|
+
# line naming the exact errno and path. LAST-INCH: everything
|
|
290
|
+
# upstream knew; the surface the human reads did not.
|
|
244
291
|
logger.error(f"🎤 Voice synthesis failed: {e}")
|
|
292
|
+
self.last_error = f"{type(e).__name__}: {e}"
|
|
245
293
|
return False
|
|
246
294
|
finally:
|
|
247
295
|
# Clean up temporary file
|
|
@@ -277,15 +325,22 @@ class ChipVoiceSystem:
|
|
|
277
325
|
"details": "Piper TTS model not loaded"
|
|
278
326
|
}
|
|
279
327
|
|
|
328
|
+
self.last_error = None
|
|
280
329
|
success = self.synthesize_and_play(text)
|
|
281
330
|
|
|
282
|
-
|
|
331
|
+
result = {
|
|
283
332
|
"success": success,
|
|
284
333
|
"text": text,
|
|
285
334
|
"text_length": len(text),
|
|
286
335
|
"voice_model": "en_US-amy-low",
|
|
287
336
|
"message": "Speech synthesis completed" if success else "Speech synthesis failed"
|
|
288
337
|
}
|
|
338
|
+
# The failure branch MUST carry an 'error' key, because every caller
|
|
339
|
+
# reads .get("error", ...) and a missing key silently becomes a lie
|
|
340
|
+
# about how much was known.
|
|
341
|
+
if not success:
|
|
342
|
+
result["error"] = self.last_error or "synthesis or playback failed"
|
|
343
|
+
return result
|
|
289
344
|
|
|
290
345
|
class MemoryToVoiceConverter:
|
|
291
346
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipulate
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4
|
|
4
4
|
Summary: AI-readiness for the agentic web — local-first, Nix-reproducible workflows. The successor to AI SEO software.
|
|
5
5
|
Author-email: Mike Levin <pipulate@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pipulate"
|
|
7
7
|
# VERSION NOTE: Synced from pipulate/__init__.py.__version__ via python version_sync.py
|
|
8
|
-
version = "2.
|
|
8
|
+
version = "2.04"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Mike Levin", email="pipulate@gmail.com" },
|
|
11
11
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|