npcsh 1.1.14__py3-none-any.whl → 1.1.16__py3-none-any.whl
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.
- npcsh/_state.py +533 -80
- npcsh/mcp_server.py +2 -1
- npcsh/npc.py +84 -32
- npcsh/npc_team/alicanto.npc +22 -1
- npcsh/npc_team/corca.npc +28 -9
- npcsh/npc_team/frederic.npc +25 -4
- npcsh/npc_team/guac.npc +22 -0
- npcsh/npc_team/jinxs/bin/nql.jinx +141 -0
- npcsh/npc_team/jinxs/bin/sync.jinx +230 -0
- {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/bin}/vixynt.jinx +8 -30
- npcsh/npc_team/jinxs/bin/wander.jinx +152 -0
- npcsh/npc_team/jinxs/lib/browser/browser_action.jinx +220 -0
- npcsh/npc_team/jinxs/lib/browser/browser_screenshot.jinx +40 -0
- npcsh/npc_team/jinxs/lib/browser/close_browser.jinx +14 -0
- npcsh/npc_team/jinxs/lib/browser/open_browser.jinx +43 -0
- npcsh/npc_team/jinxs/lib/computer_use/click.jinx +23 -0
- npcsh/npc_team/jinxs/lib/computer_use/key_press.jinx +26 -0
- npcsh/npc_team/jinxs/lib/computer_use/launch_app.jinx +37 -0
- npcsh/npc_team/jinxs/lib/computer_use/screenshot.jinx +23 -0
- npcsh/npc_team/jinxs/lib/computer_use/type_text.jinx +27 -0
- npcsh/npc_team/jinxs/lib/computer_use/wait.jinx +21 -0
- {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/edit_file.jinx +3 -3
- {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/load_file.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/paste.jinx +134 -0
- {npcsh-1.1.14.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/lib/core}/search.jinx +2 -1
- npcsh/npc_team/jinxs/{code → lib/core}/sh.jinx +2 -8
- npcsh/npc_team/jinxs/{code → lib/core}/sql.jinx +1 -1
- npcsh/npc_team/jinxs/lib/orchestration/convene.jinx +232 -0
- npcsh/npc_team/jinxs/lib/orchestration/delegate.jinx +184 -0
- npcsh/npc_team/jinxs/lib/research/arxiv.jinx +76 -0
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +101 -0
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +69 -0
- npcsh/npc_team/jinxs/{utils/core → lib/utils}/build.jinx +8 -8
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +176 -0
- npcsh/npc_team/jinxs/lib/utils/shh.jinx +17 -0
- npcsh/npc_team/jinxs/lib/utils/switch.jinx +62 -0
- npcsh/npc_team/jinxs/lib/utils/switches.jinx +61 -0
- npcsh/npc_team/jinxs/lib/utils/teamviz.jinx +205 -0
- npcsh/npc_team/jinxs/lib/utils/verbose.jinx +17 -0
- npcsh/npc_team/kadiefa.npc +19 -1
- npcsh/npc_team/plonk.npc +26 -1
- npcsh/npc_team/plonkjr.npc +22 -1
- npcsh/npc_team/sibiji.npc +23 -2
- npcsh/npcsh.py +153 -39
- npcsh/ui.py +22 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/alicanto.npc +23 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/arxiv.jinx +76 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/browser_action.jinx +220 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/browser_screenshot.jinx +40 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/build.jinx +8 -8
- npcsh-1.1.16.data/data/npcsh/npc_team/click.jinx +23 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/close_browser.jinx +14 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/convene.jinx +232 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/corca.npc +31 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/delegate.jinx +184 -0
- {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/edit_file.jinx +3 -3
- npcsh-1.1.16.data/data/npcsh/npc_team/frederic.npc +27 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/guac.npc +22 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/jinxs.jinx +176 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/kadiefa.npc +21 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/key_press.jinx +26 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/launch_app.jinx +37 -0
- {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/load_file.jinx +1 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/nql.jinx +141 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/open_browser.jinx +43 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/paper_search.jinx +101 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/paste.jinx +134 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/plonk.npc +27 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/plonkjr.npc +23 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/screenshot.jinx +23 -0
- {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/search.jinx +2 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/semantic_scholar.jinx +69 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sh.jinx +2 -8
- npcsh-1.1.16.data/data/npcsh/npc_team/shh.jinx +17 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/sibiji.npc +24 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sql.jinx +1 -1
- npcsh-1.1.16.data/data/npcsh/npc_team/switch.jinx +62 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/switches.jinx +61 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/sync.jinx +230 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/teamviz.jinx +205 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/type_text.jinx +27 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/verbose.jinx +17 -0
- {npcsh/npc_team/jinxs/utils → npcsh-1.1.16.data/data/npcsh/npc_team}/vixynt.jinx +8 -30
- npcsh-1.1.16.data/data/npcsh/npc_team/wait.jinx +21 -0
- npcsh-1.1.16.data/data/npcsh/npc_team/wander.jinx +152 -0
- {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/METADATA +399 -58
- npcsh-1.1.16.dist-info/RECORD +170 -0
- npcsh-1.1.16.dist-info/entry_points.txt +19 -0
- npcsh-1.1.16.dist-info/top_level.txt +2 -0
- project/__init__.py +1 -0
- npcsh/npc_team/foreman.npc +0 -7
- npcsh/npc_team/jinxs/modes/alicanto.jinx +0 -194
- npcsh/npc_team/jinxs/modes/corca.jinx +0 -249
- npcsh/npc_team/jinxs/modes/guac.jinx +0 -317
- npcsh/npc_team/jinxs/modes/plonk.jinx +0 -214
- npcsh/npc_team/jinxs/modes/pti.jinx +0 -170
- npcsh/npc_team/jinxs/modes/wander.jinx +0 -186
- npcsh/npc_team/jinxs/utils/agent.jinx +0 -17
- npcsh/npc_team/jinxs/utils/core/jinxs.jinx +0 -32
- npcsh-1.1.14.data/data/npcsh/npc_team/agent.jinx +0 -17
- npcsh-1.1.14.data/data/npcsh/npc_team/alicanto.jinx +0 -194
- npcsh-1.1.14.data/data/npcsh/npc_team/alicanto.npc +0 -2
- npcsh-1.1.14.data/data/npcsh/npc_team/corca.jinx +0 -249
- npcsh-1.1.14.data/data/npcsh/npc_team/corca.npc +0 -12
- npcsh-1.1.14.data/data/npcsh/npc_team/foreman.npc +0 -7
- npcsh-1.1.14.data/data/npcsh/npc_team/frederic.npc +0 -6
- npcsh-1.1.14.data/data/npcsh/npc_team/guac.jinx +0 -317
- npcsh-1.1.14.data/data/npcsh/npc_team/jinxs.jinx +0 -32
- npcsh-1.1.14.data/data/npcsh/npc_team/kadiefa.npc +0 -3
- npcsh-1.1.14.data/data/npcsh/npc_team/plonk.jinx +0 -214
- npcsh-1.1.14.data/data/npcsh/npc_team/plonk.npc +0 -2
- npcsh-1.1.14.data/data/npcsh/npc_team/plonkjr.npc +0 -2
- npcsh-1.1.14.data/data/npcsh/npc_team/pti.jinx +0 -170
- npcsh-1.1.14.data/data/npcsh/npc_team/sibiji.npc +0 -3
- npcsh-1.1.14.data/data/npcsh/npc_team/wander.jinx +0 -186
- npcsh-1.1.14.dist-info/RECORD +0 -135
- npcsh-1.1.14.dist-info/entry_points.txt +0 -9
- npcsh-1.1.14.dist-info/top_level.txt +0 -1
- /npcsh/npc_team/jinxs/{utils → bin}/roll.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → bin}/sample.jinx +0 -0
- /npcsh/npc_team/jinxs/{modes → bin}/spool.jinx +0 -0
- /npcsh/npc_team/jinxs/{modes → bin}/yap.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/computer_use}/trigger.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/core}/chat.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/core}/cmd.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/core}/compress.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/core}/ots.jinx +0 -0
- /npcsh/npc_team/jinxs/{code → lib/core}/python.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/core}/sleep.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils/core → lib/utils}/compile.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils/core → lib/utils}/help.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils/core → lib/utils}/init.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/utils}/serve.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils/core → lib/utils}/set.jinx +0 -0
- /npcsh/npc_team/jinxs/{utils → lib/utils}/usage.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npc-studio.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/spool.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/yap.jinx +0 -0
- {npcsh-1.1.14.data → npcsh-1.1.16.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/WHEEL +0 -0
- {npcsh-1.1.14.dist-info → npcsh-1.1.16.dist-info}/licenses/LICENSE +0 -0
|
@@ -41,27 +41,15 @@ steps:
|
|
|
41
41
|
except (ValueError, TypeError):
|
|
42
42
|
width = 1024
|
|
43
43
|
|
|
44
|
-
# Get model and provider
|
|
44
|
+
# Get model and provider from context or environment
|
|
45
45
|
model = context.get('model')
|
|
46
46
|
provider = context.get('provider')
|
|
47
|
-
|
|
48
|
-
# Use NPC's model/provider as fallback
|
|
49
|
-
if not model and npc and hasattr(npc, 'model') and npc.model:
|
|
50
|
-
model = npc.model
|
|
51
|
-
if not provider and npc and hasattr(npc, 'provider') and npc.provider:
|
|
52
|
-
provider = npc.provider
|
|
53
|
-
|
|
47
|
+
|
|
54
48
|
# Fallback to environment variables
|
|
55
49
|
if not model:
|
|
56
50
|
model = os.getenv('NPCSH_IMAGE_GEN_MODEL')
|
|
57
51
|
if not provider:
|
|
58
52
|
provider = os.getenv('NPCSH_IMAGE_GEN_PROVIDER')
|
|
59
|
-
|
|
60
|
-
# Final hardcoded fallbacks if nothing else is set
|
|
61
|
-
if not model:
|
|
62
|
-
model = "runwayml/stable-diffusion-v1-5"
|
|
63
|
-
if not provider:
|
|
64
|
-
provider = "diffusers"
|
|
65
53
|
|
|
66
54
|
# Parse attachments
|
|
67
55
|
input_images = []
|
|
@@ -93,12 +81,11 @@ steps:
|
|
|
93
81
|
images_list = result
|
|
94
82
|
|
|
95
83
|
saved_files = []
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
|
|
98
85
|
for i, image in enumerate(images_list):
|
|
99
86
|
if image is None:
|
|
100
87
|
continue
|
|
101
|
-
|
|
88
|
+
|
|
102
89
|
# Determine output filename
|
|
103
90
|
if output_name and str(output_name).strip():
|
|
104
91
|
base_name, ext = os.path.splitext(os.path.expanduser(str(output_name)))
|
|
@@ -111,25 +98,16 @@ steps:
|
|
|
111
98
|
os.path.expanduser("~/.npcsh/images/")
|
|
112
99
|
+ f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
|
|
113
100
|
)
|
|
114
|
-
|
|
101
|
+
|
|
115
102
|
# Save image to file
|
|
116
103
|
image.save(current_output_file)
|
|
117
104
|
saved_files.append(current_output_file)
|
|
118
105
|
|
|
119
|
-
# Convert image to base64 and create an HTML <img> tag
|
|
120
|
-
with open(current_output_file, 'rb') as f:
|
|
121
|
-
img_data = base64.b64encode(f.read()).decode()
|
|
122
|
-
# Using raw HTML <img> tag with data URI
|
|
123
|
-
html_image_tags.append(f'<img src="data:image/png;base64,{img_data}" alt="Generated Image {i+1}" style="max-width: 100%; display: block; margin-top: 10px;">')
|
|
124
|
-
|
|
125
106
|
if saved_files:
|
|
126
|
-
|
|
107
|
+
output = f"Image(s) generated: {', '.join(saved_files)}"
|
|
127
108
|
if input_images:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
output = output_text_message # Keep the text message clean
|
|
131
|
-
output += f"\n\nThe image files have been saved and are ready to view."
|
|
132
|
-
output += "\n\n" + "\n".join(html_image_tags) # Append all HTML <img> tags to the output
|
|
109
|
+
output = f"Image(s) edited: {', '.join(saved_files)}"
|
|
110
|
+
context['generated_images'] = saved_files
|
|
133
111
|
else:
|
|
134
112
|
output = "No images were generated."
|
|
135
113
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
jinx_name: wander
|
|
2
|
+
description: Creative daydreaming with probabilistic temperature shifts mid-stream
|
|
3
|
+
inputs:
|
|
4
|
+
- problem
|
|
5
|
+
steps:
|
|
6
|
+
- name: wander_explore
|
|
7
|
+
engine: python
|
|
8
|
+
code: |
|
|
9
|
+
import random
|
|
10
|
+
from termcolor import colored
|
|
11
|
+
from npcpy.llm_funcs import get_llm_response
|
|
12
|
+
|
|
13
|
+
problem = context.get('problem', '')
|
|
14
|
+
if not problem:
|
|
15
|
+
context['output'] = "Need a topic to wander about."
|
|
16
|
+
exit()
|
|
17
|
+
|
|
18
|
+
model = 'gpt-4.1-nano'
|
|
19
|
+
provider = 'openai'
|
|
20
|
+
low_temp = 0.5
|
|
21
|
+
high_temp = 1.9
|
|
22
|
+
sample_rate = 0.4
|
|
23
|
+
interrupt_prob = 0.02
|
|
24
|
+
|
|
25
|
+
print(f"""
|
|
26
|
+
██╗ ██╗ █████╗ ███╗ ██╗██████╗ ███████╗██████╗
|
|
27
|
+
██║ ██║██╔══██╗████╗ ██║██╔══██╗██╔════╝██╔══██╗
|
|
28
|
+
██║ █╗ ██║███████║██╔██╗ ██║██║ ██║█████╗ ██████╔╝
|
|
29
|
+
██║███╗██║██╔══██║██║╚██╗██║██║ ██║██╔══╝ ██╔══██╗
|
|
30
|
+
╚███╔███╔╝██║ ██║██║ ╚████║██████╔╝███████╗██║ ██║
|
|
31
|
+
╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═╝ ╚═╝
|
|
32
|
+
|
|
33
|
+
Wandering: {problem}
|
|
34
|
+
""")
|
|
35
|
+
|
|
36
|
+
print(colored(f"--- Low temp stream ({low_temp}) ---", "cyan"))
|
|
37
|
+
|
|
38
|
+
low_prompt = f"Think about: {problem}"
|
|
39
|
+
resp = get_llm_response(low_prompt, model=model, provider=provider, temperature=low_temp, stream=True)
|
|
40
|
+
|
|
41
|
+
# Get the actual stream from the response
|
|
42
|
+
stream = resp.get('response') if isinstance(resp, dict) else resp
|
|
43
|
+
|
|
44
|
+
low_output = ""
|
|
45
|
+
interrupted = False
|
|
46
|
+
|
|
47
|
+
for chunk in stream:
|
|
48
|
+
if hasattr(chunk, 'choices') and chunk.choices:
|
|
49
|
+
delta = chunk.choices[0].delta
|
|
50
|
+
text = getattr(delta, 'content', '') or ''
|
|
51
|
+
elif isinstance(chunk, dict):
|
|
52
|
+
text = chunk.get('content', '') or chunk.get('response', '')
|
|
53
|
+
else:
|
|
54
|
+
text = ''
|
|
55
|
+
|
|
56
|
+
if text:
|
|
57
|
+
print(text, end='', flush=True)
|
|
58
|
+
low_output += text
|
|
59
|
+
|
|
60
|
+
if random.random() < interrupt_prob:
|
|
61
|
+
print(colored("\n[INTERRUPT]", "yellow"))
|
|
62
|
+
interrupted = True
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
print()
|
|
66
|
+
|
|
67
|
+
print(colored(f"\n--- High temp stream ({high_temp}) ---", "cyan"))
|
|
68
|
+
|
|
69
|
+
high_prompt = f"{low_output}\n\nContinue:"
|
|
70
|
+
resp = get_llm_response(high_prompt, model=model, provider=provider, temperature=high_temp, stream=True)
|
|
71
|
+
stream = resp.get('response') if isinstance(resp, dict) else resp
|
|
72
|
+
|
|
73
|
+
high_output = ""
|
|
74
|
+
for chunk in stream:
|
|
75
|
+
if hasattr(chunk, 'choices') and chunk.choices:
|
|
76
|
+
delta = chunk.choices[0].delta
|
|
77
|
+
text = getattr(delta, 'content', '') or ''
|
|
78
|
+
elif isinstance(chunk, dict):
|
|
79
|
+
text = chunk.get('content', '') or chunk.get('response', '')
|
|
80
|
+
else:
|
|
81
|
+
text = ''
|
|
82
|
+
|
|
83
|
+
if text:
|
|
84
|
+
print(text, end='', flush=True)
|
|
85
|
+
high_output += text
|
|
86
|
+
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
lines = [l for l in high_output.split('\n') if l.strip()]
|
|
90
|
+
sample_size = max(1, int(len(lines) * sample_rate))
|
|
91
|
+
sampled = random.sample(lines, sample_size) if lines else [high_output]
|
|
92
|
+
|
|
93
|
+
print(colored("\n=== SAMPLED INSIGHTS ===", "yellow"))
|
|
94
|
+
fragments_text = chr(10).join(sampled)
|
|
95
|
+
print(fragments_text)
|
|
96
|
+
|
|
97
|
+
print(colored("\n=== SYNTHESIS ===", "green"))
|
|
98
|
+
|
|
99
|
+
synthesis_prompt = f"""You are a mad scientist oracle. The gibberish below contains hidden truths.
|
|
100
|
+
|
|
101
|
+
QUESTION: {problem}
|
|
102
|
+
|
|
103
|
+
CHAOS FRAGMENTS:
|
|
104
|
+
{fragments_text}
|
|
105
|
+
|
|
106
|
+
RULES:
|
|
107
|
+
1. You MUST use AT LEAST HALF of the fragments above - quote them directly
|
|
108
|
+
2. Make WILD CREATIVE LEAPS - not academic, not safe, not obvious
|
|
109
|
+
3. Find patterns in the noise like reading entrails or tea leaves
|
|
110
|
+
4. Foreign text, Unicode garbage, code snippets - ALL are omens with meaning
|
|
111
|
+
5. Puns, wordplay, phonetic similarities - all valid connections
|
|
112
|
+
6. The weirder the connection, the better
|
|
113
|
+
7. NO HEDGING. No "this suggests" or "perhaps". Be BOLD. Be CERTAIN.
|
|
114
|
+
|
|
115
|
+
OUTPUT 3 WILD HYPOTHESES:
|
|
116
|
+
For each: Quote the fragments you're using -> Make your creative leap -> State the bold claim
|
|
117
|
+
|
|
118
|
+
These must be ideas that COULD NOT exist without this specific chaos. Surprise us. Make us see {problem} in a way nobody has before."""
|
|
119
|
+
|
|
120
|
+
resp = get_llm_response(synthesis_prompt, model=model, provider=provider, temperature=0.5, stream=True)
|
|
121
|
+
stream = resp.get('response') if isinstance(resp, dict) else resp
|
|
122
|
+
|
|
123
|
+
synthesis = ""
|
|
124
|
+
for chunk in stream:
|
|
125
|
+
if hasattr(chunk, 'choices') and chunk.choices:
|
|
126
|
+
delta = chunk.choices[0].delta
|
|
127
|
+
text = getattr(delta, 'content', '') or ''
|
|
128
|
+
elif isinstance(chunk, dict):
|
|
129
|
+
text = chunk.get('content', '') or chunk.get('response', '')
|
|
130
|
+
else:
|
|
131
|
+
text = ''
|
|
132
|
+
|
|
133
|
+
if text:
|
|
134
|
+
print(text, end='', flush=True)
|
|
135
|
+
synthesis += text
|
|
136
|
+
|
|
137
|
+
print()
|
|
138
|
+
|
|
139
|
+
full_output = f"""Wandering: {problem}
|
|
140
|
+
|
|
141
|
+
--- Low temp stream ({low_temp}) ---
|
|
142
|
+
{low_output}
|
|
143
|
+
|
|
144
|
+
--- High temp stream ({high_temp}) ---
|
|
145
|
+
{high_output}
|
|
146
|
+
|
|
147
|
+
=== SAMPLED INSIGHTS ===
|
|
148
|
+
{fragments_text}
|
|
149
|
+
|
|
150
|
+
=== SYNTHESIS ===
|
|
151
|
+
{synthesis}"""
|
|
152
|
+
context['output'] = full_output
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
jinx_name: browser_action
|
|
2
|
+
description: |
|
|
3
|
+
Perform an action in the browser. Actions:
|
|
4
|
+
- click: Click element
|
|
5
|
+
- type: Type text into element (clears first)
|
|
6
|
+
- type_and_enter: Type text and press Enter
|
|
7
|
+
- set_value: Force set value via JS (bypasses date pickers/validation)
|
|
8
|
+
- select: Select dropdown option by visible text
|
|
9
|
+
- wait: Wait for element to appear
|
|
10
|
+
- scroll: Scroll page (up/down/to element)
|
|
11
|
+
- get_text: Get text from element
|
|
12
|
+
- get_page: Get page title, URL, and visible text
|
|
13
|
+
- get_elements: Get interactive elements with their selectors
|
|
14
|
+
- press_key: Press a key (enter, tab, escape, etc)
|
|
15
|
+
Selectors: CSS (#id, .class, input[name="x"]) or xpath://... for XPath
|
|
16
|
+
inputs:
|
|
17
|
+
- action:
|
|
18
|
+
description: "Action: click, type, type_and_enter, set_value, select, wait, scroll, get_text, get_page, get_elements, press_key"
|
|
19
|
+
- selector:
|
|
20
|
+
description: "CSS selector or XPath (prefix xpath: for XPath)"
|
|
21
|
+
default: ""
|
|
22
|
+
- value:
|
|
23
|
+
description: "Value for type/select, or scroll direction, or key name"
|
|
24
|
+
default: ""
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- name: browser_action
|
|
28
|
+
engine: python
|
|
29
|
+
code: |
|
|
30
|
+
from selenium.webdriver.common.by import By
|
|
31
|
+
from selenium.webdriver.support.ui import WebDriverWait, Select
|
|
32
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
33
|
+
from selenium.webdriver.common.keys import Keys
|
|
34
|
+
from npcpy.work.browser import get_current_driver
|
|
35
|
+
|
|
36
|
+
action = context.get('action', '').lower()
|
|
37
|
+
selector = context.get('selector', '')
|
|
38
|
+
value = context.get('value', '')
|
|
39
|
+
|
|
40
|
+
driver = get_current_driver()
|
|
41
|
+
if not driver:
|
|
42
|
+
output = "No active browser. Use open_browser first."
|
|
43
|
+
exit()
|
|
44
|
+
|
|
45
|
+
def find_element(sel):
|
|
46
|
+
if sel.startswith('xpath:'):
|
|
47
|
+
return driver.find_element(By.XPATH, sel[6:])
|
|
48
|
+
else:
|
|
49
|
+
return driver.find_element(By.CSS_SELECTOR, sel)
|
|
50
|
+
|
|
51
|
+
def wait_for_element(sel, timeout=10):
|
|
52
|
+
if sel.startswith('xpath:'):
|
|
53
|
+
return WebDriverWait(driver, timeout).until(
|
|
54
|
+
EC.presence_of_element_located((By.XPATH, sel[6:]))
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
return WebDriverWait(driver, timeout).until(
|
|
58
|
+
EC.presence_of_element_located((By.CSS_SELECTOR, sel))
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
if action == 'click':
|
|
63
|
+
elem = wait_for_element(selector)
|
|
64
|
+
elem.click()
|
|
65
|
+
output = "Clicked: {}".format(selector)
|
|
66
|
+
|
|
67
|
+
elif action == 'type':
|
|
68
|
+
elem = wait_for_element(selector)
|
|
69
|
+
elem.click() # Focus first
|
|
70
|
+
elem.clear()
|
|
71
|
+
elem.send_keys(value)
|
|
72
|
+
output = "Typed '{}' into {}".format(value, selector)
|
|
73
|
+
|
|
74
|
+
elif action == 'set_value':
|
|
75
|
+
# Force set value via JS, bypasses validation/calendars
|
|
76
|
+
elem = wait_for_element(selector)
|
|
77
|
+
driver.execute_script("arguments[0].value = arguments[1]; arguments[0].dispatchEvent(new Event('input', {bubbles: true})); arguments[0].dispatchEvent(new Event('change', {bubbles: true}));", elem, value)
|
|
78
|
+
output = "Set value '{}' on {}".format(value, selector)
|
|
79
|
+
|
|
80
|
+
elif action == 'type_and_enter':
|
|
81
|
+
elem = wait_for_element(selector)
|
|
82
|
+
elem.clear()
|
|
83
|
+
elem.send_keys(value)
|
|
84
|
+
elem.send_keys(Keys.RETURN)
|
|
85
|
+
output = "Typed '{}' and pressed Enter".format(value)
|
|
86
|
+
|
|
87
|
+
elif action == 'select':
|
|
88
|
+
elem = wait_for_element(selector)
|
|
89
|
+
select = Select(elem)
|
|
90
|
+
select.select_by_visible_text(value)
|
|
91
|
+
output = "Selected '{}' in {}".format(value, selector)
|
|
92
|
+
|
|
93
|
+
elif action == 'wait':
|
|
94
|
+
timeout = int(value) if value else 10
|
|
95
|
+
wait_for_element(selector, timeout)
|
|
96
|
+
output = "Element found: {}".format(selector)
|
|
97
|
+
|
|
98
|
+
elif action == 'scroll':
|
|
99
|
+
if value == 'down':
|
|
100
|
+
driver.execute_script("window.scrollBy(0, 500)")
|
|
101
|
+
elif value == 'up':
|
|
102
|
+
driver.execute_script("window.scrollBy(0, -500)")
|
|
103
|
+
elif selector:
|
|
104
|
+
elem = find_element(selector)
|
|
105
|
+
driver.execute_script("arguments[0].scrollIntoView();", elem)
|
|
106
|
+
output = "Scrolled {}".format(value or 'to element')
|
|
107
|
+
|
|
108
|
+
elif action == 'get_text':
|
|
109
|
+
elem = wait_for_element(selector)
|
|
110
|
+
output = "Text: {}".format(elem.text)
|
|
111
|
+
|
|
112
|
+
elif action == 'get_page':
|
|
113
|
+
title = driver.title
|
|
114
|
+
url = driver.current_url
|
|
115
|
+
body = driver.find_element(By.TAG_NAME, 'body')
|
|
116
|
+
text = body.text[:3000]
|
|
117
|
+
output = "Page: {} ({})\n\nContent:\n{}".format(title, url, text)
|
|
118
|
+
|
|
119
|
+
elif action == 'get_elements':
|
|
120
|
+
elements = []
|
|
121
|
+
|
|
122
|
+
def is_visible(el):
|
|
123
|
+
try:
|
|
124
|
+
return el.is_displayed() and el.size['width'] > 0
|
|
125
|
+
except:
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
def safe_selector(tag, el):
|
|
129
|
+
eid = el.get_attribute('id')
|
|
130
|
+
name = el.get_attribute('name')
|
|
131
|
+
if eid and '.' not in eid and ' ' not in eid:
|
|
132
|
+
return '#' + eid
|
|
133
|
+
elif eid:
|
|
134
|
+
return '{}[id="{}"]'.format(tag, eid)
|
|
135
|
+
elif name:
|
|
136
|
+
return '{}[name="{}"]'.format(tag, name)
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
# Get inputs
|
|
140
|
+
for inp in driver.find_elements(By.CSS_SELECTOR, 'input:not([type="hidden"])'):
|
|
141
|
+
if not is_visible(inp):
|
|
142
|
+
continue
|
|
143
|
+
sel = safe_selector('input', inp)
|
|
144
|
+
if not sel:
|
|
145
|
+
ph = inp.get_attribute('placeholder')
|
|
146
|
+
if ph:
|
|
147
|
+
sel = 'input[placeholder="{}"]'.format(ph)
|
|
148
|
+
else:
|
|
149
|
+
continue
|
|
150
|
+
info = {'tag': 'input', 'type': inp.get_attribute('type') or 'text', 'selector': sel}
|
|
151
|
+
info['placeholder'] = inp.get_attribute('placeholder') or ''
|
|
152
|
+
elements.append(info)
|
|
153
|
+
|
|
154
|
+
# Get buttons
|
|
155
|
+
for btn in driver.find_elements(By.CSS_SELECTOR, 'button, input[type="submit"], input[type="button"]'):
|
|
156
|
+
if not is_visible(btn):
|
|
157
|
+
continue
|
|
158
|
+
sel = safe_selector('button', btn)
|
|
159
|
+
if not sel and btn.text:
|
|
160
|
+
sel = 'xpath://button[contains(text(),"{}")]'.format(btn.text[:30])
|
|
161
|
+
if not sel:
|
|
162
|
+
continue
|
|
163
|
+
info = {'tag': 'button', 'selector': sel, 'text': (btn.text or '')[:50]}
|
|
164
|
+
elements.append(info)
|
|
165
|
+
|
|
166
|
+
# Get select dropdowns
|
|
167
|
+
for s in driver.find_elements(By.TAG_NAME, 'select'):
|
|
168
|
+
if not is_visible(s):
|
|
169
|
+
continue
|
|
170
|
+
sel = safe_selector('select', s)
|
|
171
|
+
if not sel:
|
|
172
|
+
continue
|
|
173
|
+
opts = [o.text for o in s.find_elements(By.TAG_NAME, 'option')[:5]]
|
|
174
|
+
info = {'tag': 'select', 'selector': sel, 'options': opts}
|
|
175
|
+
elements.append(info)
|
|
176
|
+
|
|
177
|
+
# Get links
|
|
178
|
+
for link in driver.find_elements(By.TAG_NAME, 'a')[:30]:
|
|
179
|
+
if not is_visible(link) or not link.text or len(link.text) < 2:
|
|
180
|
+
continue
|
|
181
|
+
sel = safe_selector('a', link)
|
|
182
|
+
if not sel:
|
|
183
|
+
sel = 'xpath://a[contains(text(),"{}")]'.format(link.text[:30])
|
|
184
|
+
info = {'tag': 'a', 'selector': sel, 'text': link.text[:50]}
|
|
185
|
+
elements.append(info)
|
|
186
|
+
|
|
187
|
+
output = "Found {} visible elements:\n".format(len(elements))
|
|
188
|
+
for el in elements[:40]:
|
|
189
|
+
output += "{}: {} ".format(el['tag'], el.get('selector', ''))
|
|
190
|
+
if el.get('text'):
|
|
191
|
+
output += '"{}" '.format(el['text'][:30])
|
|
192
|
+
if el.get('placeholder'):
|
|
193
|
+
output += 'placeholder="{}" '.format(el['placeholder'])
|
|
194
|
+
if el.get('options'):
|
|
195
|
+
output += "opts={} ".format(el['options'][:3])
|
|
196
|
+
output += "\n"
|
|
197
|
+
|
|
198
|
+
elif action == 'press_key':
|
|
199
|
+
key_map = {
|
|
200
|
+
'enter': Keys.RETURN, 'return': Keys.RETURN,
|
|
201
|
+
'tab': Keys.TAB,
|
|
202
|
+
'escape': Keys.ESCAPE, 'esc': Keys.ESCAPE,
|
|
203
|
+
'down': Keys.DOWN, 'up': Keys.UP,
|
|
204
|
+
'left': Keys.LEFT, 'right': Keys.RIGHT,
|
|
205
|
+
'backspace': Keys.BACKSPACE,
|
|
206
|
+
'delete': Keys.DELETE,
|
|
207
|
+
}
|
|
208
|
+
key = key_map.get(value.lower(), value)
|
|
209
|
+
if selector:
|
|
210
|
+
elem = find_element(selector)
|
|
211
|
+
elem.send_keys(key)
|
|
212
|
+
else:
|
|
213
|
+
driver.find_element(By.TAG_NAME, 'body').send_keys(key)
|
|
214
|
+
output = "Pressed key: {}".format(value)
|
|
215
|
+
|
|
216
|
+
else:
|
|
217
|
+
output = "Unknown action: {}".format(action)
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
output = "Browser action failed: {}".format(str(e))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
jinx_name: browser_screenshot
|
|
2
|
+
description: Take a screenshot of the current browser page.
|
|
3
|
+
inputs:
|
|
4
|
+
- filename:
|
|
5
|
+
description: "Optional filename for screenshot"
|
|
6
|
+
default: ""
|
|
7
|
+
|
|
8
|
+
steps:
|
|
9
|
+
- name: browser_screenshot
|
|
10
|
+
engine: python
|
|
11
|
+
code: |
|
|
12
|
+
import os
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from npcpy.work.browser import get_current_driver
|
|
15
|
+
|
|
16
|
+
filename = context.get('filename', '')
|
|
17
|
+
|
|
18
|
+
driver = get_current_driver()
|
|
19
|
+
if not driver:
|
|
20
|
+
output = "No active browser. Use open_browser first."
|
|
21
|
+
exit()
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
screenshots_dir = os.path.expanduser('~/.npcsh/screenshots')
|
|
25
|
+
os.makedirs(screenshots_dir, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
if not filename:
|
|
28
|
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
29
|
+
filename = "browser_{}.png".format(timestamp)
|
|
30
|
+
|
|
31
|
+
if not filename.endswith('.png'):
|
|
32
|
+
filename += '.png'
|
|
33
|
+
|
|
34
|
+
filepath = os.path.join(screenshots_dir, filename)
|
|
35
|
+
driver.save_screenshot(filepath)
|
|
36
|
+
|
|
37
|
+
output = "Screenshot saved: {}".format(filepath)
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
output = "Screenshot failed: {}".format(str(e))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
jinx_name: close_browser
|
|
2
|
+
description: Close the current browser session.
|
|
3
|
+
inputs: []
|
|
4
|
+
|
|
5
|
+
steps:
|
|
6
|
+
- name: close_browser
|
|
7
|
+
engine: python
|
|
8
|
+
code: |
|
|
9
|
+
from npcpy.work.browser import close_current
|
|
10
|
+
|
|
11
|
+
if close_current():
|
|
12
|
+
output = "Browser closed."
|
|
13
|
+
else:
|
|
14
|
+
output = "No active browser session."
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
jinx_name: open_browser
|
|
2
|
+
description: |
|
|
3
|
+
Open a browser and navigate to a URL. The browser stays open for follow-up commands.
|
|
4
|
+
Use this to start browser automation.
|
|
5
|
+
inputs:
|
|
6
|
+
- url:
|
|
7
|
+
description: "URL to navigate to"
|
|
8
|
+
- browser: "firefox"
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
- name: open_browser
|
|
12
|
+
engine: python
|
|
13
|
+
code: |
|
|
14
|
+
from selenium import webdriver
|
|
15
|
+
from selenium.webdriver.firefox.service import Service as FirefoxService
|
|
16
|
+
from selenium.webdriver.chrome.service import Service as ChromeService
|
|
17
|
+
from webdriver_manager.firefox import GeckoDriverManager
|
|
18
|
+
from webdriver_manager.chrome import ChromeDriverManager
|
|
19
|
+
from npcpy.work.browser import set_driver
|
|
20
|
+
import uuid
|
|
21
|
+
|
|
22
|
+
url = context.get('url', '')
|
|
23
|
+
browser_type = context.get('browser', 'firefox').lower()
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
if browser_type == 'chrome':
|
|
27
|
+
options = webdriver.ChromeOptions()
|
|
28
|
+
options.add_argument('--start-maximized')
|
|
29
|
+
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
|
|
30
|
+
else:
|
|
31
|
+
options = webdriver.FirefoxOptions()
|
|
32
|
+
driver = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install()), options=options)
|
|
33
|
+
driver.maximize_window()
|
|
34
|
+
|
|
35
|
+
driver.get(url)
|
|
36
|
+
|
|
37
|
+
session_id = str(uuid.uuid4())[:8]
|
|
38
|
+
set_driver(session_id, driver)
|
|
39
|
+
|
|
40
|
+
output = "Browser opened. Session: {}. Navigated to: {}. Title: {}".format(session_id, url, driver.title)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
import traceback
|
|
43
|
+
output = "Failed to open browser: {}\n{}".format(str(e), traceback.format_exc())
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
jinx_name: click
|
|
2
|
+
description: Click at screen coordinates (0-100 percentage)
|
|
3
|
+
inputs:
|
|
4
|
+
- x: 50 # X coordinate as percentage (0-100)
|
|
5
|
+
- y: 50 # Y coordinate as percentage (0-100)
|
|
6
|
+
|
|
7
|
+
steps:
|
|
8
|
+
- name: perform_click
|
|
9
|
+
engine: python
|
|
10
|
+
code: |
|
|
11
|
+
from npcpy.work.desktop import perform_action
|
|
12
|
+
|
|
13
|
+
x = float(context.get('x', 50))
|
|
14
|
+
y = float(context.get('y', 50))
|
|
15
|
+
messages = context.get('messages', [])
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
perform_action({'type': 'click', 'x': x, 'y': y})
|
|
19
|
+
context['output'] = f"Clicked at ({x}%, {y}%)"
|
|
20
|
+
except Exception as e:
|
|
21
|
+
context['output'] = f"Click failed: {e}"
|
|
22
|
+
|
|
23
|
+
context['messages'] = messages
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
jinx_name: key_press
|
|
2
|
+
description: |
|
|
3
|
+
Press a keyboard key or key combination.
|
|
4
|
+
Valid keys: enter, tab, escape, backspace, delete, space, up, down, left, right,
|
|
5
|
+
home, end, pageup, pagedown, f1-f12, ctrl, alt, shift, command.
|
|
6
|
+
For combinations use + like: ctrl+a, ctrl+c, ctrl+v, alt+tab, ctrl+shift+t
|
|
7
|
+
For regular letters/numbers, use type_text instead.
|
|
8
|
+
inputs:
|
|
9
|
+
- key: "enter"
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- name: perform_key
|
|
13
|
+
engine: python
|
|
14
|
+
code: |
|
|
15
|
+
from npcpy.work.desktop import perform_action
|
|
16
|
+
|
|
17
|
+
key = context.get('key', 'enter')
|
|
18
|
+
messages = context.get('messages', [])
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
perform_action({'type': 'key', 'keys': key})
|
|
22
|
+
context['output'] = "Pressed key: " + str(key)
|
|
23
|
+
except Exception as e:
|
|
24
|
+
context['output'] = "Key press failed: " + str(e)
|
|
25
|
+
|
|
26
|
+
context['messages'] = messages
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
jinx_name: launch_app
|
|
2
|
+
description: Launch an application on the system
|
|
3
|
+
inputs:
|
|
4
|
+
- command: "" # Command to launch (e.g., "open -a Firefox" on macOS)
|
|
5
|
+
|
|
6
|
+
steps:
|
|
7
|
+
- name: perform_launch
|
|
8
|
+
engine: python
|
|
9
|
+
code: |
|
|
10
|
+
import platform
|
|
11
|
+
from npcpy.work.desktop import perform_action
|
|
12
|
+
|
|
13
|
+
command = context.get('command', '')
|
|
14
|
+
messages = context.get('messages', [])
|
|
15
|
+
|
|
16
|
+
if not command:
|
|
17
|
+
system = platform.system()
|
|
18
|
+
if system == "Darwin":
|
|
19
|
+
examples = "open -a Firefox, open -a TextEdit"
|
|
20
|
+
elif system == "Windows":
|
|
21
|
+
examples = "start firefox, notepad, calc"
|
|
22
|
+
else:
|
|
23
|
+
examples = "firefox &, gedit &"
|
|
24
|
+
context['output'] = f"Usage: /launch_app <command>\nExamples: {examples}"
|
|
25
|
+
context['messages'] = messages
|
|
26
|
+
exit()
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
result = perform_action({'type': 'bash', 'command': command})
|
|
30
|
+
if result.get('status') == 'success':
|
|
31
|
+
context['output'] = f"Launched: {command}"
|
|
32
|
+
else:
|
|
33
|
+
context['output'] = f"Launch failed: {result.get('message', 'unknown error')}"
|
|
34
|
+
except Exception as e:
|
|
35
|
+
context['output'] = f"Launch failed: {e}"
|
|
36
|
+
|
|
37
|
+
context['messages'] = messages
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
jinx_name: screenshot
|
|
2
|
+
description: Capture a screenshot of the current screen
|
|
3
|
+
inputs:
|
|
4
|
+
- output_path: null # Optional path to save screenshot
|
|
5
|
+
|
|
6
|
+
steps:
|
|
7
|
+
- name: capture_screenshot
|
|
8
|
+
engine: python
|
|
9
|
+
code: |
|
|
10
|
+
from npcpy.data.image import capture_screenshot
|
|
11
|
+
|
|
12
|
+
output_path = context.get('output_path')
|
|
13
|
+
messages = context.get('messages', [])
|
|
14
|
+
|
|
15
|
+
result = capture_screenshot(full=True)
|
|
16
|
+
|
|
17
|
+
if result and 'file_path' in result:
|
|
18
|
+
context['output'] = f"Screenshot saved to: {result['file_path']}"
|
|
19
|
+
context['screenshot_path'] = result['file_path']
|
|
20
|
+
else:
|
|
21
|
+
context['output'] = "Failed to capture screenshot"
|
|
22
|
+
|
|
23
|
+
context['messages'] = messages
|