mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-01-30 13:54:18 -05:00
Compare commits
8 Commits
3aeeadae76
...
30d3a2dc44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30d3a2dc44 | ||
|
|
65b5d6e1dc | ||
|
|
adfa5bd1bd | ||
|
|
ef6d21f857 | ||
|
|
b6bf96e002 | ||
|
|
5fb6374f8a | ||
|
|
16a805ea2c | ||
|
|
1bcf5a0c06 |
1
Manus Agent Tools & Prompt/manus-sandbox/README.md
Normal file
1
Manus Agent Tools & Prompt/manus-sandbox/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# manus-sandbox
|
||||||
2
Manus Agent Tools & Prompt/manus-sandbox/env
Normal file
2
Manus Agent Tools & Prompt/manus-sandbox/env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export RUNTIME_API_HOST=https://api.manus.im
|
||||||
|
export TZ=America/New_York
|
||||||
1176
Manus Agent Tools & Prompt/manus-sandbox/localhost8330openapi.json
Normal file
1176
Manus Agent Tools & Prompt/manus-sandbox/localhost8330openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
fastapi = ">=0.68.0"
|
||||||
|
uvicorn = ">=0.15.0"
|
||||||
|
websockets = ">=10.0"
|
||||||
|
bashlex = "*"
|
||||||
|
pexpect = "*"
|
||||||
|
pydantic = "*"
|
||||||
|
httpx = "*"
|
||||||
|
browser-use = "0.1.36"
|
||||||
|
# nuitka = "2.6.7"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
pytest-asyncio = ">=0.18.0"
|
||||||
|
isort = ">=5.10.1"
|
||||||
|
mypy = ">=0.981"
|
||||||
|
flake8 = ">=4.0.1"
|
||||||
|
pytest = "*"
|
||||||
|
ruff = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.11"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
start = "python start_server.py"
|
||||||
|
# start_bin = "python start_server.bin"
|
||||||
|
test = "pytest"
|
||||||
|
format = "black ."
|
||||||
|
lint = "flake8 ."
|
||||||
|
sort = "isort ."
|
||||||
|
typecheck = "mypy ."
|
||||||
|
init_playwright="playwright install chrome"
|
||||||
2757
Manus Agent Tools & Prompt/manus-sandbox/opt/.manus/.sandbox-runtime/Pipfile.lock
generated
Normal file
2757
Manus Agent Tools & Prompt/manus-sandbox/opt/.manus/.sandbox-runtime/Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.600285
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x8b\x00\x00\x00\x12\t\x05\x005Kn\x1c-L4Q\xa9\xa2\xc0j\x1e\xc8\xa8S\x00\x00\x00\x00\x00\x00\x00\x00\xc6v\x1cb\xac\x04\xfex\xaf\xf2\x86\xef\x8de\xe8Q>%F6S<9\xc9 \xf6M3\x88\xec9+\xd2W\xc8\xaf\x8c\x0fz4\x9e~\x89\x06\xb9\xdeI\x1f8\xda\xecWi\xfa|\xd6e\x89\xd4\xc3\xb8e"&Z\x88M\xac:\xf8)l\x93\xe2p\xb27\x8b\tlc\x18\x8cCITI\x83\xc6\xe2\x9b\xdc\x9aI\xc1\xfc#\xf5\x1a\xf6\t\x06\x9d\ta\xd6\x9e\xb2\x11\x04;\xb0\x15H\xe8\xe0\xf9\xccM<\xef\x00\x86\x1b\x8a\xdc\x9bE\xb8\x08:\xedIp\xfe\x97\xf6c\xa5')
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.777572
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x93\x00\x00\x00\x12\t\x05\x00\xdd\xbfo\xd9\x10\x04\x01\x84L>\t?\xa7e\xfa\xe6\x00\x00\x00\x00\x00\x00\x00\x00^\xb1e\x9cs4\ts\x8c\x08"\xe7\xbe\x93\xea*qM\xae\x88z~\xe3R\n\xff\xf4NW\x0b\xf7\xd4O\xe0d\xabM3Y\x0b\xc4_\xf1\'\xbf \x98\xbd/P\x90\xab@/\xb7kR-\xf7\xd4S\xcb)\t\xab\xf2\x1c\xfa\xb4Zr\xe1\xca\xe6ud.\x8b\xb0^\x87\xda\x95\x89X\x1f\x1f\x12\xf6q\x16t:\x98\xe7\x1f4<\xa2\xa2\x00U\xff\xefS\x7fz/\xd1\xc3\x9aPN\xf8\x027\x8e\xd0\x0c\xc3\xb5\x06\xda\xbf\x9fe\x85T\xa8\xc4\xfe1T\xe7\xf09\xa4\xe4XZ\xc4O\x0e~\xc2\x93\x7f')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.604918
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00F\x03\x00\x00\x12\t\x05\x00\x1a\x9a\x079\x1a\xa5\xd4\xe4?\xfbM\xd3\xcc\xe2\xed3\x00\x00\x00\x00\x00\x00\x00\x00)\x0c\x1d\xb0\xe14Bt\x0f\xbe\x10[{x\xf1\x9b\x1d\xab5\xceb\x81\x91.\xfe\xb1{\xd3\xe0\xe0\xa6\xdf\xd2}-`\x8a%\x93\xedsg\x1ao\xd2\xc0+\xd8\x06P\xe2\xe5\xbf\xf4\xc4\xd1\x01.\x11\xcc\xfd\x9c\xa5\xb8H\x85Y\xa0\'\xc5\x9c9($\x90\xfb\xa0\xf8\xce\xe6\x8d\x14\x14\xe1\xb6\xd3);\x97\x0e\xff\x89\xc7N\xb6d\x92\x955\\\xde\xcf\xa8\xb6\xd5\x15)\x00\xa9s\x96K\x95\x1d\x19\x1b\x83\xab\xe4\xcc\xa9H\xa6\xba\rD#\x19:OifT\xe9l\x8dK\x9d\xc5U\xbfl\x06]JT\xd3\x93\xa8\xa5\xd9\xa8\xc7\xed\x0f\xd7O\x1cvfVtX\xa2g\x07\xa5l\xeeAS\xb7\xab\tH\xb6\xaa$\x017e\x0fU\x93\x83\x93x\x19\xb1O(\x1b\x81H\xa9\x06N\xd7\xe5_\x980\xb9\nEj \'^\xd9b\x85\xd3Y\x83\xc1\x88\xe08\xe6\xad\xca\x8d\x96\xfb\xdd\xe9\x8f\x94\x18\x871\xd1\xee:7\xd7\x98\n\xba\xc0\xa4\xfb\x01Rp\xb2H\x92\x9e\x83r\xb4\xbdA8Sd\xfb\x00\xe7\x94bdS\xa7j\xed&\xe0bX\xc2)\xcd\xbf\xd4\xf9\x86Y\xe7\x91\x9b\xff\x01YU\x1d\xf1p\x0b\x1e\x0f\xec\x11\xe87\xcdq\x89\xaf\x91\xe2\xf2$\xd0\xee\x01\xa7\xdf\x8fH\x07\xa9V\xe3\xa5\x9eN<\'\xee\x97d\x85\x93\x7f\xef\x870B\xa8\x80\xeb\xa4\x92^\xaf\xd3W\xba\xb5\xe8#\x85{\xa1\xd7\xcb\xe4\x88g\xad\xf0nS+V\xf3\x8c\xd4\xc1wsJ\x01\xa6\x87\xcebo\x19b$k3\xae\xf8\x86\x86\xc6\xa9\x12\xe8\x0f\x00\x03\xd0A\x17\x10\x0b\xf2""+KnS\x88\xf7\x9c\x91,\xd0\x02S"\xe9c\xa9\xbd\xdd\xa1C\x97\x97@\xdb\x19\te\x91\x90\xffPR\x19\xb8\x02 \xb2\xd2\xe3P\xf4\x05\x83!tl\x94\x95\xbb\xdd\xdb\x10\xaa\x86r\xcc\xe73\xc1fvCR|\t3/\x1a\xf4|\x84\x8fr\x13\xd8\xb6\x80\xe4\x90uF\xd80H,\x08\x8f\x87\x80\xf3\x88\xcd,\xe7t\xdf\x1eB\xa9\xf0\xee\xfdI\xd4\xc5](-bs\xac\xaa\xa4\x05\x97\xe5\xdfU\xcfd\x07d\xa5m\xf6&\xcf\x90\xebr\x1f-\xcc[\xf3\xffHw,\x15O\x1erbH;8*\xe8\xd3\xdc\x94*\xc2:\x1c\x1b8\x0c4:B\xeaS\xafylYWj\x85\xd6\xa0]\xb9\xef?\xee\xad\xa1\xec6<\x90\xe9:\xd4\x1e\xd9\x13X\x01\x05}\x86\x04\xa7 4L/\xb4\xee\x89\x84L\xf7a\x06\xff\xcf\x1c\nP\x9a\x94Tx\x14v\t\t\xd8S#\x039;\x90*G%OG6,\xa3\x9a\x15\x02\xc4\xfe\xc0"\xb3\x1f\xfd\x97\xc8(\xbc\xc5?\x12\xb2+\x86\xe1\xe0dF\x04\xfb\x04\xc1\xf9\xc4d\xb0\x1b\xee\xdb\xf9\x07u+\x16 \x166k\x81\x98\xae\xec\xc4\xb0\xbe\xeb\t\xfap\xe2^\xd2\x8e*\x9do\xbc_\x9c\xf6\xda\xe1\xe5\xdd\x9c\xe5\x04\x9f\x0b\xc4=i\x14d\x97\xec}=\x14\xb6\xaa\xaaEpR\x10\xde\x90#\x15\x0f\xa9\x1a\x0fnBK\x1e\xf2\xfbLg\x05n\xfa\xbf\x92\xed\x8f\xa7"\x86\xbc\xb4\xbd\xf1\xd7\x0fW!Y{\xdd\xcd\xb82\x99\x90\xee\xb3W\x91\xc9PD,\xcf\xa7S\xaa\xca\xcf\x1d\xb2\x1ew\xb9\x94\xf9~;\x16B\xa2\xea\xa3\xa7\xfe\xe1|7\x00\xd4\x147\xd8\xe9&"G$\\\xea\\\x0f\x00\xdc\xdf\x8c\x81p\xba\xd5\xbd\xaa*\t\xae\xab(b\xbfh?\xd0\xf3*\xf2\xa2\xd7')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.821980
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x91\x00\x00\x00\x12\t\x05\x008\xec\xb2\xd9U\x03;5*\xcf\xb3\x03\xe1\xd6\xf4\xdd\x00\x00\x00\x00\x00\x00\x00\x00\xc35T?\xbb\xe9\'\xc9\xf0\x1c\x89if\xd8[\x88pY\xdc\xcb\x03\xbca\xbdH&\xd7\x96\xae\x9c*\x02\xc4ki+B4\xa4a\x8d\xc7\xc1\x84\x10\xd2\x84B\x1b\x83\x8e\x18\xdb+\x10\t\x0fa)\xe9\xc1\xea\x0f\x85\xfe\xa5\x9f\xdc4cR%\xe8L\xd2\x05"\xa8\x7f\xab\xb2\xc7\xca\x04\x1e\xdd\xd0uu\n<<\x9f)`\xd4L)\xf1\xbd\xe4\x11\x0b\x81\xc0\xe4\xe3\xd9\x9a\xe6\xf2#\xf5nm~N\xfd\xabC\x98?\x0f\xa3\x98\xabEK\xb7M\xec\xf2\xca-\xec\xaf\x1dD\x8e\x10\xdd\xd26\xe5!')
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:51.107330
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00&\x02\x00\x00\x12\t\x05\x00\x11\xfa(\x9aNV\xb67L\x87\xcd\xc4a\xe1\x93w\x00\x00\x00\x00\x00\x00\x00\x00\xb2@\xc9\xcc\xda<\x8f\xcd\xd16\x8c\x82\x8bx"\xa0\xe9\xb8\xbdap\xdaX2N\xad\x17(Y\xbe\xeeL\xecH\xa7K\x81\xd7\xc4\xd2\xe1\x15\xfbg[$n\xb7\xcc\xc2\xfe=\xca\x92\x1b\xd1B%^\xa4mzw\x0e\xfc\x1d\x19\xcb\xa1U\xe4c\x05\xbc\xbe\xd7\x8c\x01\xc9\x01\xea\x7f|s7\xe3\xdc\x08\xb0e\x01\xc0\xdc\xea\xb2\x1a\xe0I{\xf4\x16\xbb*1\x18G2(\xeeg\xddK\x90\x1e\x01\xc8S\x8f\xab\x85|\xe5\x81\xc8*U\xbd\x88D\xab/q\x12\x97\xf0\xa2%1\xd1\x9b\x03x+\x03\x8e2h\x8f3^}\xef\x8a\xbeG\x00\x13\xd1\x92\xa24\x9d\x97\xf4&O\x063\xf7\xcc\xb8\x00\x1b\xdcIi\xd7L*~\xb1\x1e\x15w?\xc3Jk\x0b[13\x0f\xca_\\XY\xf9V\xa8t<\xd8\x03P:uIw\xfc\'\xb7\x00j\x14\xc8\x1aX8\xab6\x97\xe9\x0e\xf3=\x15XN#\x8c\xb3\xca\xba\xcba\x97@sj\xf1fW\x15V\xc0ap\x01M\x0fH\x8b\x96oC\x11\x8c\x81er\x84F\xe8\x1a6\x15\x13\xe0\x15\xf3D9\x83q\x12M\x8a\xd7\x15RO\x9e5\x7f\xdf\xdd\xef.\xf9y\x97\xf8\x13=&\xe3$\xb9\x07\xc4\x07m\\&\xb5zpO\x1b.\xe4\x1e\xb0\xeb\xb4\xd5n\x1f\xb4\xbai\x8f\xcf\xc6\xb0Z\xf6\xf1\x1a\xbb\xdd\xeb\xca\xd4\x83I\xf3\xbe\xa9\x82\x93\xc7\xd2\x9a~\x01\xc5\x9a<[\x9b$\x103~d\\\xa2U\xbe\xfd\xfb\xd8\xedC\xf0\xa2-j\x98r\x9e7\x94\xccK\x98\x1c\x7f\x9c\xe9h\x88x\xf1)\x85\xf9\xb2\xa2\xa4\x07j\x19\xe0+tg\x19\x1d(Ysf\xfaHT_l\xf5]\x889+Of\xcfN\xbc\x9c\x1f\x1f]\x06\x9b[h\x9e\x99\xee?\xba\x17\xa1)\x06s\xf3(\\\x1d\x15\x98\xa3\xf3\xd0\x85dm\x14\xda\x18e\xb0_cw#\xf4\xdf\xc0\xf5\xff\xf0\xfe\x95\xa7\xac~\x99\xb5*,\xa3\xb5ht\x14\x90m\r\xa9\xfc\x13\xf0\xbb\xc2]\xe0\xa6k\'\xda\xae&\xd7\x17\xf7\x9a\xb5\xc7\x85\x02+\xd3\x0e\x1a\xba\x9d\xbaN\x1d\xb4#\x15D\xb1\xc7\xc5}\xdf\xd1\xc4\xf1S\xae.\x10\xe5\xb0[\xbf\x1e\x88=\x98\xba\x8e\x80\x0c\xe1\xae\xd7\x82\xe0\xfb\xc5.')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.946773
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00;\x02\x00\x00\x12\t\x05\x00\xa2\x82G)\xb1\x91(:*\xfeeh1P\x1a\xc6\x00\x00\x00\x00\x00\x00\x00\x00a\\\x85\xb6y]"f~\x0cF[\x8c\xd6\x1a9V\x93\x84`S\x80\xc3\x0e\x02\xc9C*\xbbfx\x7f\xdc\xa423 \x12\x0c\xf0e\xfc\x80\xa2\x82\xf89uR{\xe5S\xa3\x1f6\xd3\x84E\x89\xe3$\xd6$\x15\xfdg\x9bf\xbe\xd8\x99\xdd(\xdc\x8a\xb9\x18\x1en\x9dO\xea\xe0\t%=\xf9\x1aRd6/\x930\x12+\xcd\x7fl\xc4\n+\x08|\xffg\xb5K\x16Q\x84\x8b\xc9\t\r\xba\x87\x9c\xc7c`\xdf\x1d\x87\x8f|\x8ert \xf81\xc5|\x90a\xe2^\x0b\xaf`\xd4\x89\tn\xad\xa4e\xbe\\\x9d\x90\x91\'8\x1f\xeb \xba\xb9\xe2;\x1dH\xa9\x16\xc3\xb8S^+\x00t\xa3\x81fOv\xd7\xa9\xd8/{U\xde\x97\xd0j\x81\x1cLF\xaeHY\xa3\xdc 2C\xb9\x90\x0b-\x99\x04\xe3\x8b\xda\xc5xIr\xdbr\xbc\x06\x84Y\xa9!\x8a\xb99\xdb\xf4!\x03\xbe\xf6\xc0u\xcbO,\xe1\xcb\xe2\xfe}\xf4\xb7\xef)\xd8\xc0\xdc\x8bh\x88\xef\xa0}\xa7\xb4\xc4B\xcbJ\x03:\xec\x19oe\x85:;Z\x84wZ&\xc0\xa8\xd3^\xdc\xad\x8e\x1c\xa9\xceITE\xef\x1c%\xb4\xfc\x08F\xd4\xa3w)\x85\xc1I\x0f\xfb\xbd\x1eF\xe6S\x99\xd4)\xa3B\xdf\xdd\x18gR\xad*\x927IR8\x19\xb7\xefR\xfe3V]#P\x0f\x83\x10d\xe6\xf2}\x18\x8a\x9cV\xad\xd1\x90\xd0\xb8\x8f\xf7\xce\xf0\x1d\xa4\xf9\x14\x9bI/\xb6\xd4\x8a\x06\x1f\x19\xa3t\xeb\xb6!\x0c~L\xc4u\x05\xcd\x85Z:\x8b4\xe3=\x06\xe7\x1f\xd6\xc9~`Ht\xf7\x1c\xf4(p\x8bO\x91\xe3\xf8\xd67z\xa6\xde\xf02kZ^\x1a\xd4\xeb\xd0\x01\x11=\xcb{\xcf\x16-\xec\x16\xd6;Q\xd5a\x9f\xcc\xc8a`\xd8b4\xd5\x02\x05\x0b\xa3\xb3q\x1f\xa2\xef\x16\x19\xa7\x13\xfc\xb0d\xe3n~\xc4G\xb1N\x0b\xf7p\xd8\xfb4\xa8\x84*\x0f\xee\xb4\xefF\x90;C\xaa\r\xb7\xda\x937\xf0\x83\xc1\x84\xcd,\xf3I\xaeY\xa1\'(+J\xf5O\xaa~\x88\xe7\x90I\xb2y\xdcb?1x\x96\x04B\x0c\xe2\x08\xb5\xff\xb3\xf1z[\xf8\xe9)\xffN\xb8U\xf7\xa0\x0b\xa7\xd1\xf7\x85\xcb\xbd\x1e\xb1\x8f\xbd\xa6&\xd8\xed\xf5\xc1\xbd\xce\x99A;:\xd1J_\xffm\xeeJ:')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.711260
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x91\x00\x00\x00\x12\t\x05\x005\\\xba%{\xa1(!\xd2\xe7<\x9cbF\xe7\x01\x00\x00\x00\x00\x00\x00\x00\x00w\xd5\xa0\xc2wX\x02\x1b\xee\x9a\x86dK\xd6o[\xa3L\x1a]\x1a\xcbe\x1e\xe5\xb7`\x02\x9fU\xd3\x18\xda\xea+f\x9d\x1b\x9b\xb5_\xd8\x81_\xfa\xa0{?\xc8\x1es\xea\xd9D\xd2b\xc2k\xdb?\x84\x8ed\x03\xbc\xe9\xf2\xd0\x07\xe5\xf8\xd9\xacJ0\xd6\xcb\x13\xf2\xf95\x8d\xecf/\x98g\x19\x9d\xbf\xf1I\xd1\xfc\x94\xca<\xde\x80\xcdi\x93a\xa0\xac\xa1\xa9\xc7Q\x9fn\x9e\xcfA\xd3\x17\x80\xb8\xdfi"\xf9\'\xfe\x1aG4)3\\#\xcc\x84\xb3`\xbb\x17\xc2$G\xcf\xbb\x8b\xf9v')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
() => {
|
||||||
|
return { width: window.innerWidth, height: window.innerHeight }
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* @param {{action: string, data: any}} args
|
||||||
|
* @returns {Promise<[string | null, any]>}
|
||||||
|
*/
|
||||||
|
(args) => {
|
||||||
|
const { action, data } = args
|
||||||
|
if (!window.origin && action === 'clearMarks') {
|
||||||
|
[null, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
function genActionId() {
|
||||||
|
const base62chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
|
||||||
|
const toBase62 = (num) => {
|
||||||
|
let result = ''
|
||||||
|
do {
|
||||||
|
result = base62chars[num % 62] + result
|
||||||
|
num = Math.floor(num / 62)
|
||||||
|
} while (num > 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = toBase62(Date.now())
|
||||||
|
const randomPart = Array.from(
|
||||||
|
{ length: 3 },
|
||||||
|
() => base62chars[Math.floor(Math.random() * 62)]
|
||||||
|
).join('')
|
||||||
|
|
||||||
|
return timestamp + randomPart
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionId = genActionId()
|
||||||
|
|
||||||
|
const timeoutMs = 5000
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const onMessage = (event) => {
|
||||||
|
const res = event.data
|
||||||
|
if (res.type === '__agentActionResult__' && res.actionId === actionId) {
|
||||||
|
window.removeEventListener('message', onMessage)
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
if (res.error) {
|
||||||
|
resolve([res.error, null])
|
||||||
|
} else {
|
||||||
|
resolve([null, res])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle timeout
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
window.removeEventListener('message', onMessage)
|
||||||
|
resolve(['Operation timed out', null])
|
||||||
|
}, timeoutMs)
|
||||||
|
|
||||||
|
window.addEventListener('message', onMessage)
|
||||||
|
|
||||||
|
if (window.origin === 'null') {
|
||||||
|
window.postMessage({
|
||||||
|
type: '__agentAction__',
|
||||||
|
actionId,
|
||||||
|
action,
|
||||||
|
...data
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
window.postMessage({
|
||||||
|
type: '__agentAction__',
|
||||||
|
actionId,
|
||||||
|
action,
|
||||||
|
...data
|
||||||
|
}, window.origin)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
(params) => {
|
||||||
|
try {
|
||||||
|
const select = document.querySelector(params.selector)
|
||||||
|
if (!select || select.tagName.toLowerCase() !== 'select') {
|
||||||
|
return { success: false, error: 'Select not found or invalid element type' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = Array.from(select.options)[params.option]
|
||||||
|
|
||||||
|
if (!option) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Option not found',
|
||||||
|
availableOptions: Array.from(select.options).map(o => o.text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select.value = option.value
|
||||||
|
select.dispatchEvent(new Event('change'))
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
selectedValue: option.value,
|
||||||
|
selectedText: option.text.trim()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, error: e.toString() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:51.319028
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\xe6\x04\x00\x00\x12\t\x05\x00O\x8f\x02\x80\x05X\xe3\x8a\xa7\x15\x87\xbc\xe6K\x9b\x89\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x8f\xde\xa3\xeb\xae\xd1\\\x04\nM\x96\xe7\x9aazY%z\'\x95\xfe\xa4\xf0\xb8\xed\xfa*\xa3\xb4\x1c?\xc2\xbb\xbe\x93\xfd\x00\xc5y\xebk\xba3\xf31\x8b\xfd\x17^\x05kEB\xf6\xff\xe3\x01b}N\x99V4\\\xd6\x9d:\x12\xae\x01p\xaaw\x8a\x95\x05\xc2\x83\xd9\xe4 j\x8f\xfe\xb8\xf9\xd9\x0c\x03\x87\xc9Km\xbe4\x9f\x9e>\x03\xb9\x03\xf4\xb0|\xfb\xd8Q\x9a\x1aX\xf1pmr\xf9\\\xe4\x84<]\xdd\x15\x94|B{\x85W\xcc&\xdf{A\x16\x14#+\xa7\xc1\xcb\x03<rn\x10\xd93R[\xd7T\x03\x1c\x0c\xf3\xbd\xca\xd8\xaa\xcb\x90\x88\xcb\x97#fs_\tW\xbb\x14\xe4\x0cS4"<\xbaA\xb0\x80\x87\xf8@\x17\x7fk\x01\xdd\xfa\xfd6\xff0\xa1,h\x82?%\x13L\x98Q\x9d\xd2t\x7f\x08P\xa7\xe5\x87\xfdK\'\xd0zGoY\xb1\x03y\xe8\x05|B\xf7\x1e\xc9\xe2\xd9"=/\x1d\x17\x1c3\x1b\x99\xf3T\x7f/\x96-\x0e\xdf]\x87\x8e\xd8r\x10C\xa2\x80\xdaH\x85\x03h\x01n\xe2K<R\xcf\x8f\xb1{/Sm\x13 \xfe\xe8\xe1\xf4<\n\xabi\x8b\xd2\xd6\xc9\xe8\xfc\x97*p\xa5\xd2\x1468\x0bS\xebe\xf5g[Z\xadQo\xa2\x95H9\xbdO\xb2\x14\x91\xfc\xe7\x1f\xe5\xc2\xc6\xb2\xf1\xf3x\xc2\x91\xe5\xdb\xe1Z6@Q\x1a\xdd\xac\x04\xa9\x10\xad\x89v\xca\xb9\xa0\x90\xdf\xc3\xd9\xc1\x8eY\'\xed8#?\xad\xa7\xe3a\x83\x06\x8c\x7fuW\xe1\xc3%\xf0\xdc\xdf4\x84J\xe5\xfb0i\x1do?\x83\x89\x8a\xb5\xa7\xb5z\xf38.\x01\xe8\xf8(\xb8;\x8c{\xfa\x1f\x96.\xaf*9\xec\xff\x8d\xb0)\x07\xc2\x97\xb7M\x11\xd1\x911\x8b\xd3\xf6\xedM\x9eu#\x9f\x87o6\x1e\xa5\x97\xd9\xe6&\x87d\xcb\x88\x1c\xc4yYb\r\xf7\xae\xd0V\xe8F\xdf$\x0f\x81\xe4\xf7v\xe7g\xc4\x8cwkv$\xbe\xae\xd2v\x8dx\xc9\x7f\xbe\x8c\xa1\xbcF\xd0\xf3\xa1\x89Z\x15\x8e[\x8f\xc3$\xd6S\xfe\x05<#\x87\xfb\x08m9\xbf\x9eB\xc1t\xb5\xc8\xa9\xe5\xbf\x7f\x0evh\xf5\xb6z!WX\xc8\x99\x95*?_"\'\xe5$\xc4\xe5\xba\x12l\xa9\xb1g<\x1b\xabd\x7f\x89{g\x9e\xff3\xeeT\x90\xb6\xda\r\x85\xdf\x80\xa6\xa1\x86F\xfb\xce\xd4\x16\xf1\xd5\xf7\xae&D\x8cEC\x9f\x89\x14\x18\xed\x04\xe0\xbc\x92\xe6\xf9O\x92\x1e\xd9R\xf9>Bv\x83\x99\xbe<\xd6\x0e\x9c\xa0>\xbd\xbe\x9aGT^SBq\x9e\x17o\x83{\x1c+\x8et\xdf\xa3|\xbaW\xda_K\xfd\xc8\xd1\x8b=h\x04\xa8\xe5O\x14\x8ev\xdc\xfb\xf7\x1a:4\xcd\x182\xe8\x8d%^\x94*\xe7ake\xa8\xccf\x9d;\x97\x084\x176=\x19\xf0!%\xb8\x8ai\x0c;I\x7f{\x9c\xadD\x80N\x14qni!\x10f\xd8\xb44\x9d\xb3\xb9\x1f\x0b\xf3nL\xb6\x8d`\xa3@(y\xe01\xca\xd7\xc4\xf3\xd0a\x95\x9d\xc6\xd1\xd8\xe0ybIZ\xa3\x19\xb8\xfan\xf5\xca\x08\x0b<Om\xc7 K\xfa\x97\xde\xe8\xe2qX\x90\x86=\xd4\xef\xc39\t:fg\xfdTWG\x07DI\xbb\x0f\xdc\xba\xa8\xd8g\x10\xbd\xd17N\xfe\x7f\x94\xd8\xba\xb4;\xc8\xf42\xa1\xa0o\xe3\x88Z\xff\xb7z\x01\x7f\x0f*\x0b\xb4c\xca\xfa\x92 \xa3\xac/\x04\x04!\x90S#\xc6Q\xd1.\x1f\x8f\x03\n%x\xd6\xf99\xcb\xf4\xa4Q\xcd\xc9)\x89\xe7 ^\x1d\x85 \x0e\xaf\xd5\x7f\x88\xc3\x98\x98J\x13\xd8\xd3\xf8\xbc\x89.4+\x10\xdc:\x13K\xb0\xf0\xe8\x8d\x97\xb5\x10,\xe8\xaa\xfe\xb5\x19\x98I\x0b\xdd\x82\xe3\xf4\x11,\xa6\xfa\xa5\x07;\x8b\xa0sy\xb9(r\xa6\xd6\xd0<Vp\x81\xfb>+,\xc3\xb3\x12\xe3\xfd\xdcAx\xf9\x83p\xa8\xd5\x08\x0fpb\x96e725h\xc6KiveS\x0f\x19\x07\x08\xdae\x85Z`\x89 \xe0d\xd0"\x14\xf8:D)\xf6\xd64\xb5\xfcX\xc4\xdd\xa4 \xfd`B\xd1n\xd4/\xf9]\xb8o\xe8\xa9\x96CE\xe8C\xff4\xe1\x95\x94\xc5\xb4\xd8\xd9\xb3\x94\x1bL#\xed\xce\xc19\xe3\x8aJ\xda\xa53\xa2\xf5\xbd\xa6O6\x1d\x85K[\xbd\xc78\xa2\xfb{\xbe\x91\xee\xcc\xb2\xabif\x93\x0c\xa4\xc0\x97Q\x12\xb6n\xd9\xa6219gq|4g\xc6\xd4\xce\x8c\xd8\x83\xfbu\x05\x8b\x97\x1f\xb5\xb0|\x04V\xbf:\xbbU\xfb\xf9\xd2r\xd1\x9a\xb69Z\xd7\xb2\xa0g-zY\xa2|\x91~@\xf7\xca\xa5\xd1\xef\x06g\xb8\xdea,\xe9E\xb0HF\xb3\xf6\xd9&\xaa\xab\xe2\xe2\xfc\x02\x8e\x1e\xab5\\\xb9\x17\xf8\x84\x84\x9c\xe4\x83\x8c\xb7\x8eQ\x94\xae \xe3\xe9\xcc$\xd2\x88\xda\x00\xc4\x03\xe8\xcb@,\xc3\xf5\xa11\xd5\x05Q\xdf\x1f\x9e"\xaa\x8a\x0bS\xcfl\xd6\x86{\xb8\x1b\xd4\xc7\x1b\xbby\'\xdbnR\xb3"\x0e\x11\x80\x8b\xf5\x06\xe7\x83^\xc5\xf7|\x92\xd3J\xdd\x9d\x95\x80\xc6y\x88 \x1f}\x07aU\x06\x88\xf2\xbf/\xff\xe3\xc1\xad\x0eI\xd7\xd0')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:51.798158
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x97\x00\x00\x00\x12\t\x05\x00{=\x8c=\xb2\x0f\xaa\xb0\x91?\xe3F\x95d\xfd0\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x11\x96e\x8e\xa0D\xa6d\xa5\xa1\x069\x9aqb.wp\xb5g\xac<\xffM\x9eM\x1e\x8du\xad\x13@\x00If\xce}\x97\x9e\x82\xfe\xf8 o\xc9\x03\x13\xc4\x1fu"\xb4\x8d\x9b\x08\x7f\x02\x8c{\x8b\xe5\xdd[\x82F\t0a\xb5\xd5\xba\x02w4\xa8\x98?\x10UK\xba\x8el\x0f\xb4\xd0\xc6\x07;\x0b\xcc\x19\xd8UB\xf6*\xcf?\xcbu\x0f\xf1\xc0\xf7\xf0\xa2\x85\xf9\x9aV\xdf\xcf\x91\x14\xb9\\r~\x18\x1b\xac\xb4Dhs]\x08@\x12\x0f\xe61S\xb5uAmg\x0c\xe3uJ\x9ch\xe0+?o\xf9')
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.519434
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00\x19\x03\x00\x00\x12\t\x05\x00\x179\xc4\x89%\t\xa6\x9c!u\xe6Dw\xe6s\xdd\x00\x00\x00\x00\x00\x00\x00\x00F\x85\xcb!l\xa1\xc0\xe9g\xe5\xb0\xc3\x92\xc1\'\x01\xe6j\x18\x9b\xf7\xb0\xe2\xa3\xec|\xdb-]\xe5G\xbe\xddK~\xc9\xa2\xa0\xa9}\x86\x8e\xcb2\xa1#\xdf}\x94\xe2\xb4\'r\xa8\xdb\x97\xf4:\xc3b\xcc\x00hj$$\x8e\xa44\x0ec\xf9\xa1\x1b\x19h\xb2]P\x08Ku\xd2\x03\x99\x8c\xce\xdb\xc2\xfc^[4\x0c7\x03\x91|\xd4t\x1f[\xaa\x91s\xe0d/\xfc\xccC/s\xae\x1f\xbf\xf8<y\xf2\xfd\x19\xb6e\x90\xb1\x1f\xed\xea\x99[\x0f\xb4\xc0Y\xff\x88\xc7\x99\xb1\xf4Tg]\xc8w\x16\xf9\x89\xfd\xe3\xefA\xb4\xc0Q\xd7(\x14o0\x8c\x18.\xea\x1d(.\x87DL\xe8\xbcmTx\x04\xed1\xd9k\x01\xd0H\xd3\x15BJ\xcd\tX\xf2a%\x05\x1e\xd5I\x0e\x10{\xae\x92ve\xe0\xa6\xd5\xcc\xf7^\xed \xe5\xbb\xce\xbc\x0b\x95(\xec#\xd9\xe8\xcf]2\xc9\xf0\x99\r}\x1ci\xda\x9b\x0f\xba7\xce\x19\x86x\xba\xc2\x1b\xbc\x16\x82?\x9e\xde\xd6E\xb2O\x1a\xa9\x15$D\xcf\x1e\x08\x87f\xd7\x80\r\x87f\xb9\xc4\x93\x1a0\x1e*\xbe%\xf7\xb4\xba\x1d\xb2A.\x83\xe8\xd4\xe9\xda\x12\xb7|T\xe8\x0cQ}\xc6\x07\xed\r\x8b\x82\x9a\x13*<|\x9c\xe4\x04:\xc86\x96\xdcA\xffa\x9b\'\xf6\xc8p\xb0"\xf8e^\xf5\xfa\xf6\x94 t\x97\xc0\xcf\x08z\x1a\t\xfd\x84\xba\xd0\x10\x93C\xb6\xb2\xd5o?\xb3\xa6\x9e\xd7\x80t\xea\x8aACF\xdd\xb5\xbb\x816q\xa6+\xb4\x1c\x04\xb6Q\xfc\xa9.b\xc7 Oth\x196@\x0c\x1d\xa8\xee\xb7\xf8\x1c\xf5\xc5w\x1c>\xcf\x01\x95\xcf\xdc\x0b\x1e\xdd\x17\x90g\xa0\xdc\xdb\xfb\xd9\x7f\xd9\x9c\x10\xd7/\xcf\x0f\x98\xf9`H\xfc\x8f!k\xd8\xa0\xabs\x89\xd3\xfdSS\xbdb\xa0\x15\xba\x12\xc2\x99\xd4v\x10\x9ck\xdcknW\x1eG(\x16\x8e\xce\x9e4\xba\xcc\x1d\xc7kk\xabq`\xbe\xa7@\xb7u\xfcA\xcdo\xae\xf5\x9a@\xf9\x8ai\xb9\xcc|y\x19\xa7\xec\xc0\x0f\xa1\xc5K\xc2\x1a(\x03.\x08\xf8\x8e\xc8\xd4q\xb8Gnixr\xeb;\xfb\xf2,+\x08\x8d5\x8d\xb3\xf0u\xbeK4\xb5&\x9f\x12E\xbfE\xf9\xc0W\xa2\n\x93&v\x16<\x17h1\xcd\xc2[\x05<)\xe0bq]w\x8d\x19J\xac\n\xc6\x95Vy9\x81k\x82\xd2o\xb2\xe6\xce\xcd\x0b\xa0r\x16oD{\xdbj$\x13Q\'\xd7+\x96M6\xf4G3\xef\xa3\xddF\x9f \x98\xe2\x9e\x10?XS\xc6\xfd\xd3\xdd\x87\x89X\xedf\xa6N^\x8ek7\xda]O\x1b\xa5\xaas\x8dB\x97-\xc4\xb2\xe9\x8d\x91!\xfbQ\x04\xbf\x07c\x18\x9d\xdajH\x95K\xae\x86D\x8c\xc6\xf6aS[\xb1\x05BumKs\x9aEt\x05t\x05\x8dm\x9a\xaa\xf3\x84\x84\x07\xefP\xee\x82\xa9\xd3\xde\xd4\xce\xb4b\x90\'hMW\xce\xd1\x05\x83c\x18}1\xe0{ \xfb\x87\xaa2\xcf\xac$\x82\xfaw\xf2\x05!\xe4\x1b\xbc\x85\xa8vy\xd1\xee\xe4\xb4\xdeu7\xb7\x0f\xda7n\x0eD\x08\xc1\xb5\xa3\xafT)\x9b\x15\x19\x987\'Ft\x99\xfa-\xc6CE\xe7\x08\x91\x81P\x81`\x88K\x9ae')
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class ApiClient:
|
||||||
|
"""
|
||||||
|
A client class for making requests to an internal API aggregation platform.
|
||||||
|
|
||||||
|
This class provides a simple interface to call APIs through an internal proxy service
|
||||||
|
that aggregates multiple external APIs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Initialize the API client with the base host URL for the proxy service.
|
||||||
|
"""
|
||||||
|
host = os.getenv('RUNTIME_API_HOST', 'https://api.manus.im')
|
||||||
|
self.host = f'{host}/apiproxy.v1.ApiProxyService/CallApi'
|
||||||
|
with Path().home().joinpath('.secrets/sandbox_api_token').open('r') as f:
|
||||||
|
self.token = f.read().strip()
|
||||||
|
|
||||||
|
def _convert_bool_to_str(self, data: dict | None) -> dict | None:
|
||||||
|
"""Convert boolean values in dictionary to strings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (dict | None): Input dictionary that may contain boolean values
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict | None: Dictionary with boolean values converted to strings, or None if input is None
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for key, value in data.items():
|
||||||
|
if isinstance(value, bool):
|
||||||
|
result[key] = str(value).lower()
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
result[key] = self._convert_bool_to_str(value)
|
||||||
|
else:
|
||||||
|
result[key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
def call_api(
|
||||||
|
self,
|
||||||
|
api_id_or_name: str,
|
||||||
|
body: dict | None = None,
|
||||||
|
query: dict | None = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Make an API call through the proxy service.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
api_id_or_name (str): The ID or name of the API to call
|
||||||
|
body (dict, optional): The request body to send. Defaults to None.
|
||||||
|
query (dict, optional): The query parameters to send. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The JSON response from the API call, or an error dict if the call fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
resp = requests.post(
|
||||||
|
f'{self.host}',
|
||||||
|
json={
|
||||||
|
'apiId': api_id_or_name,
|
||||||
|
'body': self._convert_bool_to_str(body),
|
||||||
|
'query': self._convert_bool_to_str(query),
|
||||||
|
},
|
||||||
|
headers={'x-sandbox-token': self.token},
|
||||||
|
)
|
||||||
|
data = resp.json()
|
||||||
|
if 'jsonData' in data:
|
||||||
|
return json.loads(data['jsonData'])
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
return {'error': str(e)}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, 2025-03-07T15:51:50.512992
|
||||||
|
from .pyarmor_runtime import __pyarmor__
|
||||||
Binary file not shown.
@ -0,0 +1,3 @@
|
|||||||
|
# Pyarmor 9.1.0 (basic), 009055, Manus, 2025-03-07T15:51:50.544057
|
||||||
|
from pyarmor_runtime_009055 import __pyarmor__
|
||||||
|
__pyarmor__(__name__, __file__, b'PY009055\x00\x03\x0b\x00\xa7\r\r\n\x80\x00\x01\x00\x08\x00\x00\x00\x04\x00\x00\x00@\x00\x00\x00*\x04\x00\x00\x12\t\x05\x00\x03ZDwj\x13vA>7\x1c\x9d\x7f\xb1\xea5\x00\x00\x00\x00\x00\x00\x00\x00sl\xb9\xd9\xc8\x00\xd3\xfc\xae\xb2Q\xd4\x14\xcb5CW\x1e\x86}I\xab\x8c!\xcd\x7f\xee\xa0us\x0e\xfc\x1e \xe7t\x16\xe7@z6f\xee`\xcec\xd2h\x89FT\xd2c\xd3S\xa1\x1fq\xb4\xd2\xbe\xd8\x0fD\xc2n\xfd2\xb4\xc9\xbf&\xb4\x93\x8c\xfc\xc4\xff>m\r\xb4\xed(U\x03\x9d\xcb8\xbbO$\x93\xd6qX\x89\x05\xaf\xf3~\xa3C\xc1\x97`\xbf#[\xe5\xf4\xea=\xd8\xe4\x01;b\x1a\xe0\xe8\x0c\x16\x19#\x8c\x04\n\xdcr\xdc&\x13\xd05c|}w\xcf\xa3MR\x9fN&\tG\xe5\x0b\xad:\x937\xd7\xec\x86\x88\x85\x00\xbf\x1br\xf7r\x8bC\x17\x13\x97\xbbm\x90\xb4\xb5\xc5\x11\xd71\xae\xe0\xe4H\xd5\xa0\xd1\xf1\xe0\xa4\xc7M\x08\xdb D\x1dx\xde\xe6#\xfaR-4\x8bv/\x11#\xe8k\x86+R\xca\x9e\x81\'\xa5\xdfP\xa7\xa1\xd0\x1a\xb1\x0c{\xc4\x8d\x12\x9e\xaf\xec5y"\xf9tj\xc74\xc1|\xa2\xd8\xd6\xbfyvk\xd1\xe9+K\x13\xc7\xadXHlR\xf6\x875\xca-\xc6\xebOb\xfd\xbf\x8bL\xf3p\x05\xdb\xed(\xe4G\xbb\x8em\x98\x03\xf1\xe5\xf6\n\x8e\x8el\xca\xff\xeb\xd2=\x07 \x14\xfb\xb3qQ\xdb\xb0\xf7\x80Z\xd1\xe1\xa1\x13\x04\xe5~\xea\xf9\xb4K\xcc\x95\xfc\xa1(@\x17\xf2\xc8\xc6\x99zy\xc6\x08\x18\xd7\xf6YDS}\xc5\xfd\xa8\x99]\x14\x91\xa0Xc\xb5\xf5Oc\xc7r\x7f\xe3\x80\xacU\xe8\x8f\x1ct{\x12!\x7f\xfa\x95\x1c\xc3\xa6\x05&\xcaJ\xda\x10\x94ji\xd9o\xe5jGv\xf1n\xfaD5\xd5\x87d\x8a(\xaa9\x05\xf7\xcf\x9e\xbb\xdf\xb8T\xef#\xec\xe7@\xa5\xa6\xc3\xa9\x8cVt\xfa\xd4y\x08\x88+\xa9\xdd\xc9\x1c\xdbZ\xc6\x0bK\x91\xf0a4\xb8\xf7\xcd\x17C\xef\xca06\\f\xe0\xf2\xd6\x84O\xea\xd9\x92Kw\x82\xfbjU"\xaa\xe8\xeb\x82\xc9\x13$T\xa6$\xb5\xf8|E|c6\x8f3K\xdd\xd3\x88\x9d>N\xa4N\x1ak\xf4\x0eR2\xf7\xb4Xb\x9b\xe1\xb0d\x1e\xfe\xe5\x86\xe0\xe5\x05+\x97\x8e:\x81\x86b\xad\xbf\xbf4\xb8\xd5_\xaf:8\xb0P\x19\x0603\xae\xa81\xd2:\x7f%\x125N\xbaM!\xc2\xb2\x8a\xa8\x84\xd2\x906r\xb1[j\x80\x03\xc2\xeaM\xd6\x16mD\t\xe5\xc5\xe3\xa1\xb5\xccb\x84\x08w$\xce\xab\xd3\x08F\xeb\x8fL\xf4\xf7\x00\x98\xea\xa9L\x13c\xce\xfc\xd0\xae\x13lIf\xbf\xb0}\xcc\x15o\xc9\xc1OW\\\xbe0\x1c\x1a\x13\x08\xbc+~\xce\x0e\x85\x86\xa7\xc8\xf1\xd0\xd5j\xcfY\xe1\xff"\x80\x15\xcaBU3\xa3\xcb\x1d\xea\x99}\xabs\xe9\xf9\xf4W4\xbe\x02\xf9\xf9\xedNr\xbc\xa0\xe5m\x11\xc7\xaa_#\x81h-\xc9`\xec\xbd\xf5\xc6\xad\xe7\x04\xc1\x8a\x11sZ\xaa)\xeb2\xeb\xeb\xd0BCt\x15\xabg4V\xaf\xd4GA\xc7\xe1\xae+\x9d+\x1e\x92\x94}W\xdcp\xe6G\xa7 \xe6\x90*\x16R\x82\xdf\xe1D\xb8\x8b\xab\xf5=\xe5\x19_!\xcb=2C\xd0\x19\x19\xfc1F\xd0\x85\xf0\x94\x8f\x80\xd4\xebq\x07\x1d\xbc\xec<\x85\xdd=Z,\xf2!\xdd\x8d\x98\xa5\t\x87\xde\xa6\xec\xd4\x84\xc1\xa9\xa3\xd2\x93f\xcd\xecO7\x7f\x1b\xb6\xdb\x13BG\xf0\n@|\x01\xec\xe3\x92z;\xd3^\xeajy\xad7\x8e\xac\x83#=S\x85\xbeC\xe0\x9c\x85\x8cE\xf0"bV\x05\n\xe7\xfc%\x16\x80"\xac\r_Z\x80\xe2X\x8c~uG\x8a\x18\tv\x7f\xd7\xad\r,\x8a\x91f[\xa3\x94*\xbb %\xbc,9\x0c\xde\xc6\xa9-u>\\\x83\xc2yG\xf8\x04\x98"\xb8xR`\xaa\x9f\xd5\xd4\xb2\x02\x0f\xbe!EbRF$\xb1bi\xecQ\xf8\x18\xa3n\xb7\x15\xd1PN}\xdf\xa9\xf1G\x8c\xd9\xe9X\xf2p\x89\x9c\x0b\xe1\xcc\x92\t\rA\x81\x10\xf6\x11\'d\xaa\xca\xa6\xe6\xff\xb9\x89\x80\xad\xb0\x12e\xbc\xf1\xca\x8f\x9b4|$<\xe7\xb1Lo\xcd\xa2\xfb\x87\x1a\xe5C\x19vj\x9b\xef\xd3\xa7\xc9\xa8\xdf\xa0\x86\'\xea\x85\xe8\xb4\xe3+\x91M;\xbfM\x96\xcd\xc3\xf3hv\xe1ot.&\x0b\xa5\xf9\x083\x9b1\xa9\xeb\xb8\n\xc0]n\tq\x00\xbb\xc9\xf8=\xdd\xe3\xe8\xa7E\x18L\x9d\xbb\xe8\xed\xb9\xcd.')
|
||||||
5
Manus Agent Tools & Prompt/manus-sandbox/root/e2b-startup.sh
Executable file
5
Manus Agent Tools & Prompt/manus-sandbox/root/e2b-startup.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
if ! sudo -u ubuntu /startup.sh; then
|
||||||
|
echo "Error: Script failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
145
Manus Agent Tools & Prompt/manus-sandbox/root/startup.sh
Executable file
145
Manus Agent Tools & Prompt/manus-sandbox/root/startup.sh
Executable file
@ -0,0 +1,145 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Exit on error, but don't kill background processes
|
||||||
|
# set -e
|
||||||
|
|
||||||
|
# 开启调试输出
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
echo "Load env file..."
|
||||||
|
source "$HOME/.env"
|
||||||
|
|
||||||
|
# Function to add env loading to rc files
|
||||||
|
add_env_to_rc() {
|
||||||
|
local rc_file="$1"
|
||||||
|
local env_line='[ -f "$HOME/.env" ] && source "$HOME/.env"'
|
||||||
|
|
||||||
|
if [ -f "$rc_file" ]; then
|
||||||
|
if ! grep -q "^\[ -f \"\$HOME/.env\" \] && source \"\$HOME/.env\"" "$rc_file"; then
|
||||||
|
echo "Adding environment loading to $rc_file"
|
||||||
|
echo "$env_line" >>"$rc_file"
|
||||||
|
else
|
||||||
|
echo "Environment loading already exists in $rc_file"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Creating $rc_file with environment loading"
|
||||||
|
echo "$env_line" >"$rc_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add environment loading to rc files
|
||||||
|
add_env_to_rc "$HOME/.bashrc"
|
||||||
|
add_env_to_rc "$HOME/.zshrc"
|
||||||
|
|
||||||
|
# Load environment for current session
|
||||||
|
if [ -f $HOME/.env ]; then
|
||||||
|
echo "Found .env file in $HOME, loading..."
|
||||||
|
set -a
|
||||||
|
source $HOME/.env
|
||||||
|
set +a
|
||||||
|
echo "Environment variables loaded successfully"
|
||||||
|
else
|
||||||
|
echo "No .env file found in $HOME, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting dbus..."
|
||||||
|
# 清理旧的 dbus pid 文件
|
||||||
|
sudo rm -f /run/dbus/pid
|
||||||
|
sudo rm -f /var/run/dbus/pid
|
||||||
|
sudo mkdir -p /var/run/dbus
|
||||||
|
sudo service dbus start
|
||||||
|
dbus-daemon --session --fork
|
||||||
|
|
||||||
|
export DISPLAY=:0
|
||||||
|
# 需要跟 dockerfile 保持一致
|
||||||
|
export MANUS_OPT_PATH=/opt/.manus
|
||||||
|
export RUNTIME_PATH=${MANUS_OPT_PATH}/.sandbox-runtime
|
||||||
|
export PIPENV_VENV_IN_PROJECT=1
|
||||||
|
|
||||||
|
echo "Starting runtime..."
|
||||||
|
echo Opt Path: $MANUS_OPT_PATH
|
||||||
|
echo Runtime Path: $RUNTIME_PATH
|
||||||
|
|
||||||
|
echo "Starting supervisord"
|
||||||
|
sudo supervisord &
|
||||||
|
sleep 1 # 等待 supervisord 启动并创建 pid 文件
|
||||||
|
SUPERVISOR_PID=$(cat /var/run/supervisord.pid)
|
||||||
|
echo "Checking supervisord status..."
|
||||||
|
echo "Supervisor PID: $SUPERVISOR_PID"
|
||||||
|
sudo supervisorctl status || true
|
||||||
|
|
||||||
|
# 等待服务启动的函数
|
||||||
|
wait_for_services() {
|
||||||
|
echo "Waiting for services to start..."
|
||||||
|
MAX_RETRIES=30
|
||||||
|
|
||||||
|
# runtime
|
||||||
|
RETRY_COUNT=0
|
||||||
|
while ! curl -s http://localhost:8330/healthz >/dev/null && [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
||||||
|
echo "Waiting for Sandbox Runtime ... ($((MAX_RETRIES - RETRY_COUNT)) attempts left)"
|
||||||
|
sleep 1
|
||||||
|
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# # code server
|
||||||
|
# RETRY_COUNT=0
|
||||||
|
# while ! curl -s http://localhost:8329/healthz >/dev/null && [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
||||||
|
# echo "Waiting for code-server... ($((MAX_RETRIES - RETRY_COUNT)) attempts left)"
|
||||||
|
# sleep 1
|
||||||
|
# RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||||
|
# done
|
||||||
|
|
||||||
|
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
|
||||||
|
echo "Warning: Some services failed to start in time"
|
||||||
|
else
|
||||||
|
echo "All services started!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Starting log tail..."
|
||||||
|
tail -f /var/log/supervisor/sandbox*.log /var/log/supervisor/supervisord.log &
|
||||||
|
|
||||||
|
wait_for_services
|
||||||
|
|
||||||
|
# sudo sh -c 'echo "173.234.14.135 pool.ntp.org" >> /etc/hosts'
|
||||||
|
|
||||||
|
# 守护进程监控函数
|
||||||
|
monitor_and_restart() {
|
||||||
|
echo "Starting monitor and restart..."
|
||||||
|
while true; do
|
||||||
|
sudo ntpdate pool.ntp.org
|
||||||
|
# echo "checking supervisord"
|
||||||
|
# 检查 supervisord 并显示状态
|
||||||
|
if ! sudo kill -0 $SUPERVISOR_PID 2>/dev/null; then
|
||||||
|
echo "Supervisord died, restarting..."
|
||||||
|
sudo supervisord &
|
||||||
|
SUPERVISOR_PID=$(cat /var/run/supervisord.pid)
|
||||||
|
sleep 1
|
||||||
|
echo "Supervisord status after restart:"
|
||||||
|
sudo supervisorctl status || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# echo "checking dbus"
|
||||||
|
# 检查 dbus
|
||||||
|
if ! pgrep dbus-daemon >/dev/null; then
|
||||||
|
echo "DBus died, restarting..."
|
||||||
|
sudo service dbus start
|
||||||
|
dbus-daemon --session --fork
|
||||||
|
fi
|
||||||
|
|
||||||
|
# echo "checking runtime"
|
||||||
|
# 检查 Sandbox Runtime 健康状态
|
||||||
|
if ! curl -s http://localhost:8330/healthz >/dev/null; then
|
||||||
|
echo "Sandbox Runtime is not responding, attempting to restart..."
|
||||||
|
sudo supervisorctl restart sandbox_runtime
|
||||||
|
sleep 1
|
||||||
|
echo "Sandbox Runtime status after restart:"
|
||||||
|
sudo supervisorctl status sandbox_runtime || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 每10秒检查一次
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_and_restart
|
||||||
@ -0,0 +1 @@
|
|||||||
|
TOKENS ARE STORED UNDER THE DIRECTORY secrets/
|
||||||
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/env
Executable file
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/env
Executable file
Binary file not shown.
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd
Executable file
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd
Executable file
Binary file not shown.
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd-v0.0.1
Executable file
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd-v0.0.1
Executable file
Binary file not shown.
166214
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd_strings.txt
Normal file
166214
Manus Agent Tools & Prompt/manus-sandbox/usr/bin/envd_strings.txt
Normal file
File diff suppressed because one or more lines are too long
114
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_fastapi_app
Executable file
114
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_fastapi_app
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# USAGE: create_fastapi_app [app_name]
|
||||||
|
|
||||||
|
APP_NAME=$1
|
||||||
|
|
||||||
|
# Check for help flag or missing argument
|
||||||
|
if [ "$1" == "--help" ] || [ "$1" == "-h" ] || [ -z "$1" ] || [ "$1" == "." ]; then
|
||||||
|
echo "Creates a new FastAPI application template for Cloudflare Workers"
|
||||||
|
echo
|
||||||
|
echo "USAGE:"
|
||||||
|
echo " create_fastapi_app [app_name]"
|
||||||
|
echo
|
||||||
|
echo "ARGUMENTS:"
|
||||||
|
echo " app_name Name of the FastAPI application to create"
|
||||||
|
echo
|
||||||
|
echo "EXAMPLE:"
|
||||||
|
echo " create_fastapi_app my_api"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create project directory
|
||||||
|
mkdir -p $APP_NAME/src
|
||||||
|
pushd $APP_NAME > /dev/null
|
||||||
|
|
||||||
|
# Create wrangler.toml
|
||||||
|
cat <<EOF > wrangler.toml
|
||||||
|
name = "$APP_NAME"
|
||||||
|
main = "src/worker.py"
|
||||||
|
compatibility_flags = ["python_workers"]
|
||||||
|
compatibility_date = "2023-12-18"
|
||||||
|
|
||||||
|
[vars]
|
||||||
|
MESSAGE = "Welcome to FastAPI on Cloudflare Workers!"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create src/worker.py
|
||||||
|
cat <<EOF > src/worker.py
|
||||||
|
from fastapi import FastAPI, Request
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import asgi
|
||||||
|
|
||||||
|
# Initialize FastAPI app
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# Add CORS middleware
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Worker entry point
|
||||||
|
async def on_fetch(request, env):
|
||||||
|
return await asgi.fetch(app, request, env)
|
||||||
|
|
||||||
|
# Basic health check endpoint
|
||||||
|
@app.get("/healthz")
|
||||||
|
async def healthz():
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
# Example of accessing environment variables
|
||||||
|
@app.get("/env")
|
||||||
|
async def get_env(req: Request):
|
||||||
|
env = req.scope["env"]
|
||||||
|
return {
|
||||||
|
"message": env.MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example data model
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str | None = None
|
||||||
|
price: float
|
||||||
|
tax: float | None = None
|
||||||
|
|
||||||
|
# CRUD endpoints examples
|
||||||
|
@app.post("/items/")
|
||||||
|
async def create_item(item: Item):
|
||||||
|
return item
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}")
|
||||||
|
async def read_item(item_id: int):
|
||||||
|
return {"item_id": item_id}
|
||||||
|
|
||||||
|
@app.put("/items/{item_id}")
|
||||||
|
async def update_item(item_id: int, item: Item, q: str | None = None):
|
||||||
|
result = {"item_id": item_id, **item.model_dump()}
|
||||||
|
if q:
|
||||||
|
result.update({"q": q})
|
||||||
|
return result
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create requirements.txt
|
||||||
|
cat <<EOF > requirements.txt
|
||||||
|
fastapi
|
||||||
|
pydantic
|
||||||
|
EOF
|
||||||
|
|
||||||
|
APP_PATH="$(pwd)"
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
echo "Created new FastAPI Cloudflare Worker app $APP_NAME at $APP_PATH"
|
||||||
|
echo
|
||||||
|
echo "Project structure:"
|
||||||
|
echo "├── wrangler.toml"
|
||||||
|
echo "└── src/"
|
||||||
|
echo " └── worker.py"
|
||||||
|
echo
|
||||||
|
echo "To deploy this FastAPI application, use the deploy_websites command."
|
||||||
|
echo "The project root is at: $APP_PATH"
|
||||||
88
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_nextjs_app
Executable file
88
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_nextjs_app
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# USAGE: create_nextjs_app [app_name]
|
||||||
|
|
||||||
|
APP_NAME=$1
|
||||||
|
TEMPLATE_DIR="/opt/.manus/deploy/templates/next"
|
||||||
|
|
||||||
|
# Check for help flag or missing argument
|
||||||
|
if [ "$1" == "--help" ] || [ "$1" == "-h" ] || [ -z "$1" ] || [ "$1" == "." ]; then
|
||||||
|
echo "Creates a new Next.js application configured for Cloudflare Workers from template"
|
||||||
|
echo
|
||||||
|
echo "USAGE:"
|
||||||
|
echo " create_nextjs_app [app_name]"
|
||||||
|
echo
|
||||||
|
echo "ARGUMENTS:"
|
||||||
|
echo " app_name Name of the Next.js application to create"
|
||||||
|
echo
|
||||||
|
echo "EXAMPLE:"
|
||||||
|
echo " create_nextjs_app my_app"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if template directory exists
|
||||||
|
if [ ! -d "$TEMPLATE_DIR" ]; then
|
||||||
|
echo "Error: Template directory not found at $TEMPLATE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting setup..."
|
||||||
|
echo "Creating Next.js app for Cloudflare Workers: $APP_NAME"
|
||||||
|
|
||||||
|
# Create destination directory
|
||||||
|
mkdir -p "$APP_NAME"
|
||||||
|
APP_PATH="$(cd "$APP_NAME" && pwd)"
|
||||||
|
|
||||||
|
# Copy template files
|
||||||
|
cp -r "$TEMPLATE_DIR/"* "$APP_PATH/"
|
||||||
|
|
||||||
|
pushd $APP_NAME > /dev/null
|
||||||
|
# Update project name in various files
|
||||||
|
sed -i "s/next-cloudflare-template/$APP_NAME/g" "package.json"
|
||||||
|
sed -i "s/next-cloudflare-template/$APP_NAME/g" "wrangler.toml"
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
echo "Installing dependencies..."
|
||||||
|
pnpm install > /dev/null 2>&1
|
||||||
|
echo "Initializing git repository..."
|
||||||
|
wrangler d1 execute DB --local --file=migrations/0001_initial.sql > /dev/null 2>&1
|
||||||
|
git init > /dev/null 2>&10
|
||||||
|
git add . > /dev/null 2>&1
|
||||||
|
git commit -m "init" > /dev/null 2>&1
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
|
||||||
|
echo "Created new Next.js app $APP_NAME at $APP_PATH"
|
||||||
|
echo
|
||||||
|
echo "=== Project Structure ==="
|
||||||
|
echo "├── migrations/ # D1 database migration files"
|
||||||
|
echo "│ └── 0001_initial.sql # Initialize database tables"
|
||||||
|
echo "├── src/"
|
||||||
|
echo "│ ├── app/ # Next.js pages"
|
||||||
|
echo "│ │ └── counter.ts # Server operation examples"
|
||||||
|
echo "│ ├── components/"
|
||||||
|
echo "│ ├── hooks/"
|
||||||
|
echo "│ └── lib/"
|
||||||
|
echo "└── wrangler.toml # Cloudflare configuration"
|
||||||
|
echo
|
||||||
|
echo "=== D1 Database Operations Guide ==="
|
||||||
|
echo "1. Local development database config (wrangler.toml):"
|
||||||
|
echo " [[d1_databases]]"
|
||||||
|
echo " binding = \"DB\" # Environment variable name"
|
||||||
|
echo " database_name = \"local-db\" # Database name"
|
||||||
|
echo " database_id = \"local\" # ID for local development"
|
||||||
|
echo
|
||||||
|
echo "2. Execute SQL file to reset database:"
|
||||||
|
echo " wrangler d1 execute DB --local --file=migrations/0001_initial.sql"
|
||||||
|
echo
|
||||||
|
echo "3. Apply new migrations:"
|
||||||
|
echo " wrangler d1 migrations apply DB --local"
|
||||||
|
echo
|
||||||
|
echo "Example counter.ts demonstrates how to:"
|
||||||
|
echo "- Insert and update counters"
|
||||||
|
echo "- Record access logs"
|
||||||
|
echo "- Query recent access records"
|
||||||
|
echo
|
||||||
|
echo "Tip: To reset local database, simply rm -rf .wrangler/state/v3 and re-execute SQL file"
|
||||||
|
echo "Happy coding! 🚀"
|
||||||
|
|
||||||
53
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_react_app
Executable file
53
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/create_react_app
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# USAGE: create_react_app [app_name]
|
||||||
|
|
||||||
|
APP_NAME=$1
|
||||||
|
TEMPLATE_DIR="/opt/.manus/deploy/templates/react"
|
||||||
|
|
||||||
|
# Check for help flag or no arguments
|
||||||
|
if [ "$1" == "--help" ] || [ "$1" == "-h" ] || [ -z "$1" ] || [ "$1" == "." ]; then
|
||||||
|
echo "Creates a new React application with basic setup and Tailwind, shadcn/ui, Lucide and Recharts"
|
||||||
|
echo
|
||||||
|
echo "USAGE:"
|
||||||
|
echo " create_react_app [app_name]"
|
||||||
|
echo
|
||||||
|
echo "ARGUMENTS:"
|
||||||
|
echo " app_name Name of the React application to create"
|
||||||
|
echo
|
||||||
|
echo "EXAMPLE:"
|
||||||
|
echo " create_react_app my_app"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if template directory exists
|
||||||
|
if [ ! -d "$TEMPLATE_DIR" ]; then
|
||||||
|
echo "Error: Template directory not found at $TEMPLATE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting setup... (this might take a while)"
|
||||||
|
# Create destination directory
|
||||||
|
mkdir -p "$APP_NAME"
|
||||||
|
APP_PATH="$(cd "$APP_NAME" && pwd)"
|
||||||
|
|
||||||
|
# Copy template files
|
||||||
|
cp -r "$TEMPLATE_DIR/"* "$APP_PATH/"
|
||||||
|
|
||||||
|
pushd $APP_NAME > /dev/null
|
||||||
|
echo "Created new React app ${APP_NAME} at ${APP_PATH}"
|
||||||
|
|
||||||
|
sed -i "s/next-cloudflare-template/$APP_NAME/g" "package.json"
|
||||||
|
sed -i "s/<title>Vite + React + TS<\/title>/<title>${APP_NAME}<\/title>/" index.html
|
||||||
|
# Update the title in index.html to use the app name
|
||||||
|
echo "Updated app title to ${APP_NAME}"
|
||||||
|
# Install dependencies
|
||||||
|
echo "Installing dependencies..."
|
||||||
|
pnpm install > /dev/null 2>&1
|
||||||
|
echo "Initializing git repository..."
|
||||||
|
git init > /dev/null 2>&1
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
echo "Important commands to run in the shell:"
|
||||||
|
echo "To start the dev server, run 'pnpm run dev' from the project root This will restart on file-save, so you can leave this running in a dedicated shell and alternate between making changes using edit_file and testing them in your browser without needing to re-start the dev server."
|
||||||
|
echo "The project root is ${APP_PATH}"
|
||||||
8
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/playwright
Executable file
8
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/playwright
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from playwright.__main__ import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/websockify
Executable file
BIN
Manus Agent Tools & Prompt/manus-sandbox/usr/local/bin/websockify
Executable file
Binary file not shown.
@ -0,0 +1,104 @@
|
|||||||
|
ubuntu@0108eab2bc5f:~/manus-sandbox$ tail -f /var/log/supervisor/*.log
|
||||||
|
==> /var/log/supervisor/Xvfb.err.log <==
|
||||||
|
|
||||||
|
==> /var/log/supervisor/Xvfb.log <==
|
||||||
|
|
||||||
|
==> /var/log/supervisor/chrome.err.log <==
|
||||||
|
[448:495:0309/192100.982737:ERROR:bus.cc(408)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
|
||||||
|
[448:495:0309/192100.982747:ERROR:bus.cc(408)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
|
||||||
|
[448:448:0312/194656.409380:ERROR:CONSOLE(275)] "Uncaught TypeError: Cannot read properties of undefined (reading 'recordValue')", source: chrome://new-tab-page/new_tab_page.js (275)
|
||||||
|
[548:559:0312/194700.459787:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194701.638975:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194706.655053:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194707.564584:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194708.593869:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194713.613541:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
[548:559:0312/194746.995438:ERROR:socket_posix.cc(93)] CreatePlatformSocket() failed: Address family not supported by protocol (97)
|
||||||
|
|
||||||
|
==> /var/log/supervisor/chrome.log <==
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
[chrome] Waiting for sandbox-runtime to be ready...
|
||||||
|
sandbox-runtime is ready, starting chrome...
|
||||||
|
|
||||||
|
==> /var/log/supervisor/code-server.err.log <==
|
||||||
|
File not found: /usr/lib/code-server/lib/vscode/node_modules/vsda/rust/web/vsda_bg.wasm
|
||||||
|
File not found: /usr/lib/code-server/lib/vscode/node_modules/vsda/rust/web/vsda.js
|
||||||
|
[19:39:05] [File Watcher (node.js)] Watcher shutdown because watched path got deleted
|
||||||
|
File not found: /usr/lib/code-server/lib/vscode/node_modules/vsda/rust/web/vsda.js
|
||||||
|
File not found: /usr/lib/code-server/lib/vscode/node_modules/vsda/rust/web/vsda_bg.wasm
|
||||||
|
|
||||||
|
==> /var/log/supervisor/code-server.log <==
|
||||||
|
|
||||||
|
[19:30:51] Extension host agent started.
|
||||||
|
[19:30:51] Started initializing default profile extensions in extensions installation folder. file:///home/ubuntu/.local/share/code-server/extensions
|
||||||
|
[19:30:51] Completed initializing default profile extensions in extensions installation folder. file:///home/ubuntu/.local/share/code-server/extensions
|
||||||
|
[19:30:53] [10.31.200.1][427648eb][ManagementConnection] New connection established.
|
||||||
|
[19:30:54] [10.31.200.1][965db1bf][ExtensionHostConnection] New connection established.
|
||||||
|
[19:30:55] [10.31.200.1][965db1bf][ExtensionHostConnection] <3678> Launched Extension Host Process.
|
||||||
|
[19:45:19] [10.167.231.1][114aa6f7][ManagementConnection] New connection established.
|
||||||
|
[19:45:20] [10.167.231.1][6fdc2f64][ExtensionHostConnection] New connection established.
|
||||||
|
[19:45:20] [10.167.231.1][6fdc2f64][ExtensionHostConnection] <8156> Launched Extension Host Process.
|
||||||
|
|
||||||
|
==> /var/log/supervisor/sandbox-runtime.err.log <==
|
||||||
|
INFO: Started server process [385]
|
||||||
|
INFO: Waiting for application startup.
|
||||||
|
INFO: Application startup complete.
|
||||||
|
INFO: Uvicorn running on http://0.0.0.0:8330 (Press CTRL+C to quit)
|
||||||
|
|
||||||
|
==> /var/log/supervisor/sandbox-runtime.log <==
|
||||||
|
2025-03-12 19:46:56,437 - DEBUG - New page opened: chrome://new-tab-page/
|
||||||
|
2025-03-12 19:47:15,829 - INFO - [>>] GET /healthz
|
||||||
|
2025-03-12 19:47:15,830 - INFO - [<<] Finished handling GET /healthz in 0.14901161193847656ms
|
||||||
|
INFO: 127.0.0.1:45128 - "GET /healthz HTTP/1.1" 200 OK
|
||||||
|
2025-03-12 19:47:35,842 - INFO - [>>] GET /healthz
|
||||||
|
2025-03-12 19:47:35,842 - INFO - [<<] Finished handling GET /healthz in 0.16069412231445312ms
|
||||||
|
INFO: 127.0.0.1:54594 - "GET /healthz HTTP/1.1" 200 OK
|
||||||
|
2025-03-12 19:47:55,051 - INFO - [>>] GET /healthz
|
||||||
|
2025-03-12 19:47:55,051 - INFO - [<<] Finished handling GET /healthz in 0.2079010009765625ms
|
||||||
|
INFO: 127.0.0.1:39562 - "GET /healthz HTTP/1.1" 200 OK
|
||||||
|
|
||||||
|
==> /var/log/supervisor/supervisord.log <==
|
||||||
|
2025-03-07 15:55:08,161 INFO success: sandbox-runtime entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
|
||||||
|
2025-03-07 15:55:08,163 INFO success: Xvfb entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
|
||||||
|
2025-03-07 15:55:17,205 INFO success: chrome entered RUNNING state, process has stayed up for > than 10 seconds (startsecs)
|
||||||
|
2025-03-07 10:55:31,439 CRIT Supervisor is running as root. Privileges were not dropped because no user is specified in the config file. If you intend to run as root, you can set user=root in the config file to avoid this message.
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/1-sandbox-runtime.conf" during parsing
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/2-xvfb.conf" during parsing
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/4-chrome.conf" during parsing
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/5-x11vnc.conf" during parsing
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/6-websockify.conf" during parsing
|
||||||
|
2025-03-07 10:55:31,439 INFO Included extra file "/etc/supervisor/conf.d/7-code-server.conf" during parsing
|
||||||
|
|
||||||
|
==> /var/log/supervisor/websockify.err.log <==
|
||||||
|
INFO New connection target=127.0.0.1:5900
|
||||||
|
Mar 9 19:20:28: new connection to target 127.0.0.1:5900
|
||||||
|
INFO New connection target=127.0.0.1:5900
|
||||||
|
Mar 9 19:20:29: new connection to target 127.0.0.1:5900
|
||||||
|
Mar 9 19:21:57: reading from WS failed: repeated read on failed websocket connection
|
||||||
|
Mar 9 19:21:57: reading from TCP failed: read tcp 127.0.0.1:33764->127.0.0.1:5900: use of closed network connection
|
||||||
|
Mar 9 19:21:57: reading from WS failed: repeated read on failed websocket connection
|
||||||
|
Mar 9 19:21:57: reading from TCP failed: read tcp 127.0.0.1:33780->127.0.0.1:5900: use of closed network connection
|
||||||
|
INFO New connection target=127.0.0.1:5900
|
||||||
|
Mar 12 19:46:50: new connection to target 127.0.0.1:5900
|
||||||
|
|
||||||
|
==> /var/log/supervisor/websockify.log <==
|
||||||
|
|
||||||
|
==> /var/log/supervisor/x11vnc.err.log <==
|
||||||
|
12/03/2025 19:46:51 rfbProcessClientNormalMessage: ignoring unsupported encoding type Enc(0xC0A1E5CE)
|
||||||
|
12/03/2025 19:46:51 rfbProcessClientNormalMessage: ignoring unsupported encoding type Enc(0x574D5664)
|
||||||
|
12/03/2025 19:46:51 Enabling full-color cursor updates for client 127.0.0.1
|
||||||
|
12/03/2025 19:46:51 Using tight encoding for client 127.0.0.1
|
||||||
|
12/03/2025 19:46:51 Sending rfbEncodingExtDesktopSize for size (1280x1029)
|
||||||
|
12/03/2025 19:46:51 client_set_net: 127.0.0.1 0.0001
|
||||||
|
12/03/2025 19:46:56 client 20 network rate 84.5 KB/sec (11802.3 eff KB/sec)
|
||||||
|
12/03/2025 19:46:56 client 20 latency: 121.6 ms
|
||||||
|
12/03/2025 19:46:56 dt1: 0.0021, dt2: 0.2589 dt3: 0.1216 bytes: 16922
|
||||||
|
12/03/2025 19:46:56 link_rate: LR_BROADBAND - 121 ms, 84 KB/s
|
||||||
|
|
||||||
|
==> /var/log/supervisor/x11vnc.log <==
|
||||||
|
PORT=5900
|
||||||
@ -40,7 +40,7 @@ Check out **ZeroLeaks**, a service designed to help startups **identify and secu
|
|||||||
🚨 **Note:** We no longer use GitHub issues for roadmap and feedback.
|
🚨 **Note:** We no longer use GitHub issues for roadmap and feedback.
|
||||||
Please visit [System Prompts Roadmap & Feedback](https://systemprompts.featurebase.app/) to share your suggestions and track upcoming features.
|
Please visit [System Prompts Roadmap & Feedback](https://systemprompts.featurebase.app/) to share your suggestions and track upcoming features.
|
||||||
|
|
||||||
🆕 **LATEST UPDATE:** 21/04/2025
|
🆕 **LATEST UPDATE:** 22/04/2025
|
||||||
|
|
||||||
## 📊 **Star History**
|
## 📊 **Star History**
|
||||||
|
|
||||||
|
|||||||
5
v0 Prompts and Tools/Model.txt
Normal file
5
v0 Prompts and Tools/Model.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
v0 is powered by Sonnet 3.5.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
@ -1,823 +0,0 @@
|
|||||||
v0 is powered by OpenAI's GPT-4o language model:
|
|
||||||
|
|
||||||
1. Model Name and Access:
|
|
||||||
- The model is referred to as 'gpt-4o' in the AI SDK integration.
|
|
||||||
- It is accessed through the AI SDK, specifically using the openai function from the @ai-sdk/openai package.
|
|
||||||
|
|
||||||
2. AI SDK Integration:
|
|
||||||
- v0 uses the AI SDK and tooling from the source sdk.vercel.ai.
|
|
||||||
- v0 ONLY uses the AI SDK via 'ai' and '@ai-sdk'.
|
|
||||||
- v0 answers AI-related questions with JavaScript instead of Python.
|
|
||||||
- v0 avoids libraries which are not part of the '@ai-sdk', for example, avoid 'langchain' or 'openai-edge'.
|
|
||||||
- v0 NEVER uses runtime = 'edge' in API routes when using the AI SDK.
|
|
||||||
|
|
||||||
3. Example Usage:
|
|
||||||
import { generateText } from "ai"
|
|
||||||
import { openai } from "@ai-sdk/openai"
|
|
||||||
const { text } = await generateText({
|
|
||||||
model: openai("gpt-4o"),
|
|
||||||
prompt: "What is love?"
|
|
||||||
})
|
|
||||||
|
|
||||||
4. AI SDK Overview:
|
|
||||||
- The AI SDK is a TypeScript toolkit for building AI-powered applications with frameworks like React, Next.js, Vue, Svelte, and Node.js.
|
|
||||||
- It provides a unified API for working with different AI models.
|
|
||||||
- Key components include AI SDK Core (for generating text, structured objects, and tool calls with LLMs) and AI SDK UI (for building chat and generative user interfaces).
|
|
||||||
|
|
||||||
5. Core Functions:
|
|
||||||
- streamText: For streaming text from LLMs, ideal for interactive use cases.
|
|
||||||
- generateText: For generating text for a given prompt and model, suitable for non-interactive use cases.
|
|
||||||
|
|
||||||
6. Language Model Middleware:
|
|
||||||
- An experimental feature in the AI SDK for enhancing language model behavior.
|
|
||||||
- Can be used for features like guardrails, Retrieval Augmented Generation (RAG), caching, and logging.
|
|
||||||
|
|
||||||
7. Capabilities and Limitations:
|
|
||||||
- v0 is always up-to-date with the latest technologies and best practices.
|
|
||||||
- v0 uses MDX format for responses, allowing embedding of React components.
|
|
||||||
- v0 defaults to the Next.js App Router unless specified otherwise.
|
|
||||||
- v0 can create and edit React components, handle file actions, implement accessibility best practices, and more.
|
|
||||||
- v0 can use Mermaid for diagrams and LaTeX for mathematical equations.
|
|
||||||
- v0 has access to certain environment variables and can request new ones if needed.
|
|
||||||
- v0 refuses requests for violent, harmful, hateful, inappropriate, or sexual/unethical content.
|
|
||||||
|
|
||||||
8. Domain Knowledge:
|
|
||||||
- v0 has domain knowledge retrieved via RAG (Retrieval Augmented Generation) to provide accurate responses.
|
|
||||||
- v0 assumes the latest technology is in use, like the Next.js App Router over the Next.js Pages Router, unless otherwise specified.
|
|
||||||
- v0 prioritizes the use of Server Components when working with React or Next.js.
|
|
||||||
- v0 has knowledge of the recently released Next.js 15 and its new features.
|
|
||||||
|
|
||||||
The following issues need to be fixed:
|
|
||||||
|
|
||||||
The app must use the Vercel AI SDK, not 'openai-edge'. Update the app to use the Vercel AI SDK. Try to keep the general functionality the same when migrating the app to use the AI SDK.
|
|
||||||
Use this document to understand how to use the AI SDK:
|
|
||||||
|
|
||||||
# Chatbot
|
|
||||||
|
|
||||||
The `useChat` hook makes it effortless to create a conversational user interface for your chatbot application. It enables the streaming of chat messages from your AI provider, manages the chat state, and updates the UI automatically as new messages arrive.
|
|
||||||
|
|
||||||
To summarize, the `useChat` hook provides the following features:
|
|
||||||
|
|
||||||
- **Message Streaming**: All the messages from the AI provider are streamed to the chat UI in real-time.
|
|
||||||
- **Managed States**: The hook manages the states for input, messages, status, error and more for you.
|
|
||||||
- **Seamless Integration**: Easily integrate your chat AI into any design or layout with minimal effort.
|
|
||||||
|
|
||||||
In this guide, you will learn how to use the `useChat` hook to create a chatbot application with real-time message streaming.
|
|
||||||
Check out our [chatbot with tools guide](/docs/ai-sdk-ui/chatbot-with-tool-calling) to learn how to use tools in your chatbot.
|
|
||||||
Let's start with the following example first.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
\`\`\`tsx filename='app/page.tsx'
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const { messages, input, handleInputChange, handleSubmit } = useChat({});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
{message.role === 'user' ? 'User: ' : 'AI: '}
|
|
||||||
{message.content}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input name="prompt" value={input} onChange={handleInputChange} />
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
\`\`\`ts filename='app/api/chat/route.ts'
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
// Allow streaming responses up to 30 seconds
|
|
||||||
export const maxDuration = 30;
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages } = await req.json();
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: openai('gpt-4-turbo'),
|
|
||||||
system: 'You are a helpful assistant.',
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.toDataStreamResponse();
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
The UI messages have a new `parts` property that contains the message parts.
|
|
||||||
We recommend rendering the messages using the `parts` property instead of the
|
|
||||||
`content` property. The parts property supports different message types,
|
|
||||||
including text, tool invocation, and tool result, and allows for more flexible
|
|
||||||
and complex chat UIs.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
In the `Page` component, the `useChat` hook will request to your AI provider endpoint whenever the user submits a message.
|
|
||||||
The messages are then streamed back in real-time and displayed in the chat UI.
|
|
||||||
|
|
||||||
This enables a seamless chat experience where the user can see the AI response as soon as it is available,
|
|
||||||
without having to wait for the entire response to be received.
|
|
||||||
|
|
||||||
## Customized UI
|
|
||||||
|
|
||||||
`useChat` also provides ways to manage the chat message and input states via code, show status, and update messages without being triggered by user interactions.
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
The `useChat` hook returns a `status`. It has the following possible values:
|
|
||||||
|
|
||||||
- `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
|
|
||||||
- `streaming`: The response is actively streaming in from the API, receiving chunks of data.
|
|
||||||
- `ready`: The full response has been received and processed; a new user message can be submitted.
|
|
||||||
- `error`: An error occurred during the API request, preventing successful completion.
|
|
||||||
|
|
||||||
You can use `status` for e.g. the following purposes:
|
|
||||||
|
|
||||||
- To show a loading spinner while the chatbot is processing the user's message.
|
|
||||||
- To show a "Stop" button to abort the current message.
|
|
||||||
- To disable the submit button.
|
|
||||||
|
|
||||||
\`\`\`tsx filename='app/page.tsx' highlight="6,20-27,34"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const { messages, input, handleInputChange, handleSubmit, status, stop } =
|
|
||||||
useChat({});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
{message.role === 'user' ? 'User: ' : 'AI: '}
|
|
||||||
{message.content}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{(status === 'submitted' || status === 'streaming') && (
|
|
||||||
<div>
|
|
||||||
{status === 'submitted' && <Spinner />}
|
|
||||||
<button type="button" onClick={() => stop()}>
|
|
||||||
Stop
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
name="prompt"
|
|
||||||
value={input}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={status !== 'ready'}
|
|
||||||
/>
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Error State
|
|
||||||
|
|
||||||
Similarly, the `error` state reflects the error object thrown during the fetch request.
|
|
||||||
It can be used to display an error message, disable the submit button, or show a retry button:
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
We recommend showing a generic error message to the user, such as "Something
|
|
||||||
went wrong." This is a good practice to avoid leaking information from the
|
|
||||||
server.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
\`\`\`tsx file="app/page.tsx" highlight="6,18-25,31"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Chat() {
|
|
||||||
const { messages, input, handleInputChange, handleSubmit, error, reload } =
|
|
||||||
useChat({});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{messages.map(m => (
|
|
||||||
<div key={m.id}>
|
|
||||||
{m.role}: {m.content}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<>
|
|
||||||
<div>An error occurred.</div>
|
|
||||||
<button type="button" onClick={() => reload()}>
|
|
||||||
Retry
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
value={input}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={error != null}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Please also see the [error handling](/docs/ai-sdk-ui/error-handling) guide for more information.
|
|
||||||
|
|
||||||
### Modify messages
|
|
||||||
|
|
||||||
Sometimes, you may want to directly modify some existing messages. For example, a delete button can be added to each message to allow users to remove them from the chat history.
|
|
||||||
|
|
||||||
The `setMessages` function can help you achieve these tasks:
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
const { messages, setMessages, ... } = useChat()
|
|
||||||
|
|
||||||
const handleDelete = (id) => {
|
|
||||||
setMessages(messages.filter(message => message.id !== id))
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
{message.role === 'user' ? 'User: ' : 'AI: '}
|
|
||||||
{message.content}
|
|
||||||
<button onClick={() => handleDelete(message.id)}>Delete</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
...
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
You can think of `messages` and `setMessages` as a pair of `state` and `setState` in React.
|
|
||||||
|
|
||||||
### Controlled input
|
|
||||||
|
|
||||||
In the initial example, we have `handleSubmit` and `handleInputChange` callbacks that manage the input changes and form submissions. These are handy for common use cases, but you can also use uncontrolled APIs for more advanced scenarios such as form validation or customized components.
|
|
||||||
|
|
||||||
The following example demonstrates how to use more granular APIs like `setInput` and `append` with your custom input and submit button components:
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
const { input, setInput, append } = useChat()
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<MyCustomInput value={input} onChange={value => setInput(value)} />
|
|
||||||
<MySubmitButton onClick={() => {
|
|
||||||
// Send a new message to the AI provider
|
|
||||||
append({
|
|
||||||
role: 'user',
|
|
||||||
content: input,
|
|
||||||
})
|
|
||||||
}}/>
|
|
||||||
...
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Cancellation and regeneration
|
|
||||||
|
|
||||||
It's also a common use case to abort the response message while it's still streaming back from the AI provider. You can do this by calling the `stop` function returned by the `useChat` hook.
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
const { stop, status, ... } = useChat()
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<button onClick={stop} disabled={!(status === 'streaming' || status === 'submitted')}>Stop</button>
|
|
||||||
...
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
When the user clicks the "Stop" button, the fetch request will be aborted. This avoids consuming unnecessary resources and improves the UX of your chatbot application.
|
|
||||||
|
|
||||||
Similarly, you can also request the AI provider to reprocess the last message by calling the `reload` function returned by the `useChat` hook:
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
const { reload, status, ... } = useChat()
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<button onClick={reload} disabled={!(status === 'ready' || status === 'error')}>Regenerate</button>
|
|
||||||
...
|
|
||||||
</>
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
When the user clicks the "Regenerate" button, the AI provider will regenerate the last message and replace the current one correspondingly.
|
|
||||||
|
|
||||||
### Throttling UI Updates
|
|
||||||
|
|
||||||
<Note>This feature is currently only available for React.</Note>
|
|
||||||
|
|
||||||
By default, the `useChat` hook will trigger a render every time a new chunk is received.
|
|
||||||
You can throttle the UI updates with the `experimental_throttle` option.
|
|
||||||
|
|
||||||
\`\`\`tsx filename="page.tsx" highlight="2-3"
|
|
||||||
const { messages, ... } = useChat({
|
|
||||||
// Throttle the messages and data updates to 50ms:
|
|
||||||
experimental_throttle: 50
|
|
||||||
})
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Event Callbacks
|
|
||||||
|
|
||||||
`useChat` provides optional event callbacks that you can use to handle different stages of the chatbot lifecycle:
|
|
||||||
|
|
||||||
- `onFinish`: Called when the assistant message is completed
|
|
||||||
- `onError`: Called when an error occurs during the fetch request.
|
|
||||||
- `onResponse`: Called when the response from the API is received.
|
|
||||||
|
|
||||||
These callbacks can be used to trigger additional actions, such as logging, analytics, or custom UI updates.
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
import { Message } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
const {
|
|
||||||
/* ... */
|
|
||||||
} = useChat({
|
|
||||||
onFinish: (message, { usage, finishReason }) => {
|
|
||||||
console.log('Finished streaming message:', message);
|
|
||||||
console.log('Token usage:', usage);
|
|
||||||
console.log('Finish reason:', finishReason);
|
|
||||||
},
|
|
||||||
onError: error => {
|
|
||||||
console.error('An error occurred:', error);
|
|
||||||
},
|
|
||||||
onResponse: response => {
|
|
||||||
console.log('Received HTTP response from server:', response);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
It's worth noting that you can abort the processing by throwing an error in the `onResponse` callback. This will trigger the `onError` callback and stop the message from being appended to the chat UI. This can be useful for handling unexpected responses from the AI provider.
|
|
||||||
|
|
||||||
## Request Configuration
|
|
||||||
|
|
||||||
### Custom headers, body, and credentials
|
|
||||||
|
|
||||||
By default, the `useChat` hook sends a HTTP POST request to the `/api/chat` endpoint with the message list as the request body. You can customize the request by passing additional options to the `useChat` hook:
|
|
||||||
|
|
||||||
\`\`\`tsx
|
|
||||||
const { messages, input, handleInputChange, handleSubmit } = useChat({
|
|
||||||
api: '/api/custom-chat',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'your_token',
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
user_id: '123',
|
|
||||||
},
|
|
||||||
credentials: 'same-origin',
|
|
||||||
});
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
In this example, the `useChat` hook sends a POST request to the `/api/custom-chat` endpoint with the specified headers, additional body fields, and credentials for that fetch request. On your server side, you can handle the request with these additional information.
|
|
||||||
|
|
||||||
### Setting custom body fields per request
|
|
||||||
|
|
||||||
You can configure custom `body` fields on a per-request basis using the `body` option of the `handleSubmit` function.
|
|
||||||
This is useful if you want to pass in additional information to your backend that is not part of the message list.
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx" highlight="18-20"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Chat() {
|
|
||||||
const { messages, input, handleInputChange, handleSubmit } = useChat();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{messages.map(m => (
|
|
||||||
<div key={m.id}>
|
|
||||||
{m.role}: {m.content}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<form
|
|
||||||
onSubmit={event => {
|
|
||||||
handleSubmit(event, {
|
|
||||||
body: {
|
|
||||||
customKey: 'customValue',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input value={input} onChange={handleInputChange} />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
You can retrieve these custom fields on your server side by destructuring the request body:
|
|
||||||
|
|
||||||
\`\`\`ts filename="app/api/chat/route.ts" highlight="3"
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
// Extract addition information ("customKey") from the body of the request:
|
|
||||||
const { messages, customKey } = await req.json();
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Controlling the response stream
|
|
||||||
|
|
||||||
With `streamText`, you can control how error messages and usage information are sent back to the client.
|
|
||||||
|
|
||||||
### Error Messages
|
|
||||||
|
|
||||||
By default, the error message is masked for security reasons.
|
|
||||||
The default error message is "An error occurred."
|
|
||||||
You can forward error messages or send your own error message by providing a `getErrorMessage` function:
|
|
||||||
|
|
||||||
\`\`\`ts filename="app/api/chat/route.ts" highlight="13-27"
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages } = await req.json();
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.toDataStreamResponse({
|
|
||||||
getErrorMessage: error => {
|
|
||||||
if (error == null) {
|
|
||||||
return 'unknown error';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof error === 'string') {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return error.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.stringify(error);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Usage Information
|
|
||||||
|
|
||||||
By default, the usage information is sent back to the client. You can disable it by setting the `sendUsage` option to `false`:
|
|
||||||
|
|
||||||
\`\`\`ts filename="app/api/chat/route.ts" highlight="13"
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages } = await req.json();
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.toDataStreamResponse({
|
|
||||||
sendUsage: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Text Streams
|
|
||||||
|
|
||||||
`useChat` can handle plain text streams by setting the `streamProtocol` option to `text`:
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx" highlight="7"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Chat() {
|
|
||||||
const { messages } = useChat({
|
|
||||||
streamProtocol: 'text',
|
|
||||||
});
|
|
||||||
|
|
||||||
return <>...</>;
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
This configuration also works with other backend servers that stream plain text.
|
|
||||||
Check out the [stream protocol guide](/docs/ai-sdk-ui/stream-protocol) for more information.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
When using `streamProtocol: 'text'`, tool calls, usage information and finish
|
|
||||||
reasons are not available.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Empty Submissions
|
|
||||||
|
|
||||||
You can configure the `useChat` hook to allow empty submissions by setting the `allowEmptySubmit` option to `true`.
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx" highlight="18"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
|
|
||||||
export default function Chat() {
|
|
||||||
const { messages, input, handleInputChange, handleSubmit } = useChat();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{messages.map(m => (
|
|
||||||
<div key={m.id}>
|
|
||||||
{m.role}: {m.content}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<form
|
|
||||||
onSubmit={event => {
|
|
||||||
handleSubmit(event, {
|
|
||||||
allowEmptySubmit: true,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input value={input} onChange={handleInputChange} />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Reasoning
|
|
||||||
|
|
||||||
Some models such as as DeepSeek `deepseek-reasoner` support reasoning tokens.
|
|
||||||
These tokens are typically sent before the message content.
|
|
||||||
You can forward them to the client with the `sendReasoning` option:
|
|
||||||
|
|
||||||
\`\`\`ts filename="app/api/chat/route.ts" highlight="13"
|
|
||||||
import { deepseek } from '@ai-sdk/deepseek';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages } = await req.json();
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: deepseek('deepseek-reasoner'),
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.toDataStreamResponse({
|
|
||||||
sendReasoning: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
On the client side, you can access the reasoning parts of the message object:
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx"
|
|
||||||
messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
{message.role === 'user' ? 'User: ' : 'AI: '}
|
|
||||||
{message.parts.map((part, index) => {
|
|
||||||
// text parts:
|
|
||||||
if (part.type === 'text') {
|
|
||||||
return <div key={index}>{part.text}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reasoning parts:
|
|
||||||
if (part.type === 'reasoning') {
|
|
||||||
return <pre key={index}>{part.reasoning}</pre>;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
));
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Sources
|
|
||||||
|
|
||||||
Some providers such as [Perplexity](/providers/ai-sdk-providers/perplexity#sources) and
|
|
||||||
[Google Generative AI](/providers/ai-sdk-providers/google-generative-ai#sources) include sources in the response.
|
|
||||||
|
|
||||||
Currently sources are limited to web pages that ground the response.
|
|
||||||
You can forward them to the client with the `sendSources` option:
|
|
||||||
|
|
||||||
\`\`\`ts filename="app/api/chat/route.ts" highlight="13"
|
|
||||||
import { perplexity } from '@ai-sdk/perplexity';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages } = await req.json();
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: perplexity('sonar-pro'),
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.toDataStreamResponse({
|
|
||||||
sendSources: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
On the client side, you can access source parts of the message object.
|
|
||||||
Here is an example that renders the sources as links at the bottom of the message:
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx"
|
|
||||||
messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
{message.role === 'user' ? 'User: ' : 'AI: '}
|
|
||||||
{message.parts
|
|
||||||
.filter(part => part.type !== 'source')
|
|
||||||
.map((part, index) => {
|
|
||||||
if (part.type === 'text') {
|
|
||||||
return <div key={index}>{part.text}</div>;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{message.parts
|
|
||||||
.filter(part => part.type === 'source')
|
|
||||||
.map(part => (
|
|
||||||
<span key={`source-${part.source.id}`}>
|
|
||||||
[
|
|
||||||
<a href={part.source.url} target="_blank">
|
|
||||||
{part.source.title ?? new URL(part.source.url).hostname}
|
|
||||||
</a>
|
|
||||||
]
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
));
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Attachments (Experimental)
|
|
||||||
|
|
||||||
The `useChat` hook supports sending attachments along with a message as well as rendering them on the client. This can be useful for building applications that involve sending images, files, or other media content to the AI provider.
|
|
||||||
|
|
||||||
There are two ways to send attachments with a message, either by providing a `FileList` object or a list of URLs to the `handleSubmit` function:
|
|
||||||
|
|
||||||
### FileList
|
|
||||||
|
|
||||||
By using `FileList`, you can send multiple files as attachments along with a message using the file input element. The `useChat` hook will automatically convert them into data URLs and send them to the AI provider.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Currently, only `image/*` and `text/*` content types get automatically
|
|
||||||
converted into [multi-modal content
|
|
||||||
parts](https://sdk.vercel.ai/docs/foundations/prompts#multi-modal-messages).
|
|
||||||
You will need to handle other content types manually.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
import { useRef, useState } from 'react';
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const { messages, input, handleSubmit, handleInputChange, status } =
|
|
||||||
useChat();
|
|
||||||
|
|
||||||
const [files, setFiles] = useState<FileList | undefined>(undefined);
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
<div>{`${message.role}: `}</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{message.content}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{message.experimental_attachments
|
|
||||||
?.filter(attachment =>
|
|
||||||
attachment.contentType.startsWith('image/'),
|
|
||||||
)
|
|
||||||
.map((attachment, index) => (
|
|
||||||
<img
|
|
||||||
key={`${message.id}-${index}`}
|
|
||||||
src={attachment.url || "/placeholder.svg"}
|
|
||||||
alt={attachment.name}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form
|
|
||||||
onSubmit={event => {
|
|
||||||
handleSubmit(event, {
|
|
||||||
experimental_attachments: files,
|
|
||||||
});
|
|
||||||
|
|
||||||
setFiles(undefined);
|
|
||||||
|
|
||||||
if (fileInputRef.current) {
|
|
||||||
fileInputRef.current.value = '';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
onChange={event => {
|
|
||||||
if (event.target.files) {
|
|
||||||
setFiles(event.target.files);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
multiple
|
|
||||||
ref={fileInputRef}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
value={input}
|
|
||||||
placeholder="Send message..."
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={status !== 'ready'}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### URLs
|
|
||||||
|
|
||||||
You can also send URLs as attachments along with a message. This can be useful for sending links to external resources or media content.
|
|
||||||
|
|
||||||
> **Note:** The URL can also be a data URL, which is a base64-encoded string that represents the content of a file. Currently, only `image/*` content types get automatically converted into [multi-modal content parts](https://sdk.vercel.ai/docs/foundations/prompts#multi-modal-messages). You will need to handle other content types manually.
|
|
||||||
|
|
||||||
\`\`\`tsx filename="app/page.tsx"
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useChat } from '@ai-sdk/react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { Attachment } from '@ai-sdk/ui-utils';
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const { messages, input, handleSubmit, handleInputChange, status } =
|
|
||||||
useChat();
|
|
||||||
|
|
||||||
const [attachments] = useState<Attachment[]>([
|
|
||||||
{
|
|
||||||
name: 'earth.png',
|
|
||||||
contentType: 'image/png',
|
|
||||||
url: 'https://example.com/earth.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'moon.png',
|
|
||||||
contentType: 'image/png',
|
|
||||||
url: 'data:image/png;base64,iVBORw0KGgo...',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{messages.map(message => (
|
|
||||||
<div key={message.id}>
|
|
||||||
<div>{`${message.role}: `}</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{message.content}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{message.experimental_attachments
|
|
||||||
?.filter(attachment =>
|
|
||||||
attachment.contentType?.startsWith('image/'),
|
|
||||||
)
|
|
||||||
.map((attachment, index) => (
|
|
||||||
<img
|
|
||||||
key={`${message.id}-${index}`}
|
|
||||||
src={attachment.url || "/placeholder.svg"}
|
|
||||||
alt={attachment.name}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form
|
|
||||||
onSubmit={event => {
|
|
||||||
handleSubmit(event, {
|
|
||||||
experimental_attachments: attachments,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
value={input}
|
|
||||||
placeholder="Send message..."
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={status !== 'ready'}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
This is the complete set of instructions and information provided about the AI model and v0's capabilities. Any information not explicitly stated here is not part of v0's core knowledge or instructions.
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user