orrin-cli 0.1.7__tar.gz → 0.1.8__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.
- orrin_cli-0.1.8/PKG-INFO +15 -0
- orrin_cli-0.1.8/README.md +3 -0
- orrin_cli-0.1.8/orrin/chat_tui.py +139 -0
- orrin_cli-0.1.8/orrin/cli.py +451 -0
- orrin_cli-0.1.8/orrin/minimal_display.py +305 -0
- orrin_cli-0.1.8/orrin_cli.egg-info/PKG-INFO +15 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin_cli.egg-info/SOURCES.txt +2 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/pyproject.toml +1 -1
- orrin_cli-0.1.7/PKG-INFO +0 -112
- orrin_cli-0.1.7/README.md +0 -100
- orrin_cli-0.1.7/orrin/cli.py +0 -158
- orrin_cli-0.1.7/orrin_cli.egg-info/PKG-INFO +0 -112
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/LICENSE +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin/__init__.py +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin_cli.egg-info/dependency_links.txt +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin_cli.egg-info/entry_points.txt +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin_cli.egg-info/requires.txt +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/orrin_cli.egg-info/top_level.txt +0 -0
- {orrin_cli-0.1.7 → orrin_cli-0.1.8}/setup.cfg +0 -0
orrin_cli-0.1.8/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orrin-cli
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: Orrin CLI
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer
|
|
9
|
+
Requires-Dist: requests
|
|
10
|
+
Requires-Dist: platformdirs
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Orrin CLI v0.0.8
|
|
14
|
+
|
|
15
|
+
Please refer to [the docs](https://stellr-company.com/orrin/sdk) for more information over Orrin CLI.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from textual.app import App, ComposeResult
|
|
3
|
+
from textual.widgets import Header, Footer, Input, Markdown
|
|
4
|
+
from textual.containers import ScrollableContainer
|
|
5
|
+
from textual import events
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OrrinCLI(App):
|
|
9
|
+
CSS = """
|
|
10
|
+
Screen {
|
|
11
|
+
background: $background;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#chat_container {
|
|
15
|
+
height: 1fr;
|
|
16
|
+
overflow-y: auto;
|
|
17
|
+
padding: 1 2;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Markdown {
|
|
21
|
+
margin: 0 0 1 0;
|
|
22
|
+
background: $surface;
|
|
23
|
+
border: tall $primary;
|
|
24
|
+
padding: 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Style user messages differently */
|
|
28
|
+
.user {
|
|
29
|
+
border: tall $accent;
|
|
30
|
+
background: $boost;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#input {
|
|
34
|
+
dock: bottom;
|
|
35
|
+
margin: 1 2;
|
|
36
|
+
}
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, file_path: str, file_content: str):
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.file_path = file_path
|
|
42
|
+
self.file_content = file_content
|
|
43
|
+
|
|
44
|
+
self.chat_history = [
|
|
45
|
+
{
|
|
46
|
+
'role': 'system',
|
|
47
|
+
'content': '''You are **OrrinCLI**, an expert with **Orrin SDK**, Exegesis CE (Centralized Execution), and the **Stellr API**...''' # ← keep your full system prompt here
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
'role': 'assistant',
|
|
51
|
+
'content': f'I obtained the contents of the file that I am connected to. Here they are:\n\n```python\n{file_content}\n```'
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def compose(self) -> ComposeResult:
|
|
56
|
+
yield Header()
|
|
57
|
+
yield ScrollableContainer(id="chat_container")
|
|
58
|
+
yield Input(
|
|
59
|
+
placeholder="Chat with AI about this file... (press Enter to send)",
|
|
60
|
+
id="input"
|
|
61
|
+
)
|
|
62
|
+
yield Footer()
|
|
63
|
+
|
|
64
|
+
def on_mount(self) -> None:
|
|
65
|
+
container = self.query_one("#chat_container", ScrollableContainer)
|
|
66
|
+
|
|
67
|
+
# Initial welcome message
|
|
68
|
+
welcome = Markdown(
|
|
69
|
+
f"📄 **Connected to:** `{self.file_path}`\n"
|
|
70
|
+
f"File size: `{len(self.file_content)}` characters\n\n"
|
|
71
|
+
"Ask me anything about this file!\n"
|
|
72
|
+
"─" * 60
|
|
73
|
+
)
|
|
74
|
+
container.mount(welcome)
|
|
75
|
+
|
|
76
|
+
# Show the initial AI message from history
|
|
77
|
+
initial_ai = Markdown(self.chat_history[1]['content'])
|
|
78
|
+
container.mount(initial_ai)
|
|
79
|
+
|
|
80
|
+
container.scroll_end(animate=False)
|
|
81
|
+
|
|
82
|
+
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
83
|
+
user_msg = event.value.strip()
|
|
84
|
+
if not user_msg:
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
event.input.value = ""
|
|
88
|
+
|
|
89
|
+
container = self.query_one("#chat_container", ScrollableContainer)
|
|
90
|
+
|
|
91
|
+
# 1. Add User Message (styled differently)
|
|
92
|
+
user_md = Markdown(f"🧑 **You:** {user_msg}")
|
|
93
|
+
user_md.add_class("user")
|
|
94
|
+
container.mount(user_md)
|
|
95
|
+
|
|
96
|
+
# Add to history
|
|
97
|
+
self.chat_history.append({'role': 'user', 'content': user_msg})
|
|
98
|
+
|
|
99
|
+
# 2. Add a "thinking" indicator (optional but nice)
|
|
100
|
+
thinking = Markdown("🤖 **OrrinCLI** is thinking...")
|
|
101
|
+
container.mount(thinking)
|
|
102
|
+
container.scroll_end(animate=False)
|
|
103
|
+
|
|
104
|
+
# 3. Get real AI response
|
|
105
|
+
ai_response = self.prompt_ai()
|
|
106
|
+
|
|
107
|
+
# 4. Remove thinking message and add real response
|
|
108
|
+
thinking.remove()
|
|
109
|
+
ai_md = Markdown(ai_response)
|
|
110
|
+
container.mount(ai_md)
|
|
111
|
+
|
|
112
|
+
# Add to history
|
|
113
|
+
self.chat_history.append({'role': 'assistant', 'content': ai_response})
|
|
114
|
+
|
|
115
|
+
container.scroll_end(animate=False)
|
|
116
|
+
|
|
117
|
+
def prompt_ai(self) -> str:
|
|
118
|
+
try:
|
|
119
|
+
resp = requests.post(
|
|
120
|
+
'http://172.20.10.2:8080/api/prompt_grok',
|
|
121
|
+
headers={'authorization': 'a7759iujj590poeu52199093kmeyahwpqudjw'},
|
|
122
|
+
json={'history': self.chat_history},
|
|
123
|
+
timeout=90
|
|
124
|
+
)
|
|
125
|
+
if resp.ok:
|
|
126
|
+
return resp.json().get('response', 'No response received from AI.')
|
|
127
|
+
else:
|
|
128
|
+
return f"**Error:** HTTP {resp.status_code} — {resp.text[:200]}"
|
|
129
|
+
except Exception as e:
|
|
130
|
+
return f"**Connection error:** {str(e)}"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# For quick testing
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
app = AIChatApp(
|
|
136
|
+
file_path="example.py",
|
|
137
|
+
file_content="print('Hello world')"
|
|
138
|
+
)
|
|
139
|
+
app.run()
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
from click import Choice
|
|
2
|
+
import typer
|
|
3
|
+
import requests
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from platformdirs import user_config_dir
|
|
8
|
+
import yaml, tomli, toml
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from chat_tui import OrrinCLI
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="""
|
|
13
|
+
Welcome to Orrin CLI v0.1.8!\n\n
|
|
14
|
+
|
|
15
|
+
Orrin CLI enables you to perform actions within your terminal that would otherwise require
|
|
16
|
+
an entire dashboard. Though there is a dashboard, Orrin CLI enables a feasible development
|
|
17
|
+
experience for all developers, as they can swiftly run a command to upload their frontend,
|
|
18
|
+
perform checks on their app review status, and more!
|
|
19
|
+
""", no_args_is_help=True)
|
|
20
|
+
|
|
21
|
+
config = typer.Typer(help="Configure your Orrin Apps developer API with Orrin CLI")
|
|
22
|
+
app.add_typer(config, name='config')
|
|
23
|
+
|
|
24
|
+
ui = typer.Typer(help="UI related commands")
|
|
25
|
+
app.add_typer(ui, name="ui")
|
|
26
|
+
|
|
27
|
+
projects = typer.Typer(help='OrrinSDK project commands')
|
|
28
|
+
app.add_typer(projects, name='projects')
|
|
29
|
+
|
|
30
|
+
API_BASE = "https://stellr-company.com"#"http://192.168.1.153:8080"
|
|
31
|
+
|
|
32
|
+
APP_NAME = "orrin"
|
|
33
|
+
APP_AUTHOR = "orrin" # optional but recommended on Windows
|
|
34
|
+
|
|
35
|
+
config_dir = Path(user_config_dir(APP_NAME, APP_AUTHOR))
|
|
36
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
config_file = config_dir / "config.json"
|
|
39
|
+
|
|
40
|
+
# ---- Configuration based commands/helper functions ----
|
|
41
|
+
|
|
42
|
+
def save_config_data(api_key: str, email: str):
|
|
43
|
+
data = {"api_key": api_key, 'email': email}
|
|
44
|
+
with open(config_file, "w") as f:
|
|
45
|
+
json.dump(data, f)
|
|
46
|
+
|
|
47
|
+
def load_config():
|
|
48
|
+
if not config_file.exists():
|
|
49
|
+
return None
|
|
50
|
+
with open(config_file) as f:
|
|
51
|
+
return json.load(f)#json.load(f).get("api_key")
|
|
52
|
+
|
|
53
|
+
@config.command(help="""
|
|
54
|
+
Add your Developer API key and email to the Orrin CLI to perform tasks explicitly relating to your developer account.
|
|
55
|
+
Your email will be added to your developer account details, and correspondence regarding your submission
|
|
56
|
+
will take place via the dashboard and email, if the email is valid.
|
|
57
|
+
""")
|
|
58
|
+
def configure_dev():
|
|
59
|
+
dev_email = typer.prompt(
|
|
60
|
+
'Enter your email',
|
|
61
|
+
hide_input=False,
|
|
62
|
+
confirmation_prompt=True
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
dev_api = typer.prompt(
|
|
66
|
+
"Enter your Developer API key",
|
|
67
|
+
hide_input=True,
|
|
68
|
+
confirmation_prompt=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
resp = requests.post(
|
|
72
|
+
f'{API_BASE}/api/orrin_apps/developer_api_exists',
|
|
73
|
+
json={'developer_api_key': dev_api}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if not resp.ok:
|
|
77
|
+
typer.echo(f'❌ No developer account was found with API key:\n\n{dev_api}')
|
|
78
|
+
raise typer.Exit(1)
|
|
79
|
+
|
|
80
|
+
# Add email to developer account
|
|
81
|
+
resp = requests.post(
|
|
82
|
+
f'{API_BASE}/api/orrin_apps/add_email_to_developer_account',
|
|
83
|
+
json={'email': dev_email, 'developer_id': dev_api}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if not resp.ok:
|
|
87
|
+
typer.echo(f'❌ Something went wrong adding email to developer account. Try again.\n\nIf the problem persists, contact us:\n\thttps://stellr-company.com/contact')
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
|
|
90
|
+
save_config_data(api_key=dev_api, email=dev_email)
|
|
91
|
+
typer.echo("✅ Developer API and email configured with Orrin CLI")
|
|
92
|
+
|
|
93
|
+
@config.command(help='Revokes the Developer API currently configured with Orrin CLI')
|
|
94
|
+
def revoke_config():
|
|
95
|
+
if not config_file.exists():
|
|
96
|
+
typer.echo("ℹ️ No configuration found to revoke.")
|
|
97
|
+
raise typer.Exit(code=0)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
config_file.unlink()
|
|
101
|
+
typer.echo("✅ Credentials revoked. Local config removed.")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
typer.echo(f"❌ Failed to revoke credentials: {e}")
|
|
104
|
+
raise typer.Exit(code=1)
|
|
105
|
+
|
|
106
|
+
@config.command(help='Show configured Developer API key')
|
|
107
|
+
def show_configuration():
|
|
108
|
+
if not config_file.exists():
|
|
109
|
+
typer.echo("ℹ️ No Developer API key configured with Orrin CLI")
|
|
110
|
+
raise typer.Exit(code=0)
|
|
111
|
+
|
|
112
|
+
config = load_config()
|
|
113
|
+
|
|
114
|
+
typer.echo(f'\n\tConfigured Developer API key:\n\t\t{config["api_key"]}\n\n\tConfigured Developer Email:\n\t\t{config["email"]}\n')
|
|
115
|
+
|
|
116
|
+
# -------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
# ---- UI based commands/helper functions ----
|
|
119
|
+
|
|
120
|
+
@ui.command(help='''Build static output for next.js code, and generate a zip file to be uploaded.\n\nEnsure you have the following in next.config.tsx:\n\n
|
|
121
|
+
------------------------------------------------------\n\n
|
|
122
|
+
/** @type {import('next').NextConfig} */\n\n
|
|
123
|
+
const nextConfig = {\n\n
|
|
124
|
+
output: 'export',\n\n
|
|
125
|
+
trailingSlash: true,\n\n
|
|
126
|
+
basePath: '/<your_app_name>/current',\n\n
|
|
127
|
+
reactStrictMode: true,\n\n
|
|
128
|
+
};\n\n
|
|
129
|
+
\n\n
|
|
130
|
+
module.exports = nextConfig;
|
|
131
|
+
------------------------------------------------------\n\n
|
|
132
|
+
\n\n
|
|
133
|
+
Where <your_app_name> is the name of your app, which was configured in your backend.
|
|
134
|
+
''')
|
|
135
|
+
def generate_zip():
|
|
136
|
+
os.system('npm install && npm run build')
|
|
137
|
+
os.system('cd out && zip -r ../ui-build.zip .')
|
|
138
|
+
|
|
139
|
+
@ui.command(help="Upload your frontend code to be reviewed")
|
|
140
|
+
def upload(
|
|
141
|
+
#zip_path: Path = typer.Argument(..., exists=True),
|
|
142
|
+
app_name: str = typer.Option(..., "--app")
|
|
143
|
+
):
|
|
144
|
+
"""
|
|
145
|
+
Upload a UI zip to Orrin.
|
|
146
|
+
"""
|
|
147
|
+
if not os.path.isfile(os.path.join(os.getcwd(), 'ui-build.zip')):
|
|
148
|
+
typer.echo(f"❌ Path {os.path.join(os.getcwd(), 'ui-build.zip')} does not exist.\n\nEnsure you run `orrin ui generate-zip`, or build the zip yourself.")
|
|
149
|
+
raise typer.Exit(1)
|
|
150
|
+
|
|
151
|
+
dev_api = load_config()['api_key']
|
|
152
|
+
|
|
153
|
+
if dev_api is None:
|
|
154
|
+
typer.echo("❌ You have not configured an API key. Please register with `orrin configure configure-dev-api`.")
|
|
155
|
+
raise typer.Exit(1)
|
|
156
|
+
|
|
157
|
+
with open(os.path.join(os.getcwd(), 'ui-build.zip'), "rb") as f:
|
|
158
|
+
response = requests.post(
|
|
159
|
+
f"{API_BASE}/ui/upload",
|
|
160
|
+
files={"file": f},
|
|
161
|
+
data={
|
|
162
|
+
"developer_id": dev_api,
|
|
163
|
+
"app_name": app_name
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if response.status_code != 200:
|
|
168
|
+
try:
|
|
169
|
+
data = response.json()
|
|
170
|
+
typer.echo(f'❌ Upload failed: {response.status_code}\n\nMessage: {data["Message"]}')
|
|
171
|
+
except:
|
|
172
|
+
typer.echo("❌ Upload failed")
|
|
173
|
+
raise typer.Exit(1)
|
|
174
|
+
|
|
175
|
+
data = response.json()
|
|
176
|
+
typer.echo("✅ Uploaded")
|
|
177
|
+
typer.echo(f"Upload ID: {data.get('upload_id')}")
|
|
178
|
+
|
|
179
|
+
# --------------------------------------------
|
|
180
|
+
|
|
181
|
+
# ---- For SDK based projects ----
|
|
182
|
+
|
|
183
|
+
@projects.command(help='Create a project for an app backend with OrrinAppsSDK')
|
|
184
|
+
def init_app_backend_project():
|
|
185
|
+
project_name = typer.prompt(
|
|
186
|
+
"Project Name",
|
|
187
|
+
hide_input=False,
|
|
188
|
+
confirmation_prompt=True,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
project_desc = typer.prompt(
|
|
192
|
+
'Project Description',
|
|
193
|
+
hide_input=False
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
project_toml = {
|
|
197
|
+
'general': {
|
|
198
|
+
'project_type': 'app',
|
|
199
|
+
'name': project_name,
|
|
200
|
+
'desc': project_desc
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
path = os.path.join(os.getcwd(), project_name)
|
|
205
|
+
os.mkdir(path)
|
|
206
|
+
|
|
207
|
+
config = load_config()
|
|
208
|
+
|
|
209
|
+
source_code = f'''
|
|
210
|
+
from orrinsdk import OrrinAppsSDK
|
|
211
|
+
|
|
212
|
+
orrin_sdk = OrrinAppsSDK(
|
|
213
|
+
developer_api='{config["api_key"]}'
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
@orrin_sdk.action(
|
|
217
|
+
'ExampleBackendAction',
|
|
218
|
+
required_payload=[{{'name': 'a', 'type': 'any'}}]
|
|
219
|
+
)
|
|
220
|
+
def ExampleBackendAction(a):
|
|
221
|
+
# Do something here
|
|
222
|
+
return {{ 'status': 200, ... }}
|
|
223
|
+
|
|
224
|
+
orrin_sdk.finalize()
|
|
225
|
+
'''
|
|
226
|
+
|
|
227
|
+
with open(os.path.join(path, 'project.toml'), 'w') as file:
|
|
228
|
+
file.write(toml.dumps(project_toml))
|
|
229
|
+
|
|
230
|
+
with open(os.path.join(path, 'main.py'), 'w') as file:
|
|
231
|
+
file.write(source_code)
|
|
232
|
+
|
|
233
|
+
@projects.command(help='init-app-backend-project')
|
|
234
|
+
def iabp():
|
|
235
|
+
init_app_backend_project()
|
|
236
|
+
|
|
237
|
+
@projects.command(help='Create a project for creating a tool with OrrinToolsSDK')
|
|
238
|
+
def init_tool_project():
|
|
239
|
+
project_name = typer.prompt(
|
|
240
|
+
"Project Name",
|
|
241
|
+
hide_input=False,
|
|
242
|
+
confirmation_prompt=True,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
typer.echo(f'\n\t[orrin-cli] Version for {project_name} will default to 1.0.0\n')
|
|
246
|
+
|
|
247
|
+
project_desc = typer.prompt(
|
|
248
|
+
'Project Description',
|
|
249
|
+
hide_input=False
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
has_plugins = typer.confirm(
|
|
253
|
+
'Support Plugins?'
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
project_toml = {
|
|
257
|
+
'general': {
|
|
258
|
+
'project_type': 'tool',
|
|
259
|
+
'name': project_name,
|
|
260
|
+
'desc': project_desc,
|
|
261
|
+
'nuances': {}
|
|
262
|
+
},
|
|
263
|
+
'icons': {
|
|
264
|
+
'24x24': '<path>',
|
|
265
|
+
'32x32': '<path>',
|
|
266
|
+
'48x48': '<path>',
|
|
267
|
+
'64x64': '<path>'
|
|
268
|
+
},
|
|
269
|
+
'plugins': {
|
|
270
|
+
'supported': False
|
|
271
|
+
},
|
|
272
|
+
'metadata': {}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# Create project
|
|
276
|
+
path = os.path.join(os.getcwd(), project_name)
|
|
277
|
+
os.mkdir(path)
|
|
278
|
+
|
|
279
|
+
config = load_config()
|
|
280
|
+
|
|
281
|
+
plugin_entries = []
|
|
282
|
+
|
|
283
|
+
if has_plugins:
|
|
284
|
+
project_toml['plugins']['supported'] = True
|
|
285
|
+
project_toml['plugins']['config'] = 'plugins.yaml'
|
|
286
|
+
|
|
287
|
+
number_of_plugins = int(typer.prompt(
|
|
288
|
+
'How Many Plugins?',
|
|
289
|
+
hide_input=False
|
|
290
|
+
))
|
|
291
|
+
|
|
292
|
+
plugins = []
|
|
293
|
+
|
|
294
|
+
for i in range(number_of_plugins):
|
|
295
|
+
plugin_for = typer.prompt(
|
|
296
|
+
f'Plugin {i} For',
|
|
297
|
+
hide_input=False
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
plugin_type = typer.prompt(
|
|
301
|
+
f'Plugin {i} Type',
|
|
302
|
+
hide_input=False,
|
|
303
|
+
type=Choice(["internal", "external"], case_sensitive=False)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
plugin_name = typer.prompt(
|
|
307
|
+
f'Plugin {i} Name',
|
|
308
|
+
hide_input=False
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
plugin_display_name = typer.prompt(
|
|
312
|
+
f'Plugin {i} Display Name',
|
|
313
|
+
hide_input=False
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
plugins.append({
|
|
317
|
+
'for': plugin_for,
|
|
318
|
+
'type': plugin_type,
|
|
319
|
+
'name': plugin_name,
|
|
320
|
+
'display_name': plugin_display_name,
|
|
321
|
+
'actions': [
|
|
322
|
+
{
|
|
323
|
+
'action': '',
|
|
324
|
+
'helper': '',
|
|
325
|
+
'type': ''
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
if plugin_for == 'blink':
|
|
331
|
+
plugin_entries.append('''
|
|
332
|
+
@orrin_sdk.plugin_entry(
|
|
333
|
+
plugin='blink'
|
|
334
|
+
)
|
|
335
|
+
def blink_plugin_entry(cc: any, all_context: any):
|
|
336
|
+
if not isinstance(cc, dict):
|
|
337
|
+
return {'status': 400, 'message': 'invalid_type'}
|
|
338
|
+
|
|
339
|
+
if not isinstance(all_context, str):
|
|
340
|
+
return {'status': 400, 'message': 'invalid_type'}
|
|
341
|
+
|
|
342
|
+
# Handoff to Exegesis or process contextual data yourself
|
|
343
|
+
pass
|
|
344
|
+
''')
|
|
345
|
+
else:
|
|
346
|
+
plugin_entries.append(f'''
|
|
347
|
+
@orrin_sdk.plugin_entry(
|
|
348
|
+
plugin='{plugin_for}'
|
|
349
|
+
)
|
|
350
|
+
def custom_plugin_entry(cc: any, all_context: any):
|
|
351
|
+
if not isinstance(cc, dict):
|
|
352
|
+
return {{'status': 400, 'message': 'invalid_type'}}
|
|
353
|
+
|
|
354
|
+
if not isinstance(all_context, str):
|
|
355
|
+
return {{'status': 400, 'message': 'invalid_type'}}
|
|
356
|
+
|
|
357
|
+
# Handoff to Exegesis or process contextual data yourself
|
|
358
|
+
pass
|
|
359
|
+
''')
|
|
360
|
+
|
|
361
|
+
with open(os.path.join(path, 'plugins.yaml'), 'w') as file:
|
|
362
|
+
yaml.safe_dump({'plugins': plugins}, file)
|
|
363
|
+
file.close()
|
|
364
|
+
|
|
365
|
+
plugin_entries.append('') # filler to trigger another newline
|
|
366
|
+
|
|
367
|
+
source_code = f'''
|
|
368
|
+
from orrinsdk import OrrinToolsSDK
|
|
369
|
+
|
|
370
|
+
orrin_sdk = OrrinToolsSDK(
|
|
371
|
+
developer_api_key='{config["api_key"]}'
|
|
372
|
+
)''' + "\n".join(plugin_entries) + '''@orrin_sdk.action(
|
|
373
|
+
name='ExampleAction',
|
|
374
|
+
payload_schema='',
|
|
375
|
+
desc=''
|
|
376
|
+
)
|
|
377
|
+
def ExampleAction(data: any):
|
|
378
|
+
if not isinstance(data, dict):
|
|
379
|
+
return { 'status': 400, 'message': 'invalid_data' }
|
|
380
|
+
|
|
381
|
+
# Process data. Perform some sort of action
|
|
382
|
+
|
|
383
|
+
return { 'status': 200 }
|
|
384
|
+
|
|
385
|
+
orrin_sdk.finalize()
|
|
386
|
+
'''
|
|
387
|
+
|
|
388
|
+
with open(os.path.join(path, 'project.toml'), 'w') as file:
|
|
389
|
+
file.write(toml.dumps(project_toml))
|
|
390
|
+
|
|
391
|
+
with open(os.path.join(path, 'main.py'), 'w') as file:
|
|
392
|
+
file.write(source_code)
|
|
393
|
+
|
|
394
|
+
"""@projects.command(help='Get help writing code for a project that uses OrrinSDK')
|
|
395
|
+
def connect_to_project():
|
|
396
|
+
root = Path.cwd()
|
|
397
|
+
|
|
398
|
+
# 1. Find all Python files
|
|
399
|
+
python_files = list(root.rglob("*.py"))
|
|
400
|
+
|
|
401
|
+
# 2. Handle empty case
|
|
402
|
+
if not python_files:
|
|
403
|
+
typer.echo("❌ No Python files exist in this project.")
|
|
404
|
+
raise typer.Exit()
|
|
405
|
+
|
|
406
|
+
# 3. Display files
|
|
407
|
+
typer.echo("\n📂 Python files found:\n")
|
|
408
|
+
for idx, file in enumerate(python_files, start=1):
|
|
409
|
+
typer.echo(f"{idx}. {file.relative_to(root)}")
|
|
410
|
+
|
|
411
|
+
# 4. Prompt selection
|
|
412
|
+
while True:
|
|
413
|
+
choice = typer.prompt("\nSelect a file by number")
|
|
414
|
+
|
|
415
|
+
if not choice.isdigit():
|
|
416
|
+
typer.echo("❌ Please enter a valid number.")
|
|
417
|
+
continue
|
|
418
|
+
|
|
419
|
+
choice = int(choice)
|
|
420
|
+
|
|
421
|
+
if 1 <= choice <= len(python_files):
|
|
422
|
+
selected_file = python_files[choice - 1]
|
|
423
|
+
break
|
|
424
|
+
|
|
425
|
+
typer.echo("❌ Number out of range.")
|
|
426
|
+
|
|
427
|
+
# 5. "Connect" result
|
|
428
|
+
typer.echo(f"\n✅ Connected to: {selected_file}")
|
|
429
|
+
|
|
430
|
+
content = selected_file.read_text()
|
|
431
|
+
|
|
432
|
+
# Launch TUI
|
|
433
|
+
c = OrrinCLI(file_path=str(selected_file), file_content=content)
|
|
434
|
+
c.run()"""
|
|
435
|
+
|
|
436
|
+
@projects.command(help='init-tool-project')
|
|
437
|
+
def itp():
|
|
438
|
+
init_tool_project()
|
|
439
|
+
|
|
440
|
+
"""@projects.command(help='Get help creating a tool or app backend')
|
|
441
|
+
def orrin_cli():
|
|
442
|
+
from minimal_display import OrrinCLI
|
|
443
|
+
OrrinCLI().start()"""
|
|
444
|
+
|
|
445
|
+
# --------------------------------
|
|
446
|
+
|
|
447
|
+
def main():
|
|
448
|
+
app()
|
|
449
|
+
|
|
450
|
+
if __name__ == "__main__":
|
|
451
|
+
main()
|