├── .DS_Store ├── .gitignore ├── .vscode └── launch.json ├── README.md ├── arggenerator ├── args.html └── jquery-3.5.1.min.js ├── client └── client.go ├── go.mod ├── go.sum ├── ikatagosdk ├── .DS_Store ├── build.sh └── ikatago.go ├── katassh ├── reader.go └── ssh.go ├── main ├── main.go ├── model └── model.go ├── package.sh ├── platform └── platform.go └── utils ├── consts.go └── utils.go /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kinfkong/ikatago-client/a2d978455d813e31113bf9370eb04c2268710eba/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /bin.bak 3 | /run.sh -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}/main.go", 13 | "env": {}, 14 | "args": ["--platform", "aistudio", "--username", "kinfkong", "--password", "12345678"] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ikatago-client 2 | 这是连接ikatago-server的客户端。 3 | 4 | 暂时没时间写文档,有问题加群: 703409387 5 | 6 | ## 下载ikagato客户端 7 | 8 | * [Windows 64bit版本下载](https://github.com/kinfkong/ikatago-client/releases/download/v1.6.0/ikatago-1.6.0-win64.zip) 9 | * [Linux版本下载](https://github.com/kinfkong/ikatago-client/releases/download/v1.6.0/ikatago-1.6.0-linux.zip) 10 | * [Mac OSX版本下载](https://github.com/kinfkong/ikatago-client/releases/download/v1.6.0/ikatago-1.6.0-mac-osx.zip) 11 | * [Windows 32bit版本下载](https://github.com/kinfkong/ikatago-client/releases/download/v1.6.0/ikatago-1.6.0-win32.zip) (不要下载这个,除非你真的系统是32bit) 12 | 13 | ## 用法 14 | 15 | ### Lizzie用法 16 | ``` 17 | --platform aistudio --username <你设置的用户名> --password <你设置的密码> 18 | ``` 19 | 比如,Windows下可能是这样子: 20 | ``` 21 | C:\xxx\ikatago.exe --platform aistudio --username kinfkong --password ****** 22 | ``` 23 | 24 | ### Sabaki的用法 25 | 有三行, 26 | 第一行: 引擎名字,随便起一个名字 27 | 第二行: 程序路径,就是ikatago在你本机的路径,比如, C:\xxx\ikatago.exe 28 | 第三行: 运行参数: --platform aistudio --username <你设置的用户名> --password <你设置的密码> 29 | 30 | ### 更多参数 31 | 32 | ### 4. 如何指定katago的运行版本? 33 | 可以通过ikatago客户端参数`--kata-name`来指定, 34 | 比如: 35 | ``` 36 | ikatago.exe --kata-name katago-TENSORRT --username xxxx ... 37 | ``` 38 | 39 | ### 5. 如何更改权重? 40 | 41 | 在ikatago.exe里,添加参数`--kata-weight`,比如: 42 | ``` 43 | ikatago.exe --kata-weight 20b --username xxxx ... 44 | ``` 45 | 46 | 47 | ### 6. 如何修改katago配置文件? 48 | 你可以通过ikatago客户端,通过`--kata-local-config`来指定你自己本机上的配置文件,比如: 49 | 50 | ``` 51 | ikatago.exe --kata-local-config C:\xxx.cfg --username xxx ... 52 | ``` 53 | 54 | 55 | -------------------------------------------------------------------------------- /arggenerator/args.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ikatago参数生成器 6 | 7 | 8 | 71 | 91 | 92 | 93 | 94 | 95 |

ikatago参数生成器

96 |
97 | 只要把带*的填好就行,其它的如果没需要,可以不管呢(留空就行)。 98 |
99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 120 | 121 | 122 | 123 | 124 | 125 |
126 |
127 | 128 | 129 | 132 | 133 | 168 | 169 | -------------------------------------------------------------------------------- /arggenerator/jquery-3.5.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 0 { 190 | cmd = cmd + serverLocationOptions 191 | } 192 | 193 | stdinReader, mockWriter := io.Pipe() 194 | mockReader, stderrWriter := io.Pipe() 195 | defer mockWriter.Close() 196 | defer stdinReader.Close() 197 | defer mockReader.Close() 198 | defer stderrWriter.Close() 199 | err := (&katassh.KataSSHSession{}).RunSSH(client.sshOptions, cmd, stdinReader, stderrWriter, outputWriter) 200 | if err != nil { 201 | return err 202 | } 203 | return nil 204 | } 205 | 206 | func (client *Client) BuildKatagoCommand(cmd string, options RunKatagoOptions, subCommands []string) string { 207 | kataName := options.KataName 208 | kataWeight := options.KataWeight 209 | kataConfig := options.KataConfig 210 | kataLocalConfig := options.KataLocalConfig 211 | kataOverrideConfig := options.KataOverrideConfig 212 | extraInfo := options.ExtraInfo 213 | clientID := options.ClientID 214 | if kataName != nil && len(*kataName) > 0 { 215 | cmd = cmd + fmt.Sprintf(" --name %s", *kataName) 216 | } 217 | if kataWeight != nil && len(*kataWeight) > 0 { 218 | cmd = cmd + fmt.Sprintf(" --weight %s", *kataWeight) 219 | } 220 | if kataConfig != nil && len(*kataConfig) > 0 { 221 | cmd = cmd + fmt.Sprintf(" --config %s", *kataConfig) 222 | } 223 | if kataLocalConfig != nil && len(*kataLocalConfig) > 0 { 224 | cmd = cmd + fmt.Sprintf(" --custom-config %s", filepath.Base(*kataLocalConfig)) 225 | } 226 | if extraInfo != nil && len(*extraInfo) > 0 { 227 | cmd = cmd + fmt.Sprintf(" --extra-info %s", *extraInfo) 228 | } 229 | if clientID != nil && len(*clientID) > 0 { 230 | cmd = cmd + fmt.Sprintf(" --client-id %s", *clientID) 231 | } 232 | if !options.NoCompress { 233 | cmd = cmd + " --compress" 234 | } 235 | cmd = cmd + fmt.Sprintf(" --refresh-interval %d", options.RefreshInterval) 236 | cmd = cmd + fmt.Sprintf(" --transmit-move-num %d", options.TransmitMoveNum) 237 | 238 | // build options with server related options 239 | serverLocationOptions := client.BuildServerLocationOptions() 240 | if len(serverLocationOptions) > 0 { 241 | cmd = cmd + serverLocationOptions 242 | } 243 | if len(subCommands) > 0 { 244 | cmd = cmd + " -- " + strings.Join(subCommands, " ") 245 | if kataOverrideConfig != nil && len(*kataOverrideConfig) > 0 { 246 | cmd = cmd + " -override-config " + *kataOverrideConfig 247 | } 248 | } else if kataOverrideConfig != nil && len(*kataOverrideConfig) > 0 { 249 | cmd = cmd + " -- gtp -override-config " + *kataOverrideConfig 250 | } 251 | return cmd 252 | } 253 | 254 | func (client *Client) BuildServerLocationOptions() string { 255 | cmd := "" 256 | // build options with server related options 257 | if client.Options.EngineType != nil && len(*client.Options.EngineType) > 0 { 258 | cmd = cmd + fmt.Sprintf(" --engine-type %s", *client.Options.EngineType) 259 | } 260 | if client.Options.ForceNode != nil && len(*client.Options.ForceNode) > 0 { 261 | cmd = cmd + fmt.Sprintf(" --force-node %s", *client.Options.ForceNode) 262 | } 263 | if client.Options.GpuType != nil && len(*client.Options.GpuType) > 0 { 264 | cmd = cmd + fmt.Sprintf(" --gpu-type %s", *client.Options.GpuType) 265 | } 266 | if client.Options.Token != nil && len(*client.Options.Token) > 0 { 267 | cmd = cmd + fmt.Sprintf(" --token %s", *client.Options.Token) 268 | } 269 | return cmd 270 | } 271 | func (client *Client) initClient() error { 272 | platform, err := client.getPlatformFromWorld() 273 | if err != nil { 274 | return err 275 | } 276 | sshOptions, err := client.getSSHOptions(platform) 277 | if err != nil { 278 | return err 279 | } 280 | client.sshOptions = *sshOptions 281 | client.init = true 282 | return nil 283 | } 284 | 285 | func (client *Client) getPlatformFromWorld() (*platform.Platform, error) { 286 | type World struct { 287 | Platforms []platform.Platform `json:"platforms"` 288 | } 289 | worldJSONString, err := utils.DoHTTPRequest("GET", client.Options.World, nil, nil) 290 | if err != nil { 291 | return nil, err 292 | } 293 | world := &World{} 294 | err = json.Unmarshal([]byte(worldJSONString), &world) 295 | if err != nil { 296 | return nil, err 297 | } 298 | for _, platform := range world.Platforms { 299 | if platform.Name == client.Options.Platform { 300 | return &platform, nil 301 | } 302 | } 303 | log.Printf("ERROR platform not found in the world. platform: %s", client.Options.Platform) 304 | return nil, errors.New("platform_not_found") 305 | } 306 | 307 | // getSSHOptions gets the ssh info 308 | func (client *Client) getSSHOptions(p *platform.Platform) (*model.SSHOptions, error) { 309 | sshJSONURL := "" 310 | if p.Http != nil && p.Http.GetUrl != nil { 311 | sshJSONURL = *p.Http.GetUrl + "/users/" + client.Options.Username + ".ssh.json" 312 | } else { 313 | sshJSONURL = "https://" + p.Oss.Bucket + "." + p.Oss.BucketEndpoint + "/users/" + client.Options.Username + ".ssh.json" 314 | } 315 | response, err := utils.DoHTTPRequest("GET", sshJSONURL, nil, nil) 316 | if err != nil { 317 | log.Printf("ERROR error requestting url: %s, err: %+v\n", sshJSONURL, err) 318 | return nil, err 319 | } 320 | sshoptions := model.SSHOptions{} 321 | // parse json 322 | err = json.Unmarshal([]byte(response), &sshoptions) 323 | if err != nil { 324 | log.Printf("ERROR failed parsing json: %s\n", response) 325 | return nil, err 326 | } 327 | sshoptions.Password = client.Options.Password 328 | return &sshoptions, nil 329 | } 330 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kinfkong/ikatago-client 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/jessevdk/go-flags v1.4.0 7 | golang.org/x/crypto v0.13.0 8 | moul.io/http2curl/v2 v2.3.0 9 | ) 10 | 11 | require golang.org/x/sys v0.12.0 // indirect 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= 3 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 4 | github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 9 | github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= 10 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 12 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 13 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 14 | golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= 15 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 16 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 17 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 18 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 19 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 20 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 21 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 22 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 23 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 24 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 25 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 27 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 28 | golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= 29 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 30 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 31 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 32 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 33 | golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 34 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 35 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 36 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 37 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 38 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 39 | moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= 40 | moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= 41 | -------------------------------------------------------------------------------- /ikatagosdk/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kinfkong/ikatago-client/a2d978455d813e31113bf9370eb04c2268710eba/ikatagosdk/.DS_Store -------------------------------------------------------------------------------- /ikatagosdk/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gomobile init 3 | gomobile bind --target=ios -o ../../ikatago-ios-sdk/ikatagosdk.xcframework . 4 | rm -rf ~/gochess/react-native-ikatago-sdk/ios/ikatagosdk.xcframework 5 | gcp -RP ../../ikatago-ios-sdk/ikatagosdk.xcframework ~/gochess/react-native-ikatago-sdk/ios 6 | 7 | gomobile bind --target=android -o ../../ikatago-android-sdk/ikatagosdk.aar . 8 | gcp -RP ../../ikatago-android-sdk/ikatagosdk.aar ~/gochess/react-native-ikatago-sdk/android/libs -------------------------------------------------------------------------------- /ikatagosdk/ikatago.go: -------------------------------------------------------------------------------- 1 | package ikatagosdk 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "os" 8 | "strings" 9 | "time" 10 | 11 | "github.com/jessevdk/go-flags" 12 | "github.com/kinfkong/ikatago-client/client" 13 | "github.com/kinfkong/ikatago-client/model" 14 | "github.com/kinfkong/ikatago-client/utils" 15 | ) 16 | 17 | type genericOptions struct { 18 | NoCompress *bool `long:"no-compress" description:"compress the data during transmission"` 19 | RefreshInterval *int `long:"refresh-interval" description:"sets the refresh interval in cent seconds"` 20 | TransmitMoveNum *int `long:"transmit-move-num" description:"limits number of moves when transmission during analyze"` 21 | KataLocalConfig *string `long:"kata-local-config" description:"The katago config file. like, gtp_example.cfg"` 22 | KataOverrideConfig *string `long:"kata-override-config" description:"The katago override-config, like: analysisPVLen=30,numSearchThreads=30"` 23 | KataName *string `long:"kata-name" description:"The katago binary name"` 24 | KataWeight *string `long:"kata-weight" description:"The katago weight name"` 25 | KataConfig *string `long:"kata-config" description:"The katago config name"` 26 | EngineType *string `long:"engine-type" description:"sets the enginetype"` 27 | ForceNode *string `long:"force-node" description:"in cluster, force to a specific node."` 28 | Token *string `long:"token" description:"sets the token"` 29 | GpuType *string `long:"gpu-type" description:"sets the gpu type"` 30 | ExtraInfo *string `long:"extra-info" description:"sets the extra info of the command"` 31 | ClientID *string `long:"client-id" description:"sets the client id"` 32 | Command string `long:"cmd" description:"The command to run the katago" default:"run-katago"` 33 | } 34 | 35 | // Client the client wrapper 36 | type Client struct { 37 | remoteClient *client.Client 38 | extraArgs *string 39 | } 40 | 41 | // DataCallbackFunc Represents the data callback function 42 | type DataCallbackFunc func(content []byte) 43 | 44 | // DataCallback defines the data callback 45 | type DataCallback interface { 46 | Callback(content []byte) 47 | StderrCallback(content []byte) 48 | OnReady() 49 | } 50 | 51 | type dataNotifier struct { 52 | callback DataCallbackFunc 53 | } 54 | 55 | // KatagoRunner represents katago runner 56 | type KatagoRunner struct { 57 | client *Client 58 | noCompress bool 59 | refreshInterval int 60 | transmitMoveNum int 61 | useRawData bool 62 | kataLocalConfig *string 63 | kataOverrideConfig *string 64 | kataName *string 65 | kataWeight *string 66 | kataConfig *string 67 | extraInfo *string 68 | clientID *string 69 | subCommands []string 70 | reader io.Reader 71 | writer io.Writer 72 | stderrWriter io.Writer 73 | commandWriter io.Writer 74 | sessionResult *client.SessionResult 75 | started bool 76 | } 77 | 78 | type ClientRunner struct { 79 | Client *Client 80 | Runner *KatagoRunner 81 | } 82 | 83 | func (notifier *dataNotifier) Write(p []byte) (n int, err error) { 84 | if notifier.callback != nil { 85 | notifier.callback(p) 86 | } 87 | return len(p), nil 88 | } 89 | 90 | func NewClientRunnerFromArgs(argString string) (*ClientRunner, error) { 91 | opts := model.AllOpts{} 92 | subCommands, err := flags.ParseArgs(&opts, strings.Split(argString, " ")) 93 | if err != nil { 94 | return nil, err 95 | } 96 | world := "" 97 | if opts.World != nil { 98 | world = *opts.World 99 | } 100 | client, err := NewClient(world, opts.Platform, opts.Username, opts.Password) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if opts.EngineType != nil { 105 | client.SetEngineType(*opts.EngineType) 106 | } 107 | if opts.GpuType != nil { 108 | client.SetGpuType(*opts.GpuType) 109 | } 110 | if opts.ForceNode != nil { 111 | client.SetForceNode(*opts.ForceNode) 112 | } 113 | if opts.Token != nil { 114 | client.SetToken(*opts.Token) 115 | } 116 | runner, err := client.CreateKatagoRunner() 117 | if err != nil { 118 | return nil, err 119 | } 120 | if len(subCommands) > 0 { 121 | runner.SetSubCommands(strings.Join(subCommands, " ")) 122 | } 123 | if opts.KataWeight != nil { 124 | runner.SetKataWeight(*opts.KataWeight) 125 | } 126 | if opts.KataConfig != nil { 127 | runner.SetKataConfig(*opts.KataConfig) 128 | } 129 | if opts.KataName != nil { 130 | runner.SetKataName(*opts.KataName) 131 | } 132 | if opts.KataOverrideConfig != nil { 133 | runner.SetKataOverrideConfig(*opts.KataOverrideConfig) 134 | } 135 | if opts.ExtraInfo != nil { 136 | runner.SetExtraInfo(*opts.ExtraInfo) 137 | } 138 | if opts.ClientID != nil { 139 | runner.SetClientID(*opts.ClientID) 140 | } 141 | runner.DisableCompress(opts.NoCompress) 142 | runner.SetRefreshInterval(opts.RefreshInterval) 143 | runner.SetTransmitMoveNum(opts.TransmitMoveNum) 144 | if opts.KataLocalConfig != nil { 145 | runner.SetKataLocalConfig(*opts.KataLocalConfig) 146 | } 147 | return &ClientRunner{Client: client, Runner: runner}, nil 148 | } 149 | 150 | // NewClient creates the new mobile client 151 | func NewClient(world string, platform string, username string, password string) (*Client, error) { 152 | defaultWorld := utils.WorldURL 153 | if len(world) == 0 { 154 | world = defaultWorld 155 | } 156 | remoteClient, err := client.NewClient(client.Options{ 157 | World: world, 158 | Platform: platform, 159 | Username: username, 160 | Password: password, 161 | }) 162 | if err != nil { 163 | return nil, err 164 | } 165 | return &Client{ 166 | remoteClient: remoteClient, 167 | }, nil 168 | } 169 | 170 | // SetExtraArgs sets the extra args like "--gpu-type 3x --engine-type katago" 171 | func (client *Client) SetExtraArgs(extraArgs string) { 172 | opts := genericOptions{} 173 | _, err := flags.ParseArgs(&opts, strings.Split(extraArgs, " ")) 174 | if err != nil { 175 | return 176 | } 177 | if opts.EngineType != nil { 178 | client.SetEngineType(*opts.EngineType) 179 | } 180 | if opts.GpuType != nil { 181 | client.SetGpuType(*opts.GpuType) 182 | } 183 | if opts.ForceNode != nil { 184 | client.SetForceNode(*opts.ForceNode) 185 | } 186 | if opts.Token != nil { 187 | client.SetToken(*opts.Token) 188 | } 189 | 190 | client.extraArgs = &extraArgs 191 | } 192 | 193 | // SetToken sets the token 194 | func (client *Client) SetToken(token string) { 195 | client.remoteClient.Options.Token = &token 196 | } 197 | 198 | // SetToken sets the force node 199 | func (client *Client) SetForceNode(forceNode string) { 200 | client.remoteClient.Options.ForceNode = &forceNode 201 | } 202 | 203 | // SetGpuType sets the gpu type 204 | func (client *Client) SetGpuType(gpuType string) { 205 | client.remoteClient.Options.GpuType = &gpuType 206 | } 207 | 208 | // SetEngineType sets the engine type 209 | func (client *Client) SetEngineType(engineType string) { 210 | client.remoteClient.Options.EngineType = &engineType 211 | } 212 | 213 | // QueryServer queries the server info 214 | func (client *Client) QueryServer() (string, error) { 215 | buf := bytes.NewBuffer(nil) 216 | err := client.remoteClient.QueryServer(buf) 217 | if err != nil { 218 | return "", err 219 | } 220 | return buf.String(), nil 221 | } 222 | 223 | // CreateKatagoRunner creates the katago runner 224 | func (client *Client) CreateKatagoRunner() (*KatagoRunner, error) { 225 | runner := &KatagoRunner{ 226 | client: client, 227 | refreshInterval: 30, 228 | transmitMoveNum: 25, 229 | noCompress: false, 230 | useRawData: false, 231 | subCommands: make([]string, 0), 232 | started: false, 233 | } 234 | if client.extraArgs != nil { 235 | opts := genericOptions{} 236 | subCommands, err := flags.ParseArgs(&opts, strings.Split(*client.extraArgs, " ")) 237 | if err == nil { 238 | if len(subCommands) > 0 { 239 | runner.SetSubCommands(strings.Join(subCommands, " ")) 240 | } 241 | if opts.KataWeight != nil { 242 | runner.SetKataWeight(*opts.KataWeight) 243 | } 244 | if opts.KataConfig != nil { 245 | runner.SetKataConfig(*opts.KataConfig) 246 | } 247 | if opts.KataName != nil { 248 | runner.SetKataName(*opts.KataName) 249 | } 250 | if opts.KataOverrideConfig != nil { 251 | runner.SetKataOverrideConfig(*opts.KataOverrideConfig) 252 | } 253 | if opts.NoCompress != nil { 254 | runner.DisableCompress(*opts.NoCompress) 255 | } 256 | if opts.RefreshInterval != nil { 257 | runner.SetRefreshInterval(*opts.RefreshInterval) 258 | } 259 | if opts.TransmitMoveNum != nil { 260 | runner.SetTransmitMoveNum(*opts.TransmitMoveNum) 261 | } 262 | if opts.KataLocalConfig != nil { 263 | runner.SetKataLocalConfig(*opts.KataLocalConfig) 264 | } 265 | if opts.ExtraInfo != nil { 266 | runner.SetExtraInfo(*opts.ExtraInfo) 267 | } 268 | if opts.ClientID != nil { 269 | runner.SetClientID(*opts.ClientID) 270 | } 271 | } 272 | 273 | } 274 | return runner, nil 275 | } 276 | 277 | // Run runs the katago 278 | func (katagoRunner *KatagoRunner) Run(callback DataCallback) error { 279 | katagoRunner.started = true 280 | options := client.RunKatagoOptions{ 281 | NoCompress: katagoRunner.noCompress, 282 | RefreshInterval: katagoRunner.refreshInterval, 283 | TransmitMoveNum: katagoRunner.transmitMoveNum, 284 | KataLocalConfig: katagoRunner.kataLocalConfig, 285 | KataOverrideConfig: katagoRunner.kataOverrideConfig, 286 | KataName: katagoRunner.kataName, 287 | KataWeight: katagoRunner.kataWeight, 288 | KataConfig: katagoRunner.kataConfig, 289 | UseRawData: katagoRunner.useRawData, 290 | ExtraInfo: katagoRunner.extraInfo, 291 | ClientID: katagoRunner.clientID, 292 | } 293 | katagoRunner.writer = &dataNotifier{ 294 | callback: callback.Callback, 295 | } 296 | katagoRunner.stderrWriter = &dataNotifier{ 297 | callback: callback.StderrCallback, 298 | } 299 | pr, pw := io.Pipe() 300 | katagoRunner.commandWriter = pw 301 | katagoRunner.reader = pr 302 | defer pw.Close() 303 | defer pr.Close() 304 | 305 | sessionResult, err := katagoRunner.client.remoteClient.RunKatago(options, katagoRunner.subCommands, katagoRunner.reader, katagoRunner.writer, katagoRunner.stderrWriter, callback.OnReady) 306 | if err != nil { 307 | katagoRunner.started = false 308 | return err 309 | } 310 | katagoRunner.sessionResult = sessionResult 311 | sessionResult.Wait() 312 | katagoRunner.started = false 313 | return nil 314 | } 315 | 316 | func (katagoRunner *KatagoRunner) RunWithStdio(command string) error { 317 | remoteClient := katagoRunner.client.remoteClient 318 | options := client.RunKatagoOptions{ 319 | NoCompress: katagoRunner.noCompress, 320 | RefreshInterval: katagoRunner.refreshInterval, 321 | TransmitMoveNum: katagoRunner.transmitMoveNum, 322 | KataLocalConfig: katagoRunner.kataLocalConfig, 323 | KataOverrideConfig: katagoRunner.kataOverrideConfig, 324 | KataName: katagoRunner.kataName, 325 | KataWeight: katagoRunner.kataWeight, 326 | KataConfig: katagoRunner.kataConfig, 327 | UseRawData: katagoRunner.useRawData, 328 | ExtraInfo: katagoRunner.extraInfo, 329 | ClientID: katagoRunner.clientID, 330 | } 331 | if command == "run-katago" { 332 | sessionResult, err := remoteClient.RunKatago(options, katagoRunner.subCommands, os.Stdin, os.Stdout, os.Stderr, nil) 333 | if err != nil { 334 | return err 335 | } 336 | sessionResult.Wait() 337 | } else if command == "preload-katago" { 338 | sessionResult, err := remoteClient.PreloadKatago(options, katagoRunner.subCommands, os.Stdin, os.Stdout, os.Stderr, nil) 339 | if err != nil { 340 | return err 341 | } 342 | sessionResult.Wait() 343 | } else if command == "query-server" { 344 | // run katago command 345 | err := remoteClient.QueryServer(os.Stdout) 346 | if err != nil { 347 | return err 348 | } 349 | } else if command == "view-config" { 350 | err := remoteClient.ViewConfig(options, katagoRunner.subCommands, os.Stdout) 351 | if err != nil { 352 | return err 353 | } 354 | } else { 355 | return errors.New("unknown command") 356 | } 357 | return nil 358 | } 359 | 360 | // SetKataWeight sets the name of the kata weight 361 | func (katagoRunner *KatagoRunner) SetKataWeight(kataWeight string) { 362 | katagoRunner.kataWeight = &kataWeight 363 | } 364 | 365 | // SetKataConfig sets the name of the kata config name 366 | func (katagoRunner *KatagoRunner) SetKataConfig(kataConfig string) { 367 | katagoRunner.kataConfig = &kataConfig 368 | } 369 | 370 | // SetKataLocalConfig sets the name of the kata local config file 371 | func (katagoRunner *KatagoRunner) SetKataLocalConfig(kataLocalConfig string) { 372 | katagoRunner.kataLocalConfig = &kataLocalConfig 373 | } 374 | 375 | // SetKataOverrideConfig sets the name of the kata override-config option of kata, example: analysisPVLen=30,playoutDoublingAdvantage=3 376 | func (katagoRunner *KatagoRunner) SetKataOverrideConfig(kataOverrideConfig string) { 377 | katagoRunner.kataOverrideConfig = &kataOverrideConfig 378 | } 379 | 380 | // SetKataName sets the name of the katago name 381 | func (katagoRunner *KatagoRunner) SetKataName(kataName string) { 382 | katagoRunner.kataName = &kataName 383 | } 384 | 385 | // DisableCompress disables the compression 386 | func (katagoRunner *KatagoRunner) DisableCompress(noCompress bool) { 387 | katagoRunner.noCompress = noCompress 388 | } 389 | 390 | // SetRefreshInterval sets the refresh interval 391 | func (katagoRunner *KatagoRunner) SetRefreshInterval(refreshInterval int) { 392 | katagoRunner.refreshInterval = refreshInterval 393 | } 394 | 395 | // SetTransmitMoveNum sets the transmit move num 396 | func (katagoRunner *KatagoRunner) SetTransmitMoveNum(transmitMoveNum int) { 397 | katagoRunner.transmitMoveNum = transmitMoveNum 398 | } 399 | 400 | // SetExtraInfo sets the extra info 401 | func (katagoRunner *KatagoRunner) SetExtraInfo(extraInfo string) { 402 | katagoRunner.extraInfo = &extraInfo 403 | } 404 | 405 | // SetClientID sets the client id 406 | func (katagoRunner *KatagoRunner) SetClientID(clientID string) { 407 | katagoRunner.clientID = &clientID 408 | } 409 | 410 | // SendGTPCommand sends the gtp command 411 | func (katagoRunner *KatagoRunner) SendGTPCommand(command string) error { 412 | // gtp command must end with "\n" 413 | if !strings.HasSuffix(command, "\n") { 414 | command = command + "\n" 415 | } 416 | _, err := io.WriteString(katagoRunner.commandWriter, command) 417 | if err != nil { 418 | return err 419 | } 420 | return nil 421 | } 422 | 423 | // SetUseRawData sets if use the raw data or not 424 | func (katagoRunner *KatagoRunner) SetUseRawData(useRawData bool) { 425 | katagoRunner.useRawData = useRawData 426 | } 427 | 428 | // SetSubCommands sets the subcommands. for example: 'analysis -analysis-threads 12' 429 | func (katagoRunner *KatagoRunner) SetSubCommands(subCommands string) { 430 | katagoRunner.subCommands = strings.Split(subCommands, " ") 431 | } 432 | 433 | // Stop stops the katago engine 434 | func (katagoRunner *KatagoRunner) Stop() error { 435 | c := 0 436 | for katagoRunner.started && katagoRunner.sessionResult == nil { 437 | time.Sleep(100 * time.Millisecond) 438 | c++ 439 | if c >= 200 { 440 | // 20 seconds 441 | break 442 | } 443 | } 444 | if katagoRunner.sessionResult != nil { 445 | katagoRunner.sessionResult.Stop() 446 | katagoRunner.sessionResult = nil 447 | katagoRunner.started = false 448 | } 449 | return nil 450 | } 451 | -------------------------------------------------------------------------------- /katassh/reader.go: -------------------------------------------------------------------------------- 1 | package katassh 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "encoding/binary" 7 | "errors" 8 | "io" 9 | "io/ioutil" 10 | ) 11 | 12 | const ( 13 | // CompressStarterSymbol the symbol starts the compresssion 14 | CompressStarterSymbol = 0xff 15 | ) 16 | 17 | // GTPReader the gtp reader 18 | type GTPReader struct { 19 | reader io.Reader 20 | buffer *bytes.Buffer 21 | lastError error 22 | } 23 | 24 | // NewGTPReader the new gtp reader 25 | func NewGTPReader(reader io.Reader) *GTPReader { 26 | return >PReader{ 27 | reader: reader, 28 | buffer: bytes.NewBuffer(make([]byte, 0)), 29 | lastError: nil, 30 | } 31 | } 32 | 33 | func (r *GTPReader) Read(p []byte) (int, error) { 34 | bufferBytes := r.buffer.Bytes() 35 | if len(bufferBytes) == 0 { 36 | if r.lastError != nil { 37 | return 0, r.lastError 38 | } 39 | r.lastError = r.readAndProcess() 40 | bufferBytes = r.buffer.Bytes() 41 | } 42 | // copy the bytes to p buffer 43 | var n int = 0 44 | for i := 0; i < len(p) && i < len(bufferBytes); i++ { 45 | n++ 46 | p[i] = bufferBytes[i] 47 | } 48 | if n < len(bufferBytes) { 49 | r.buffer = bytes.NewBuffer(bufferBytes[n:]) 50 | } else { 51 | r.buffer = bytes.NewBuffer(make([]byte, 0)) 52 | } 53 | return n, nil 54 | } 55 | 56 | func (r *GTPReader) readAndProcess() error { 57 | var buf []byte = make([]byte, 4096) 58 | n, err := r.reader.Read(buf) 59 | if n == 0 { 60 | return err 61 | } 62 | idx := 0 63 | s := -1 64 | e := -1 65 | for idx < n { 66 | if buf[idx] == CompressStarterSymbol { 67 | if s >= 0 && e >= 0 { 68 | // write the normal buf 69 | r.buffer.Write(buf[s : e+1]) 70 | s = -1 71 | e = -1 72 | } 73 | n, err := r.processCompress(buf[idx:]) 74 | idx += n 75 | if err != nil { 76 | return err 77 | } 78 | } else { 79 | if s < 0 { 80 | s = idx 81 | } 82 | e = idx 83 | idx++ 84 | } 85 | } 86 | if s >= 0 && e >= 0 { 87 | // write the normal buf 88 | r.buffer.Write(buf[s : e+1]) 89 | s = -1 90 | e = -1 91 | } 92 | return err 93 | } 94 | 95 | func (r *GTPReader) processCompress(p []byte) (int, error) { 96 | pIndex := 0 97 | if p[0] != CompressStarterSymbol { 98 | // safe guard 99 | return pIndex, errors.New("not_compress_buffer") 100 | } 101 | pIndex++ 102 | // read compress length 103 | lenBuf := make([]byte, 4) 104 | lenLen := 0 105 | for i := 0; i < 4 && pIndex < len(p); i++ { 106 | lenBuf[i] = p[pIndex] 107 | lenLen++ 108 | pIndex++ 109 | } 110 | 111 | for lenLen < 4 { 112 | n, err := r.reader.Read(lenBuf[lenLen:]) 113 | lenLen += n 114 | if err != nil { 115 | return pIndex, err 116 | } 117 | } 118 | contentLength := int(binary.LittleEndian.Uint32(lenBuf)) 119 | currentContentLength := 0 120 | contentBuf := make([]byte, contentLength) 121 | for i := 0; i < contentLength && pIndex < len(p); i++ { 122 | contentBuf[i] = p[pIndex] 123 | currentContentLength++ 124 | pIndex++ 125 | } 126 | for currentContentLength < contentLength { 127 | n, err := r.reader.Read(contentBuf[currentContentLength:]) 128 | currentContentLength += n 129 | if err != nil { 130 | return pIndex, err 131 | } 132 | } 133 | unzipped := toUnzippedBuffer(contentBuf) 134 | r.buffer.Write(unzipped) 135 | return pIndex, nil 136 | } 137 | 138 | func toUnzippedBuffer(buf []byte) []byte { 139 | // Write gzipped data to the client 140 | gr, err := gzip.NewReader(bytes.NewBuffer(buf)) 141 | defer gr.Close() 142 | data, err := ioutil.ReadAll(gr) 143 | if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { 144 | return nil 145 | } 146 | return data 147 | } 148 | -------------------------------------------------------------------------------- /katassh/ssh.go: -------------------------------------------------------------------------------- 1 | package katassh 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "time" 12 | 13 | "github.com/kinfkong/ikatago-client/model" 14 | "github.com/kinfkong/ikatago-client/utils" 15 | "golang.org/x/crypto/ssh" 16 | ) 17 | 18 | type KataSSHSession struct { 19 | Stopped bool 20 | Session *ssh.Session 21 | } 22 | 23 | // RunSSH runs the ssh command 24 | func (kataSSHSession *KataSSHSession) RunSSH(sshoptions model.SSHOptions, cmd string, stdinReader io.Reader, stderrWriter io.Writer, outputWriter io.Writer) error { 25 | config := &ssh.ClientConfig{ 26 | Timeout: 30 * time.Second, 27 | User: sshoptions.User, 28 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 29 | } 30 | 31 | config.Auth = []ssh.AuthMethod{ssh.Password(sshoptions.Password)} 32 | addr := fmt.Sprintf("%s:%d", sshoptions.Host, sshoptions.Port) 33 | sshClient, err := ssh.Dial("tcp", addr, config) 34 | if err != nil { 35 | return err 36 | } 37 | defer sshClient.Close() 38 | 39 | session, err := sshClient.NewSession() 40 | if err != nil { 41 | return err 42 | } 43 | defer session.Close() 44 | if kataSSHSession.Stopped { 45 | return nil 46 | } 47 | kataSSHSession.Session = session 48 | defer session.Close() 49 | session.Stderr = stderrWriter 50 | session.Stdin = stdinReader 51 | session.Stdout = outputWriter 52 | 53 | //log.Printf("DEBUG running equal commad: ssh -p %d %s@%s %s\n", sshoptions.Port, sshoptions.User, sshoptions.Host, cmd) 54 | 55 | err = session.Run(cmd) 56 | if err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | 62 | // RunSCP runs the scp command 63 | func (kataSSHSession *KataSSHSession) RunSCP(sshoptions model.SSHOptions, localFile string, serverLocationOptions string) error { 64 | // check file existence 65 | if !utils.FileExists(localFile) { 66 | log.Printf("ERROR config file not found: %s\n", localFile) 67 | return errors.New("file_not_found") 68 | } 69 | 70 | basefileName := filepath.Base(localFile) 71 | if !strings.HasSuffix(basefileName, ".cfg") { 72 | log.Printf("ERROR config file name must ends with .cfg") 73 | return errors.New("invalid_file_extension") 74 | } 75 | fileSize, err := utils.GetFileSize(localFile) 76 | if err != nil { 77 | log.Printf("ERROR cannot get file size: %s\n", localFile) 78 | return errors.New("io_error") 79 | } 80 | if fileSize >= 1024*100 { 81 | log.Printf("ERROR config file: %s is too large: %v\n", localFile, fileSize) 82 | return errors.New("file_too_large") 83 | } 84 | config := &ssh.ClientConfig{ 85 | Timeout: 30 * time.Second, 86 | User: sshoptions.User, 87 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 88 | } 89 | 90 | config.Auth = []ssh.AuthMethod{ssh.Password(sshoptions.Password)} 91 | addr := fmt.Sprintf("%s:%d", sshoptions.Host, sshoptions.Port) 92 | sshClient, err := ssh.Dial("tcp", addr, config) 93 | if err != nil { 94 | return err 95 | } 96 | defer sshClient.Close() 97 | 98 | session, err := sshClient.NewSession() 99 | if err != nil { 100 | return err 101 | } 102 | defer session.Close() 103 | if kataSSHSession.Stopped { 104 | return nil 105 | } 106 | kataSSHSession.Session = session 107 | defer session.Close() 108 | session.Stdout = os.Stdout 109 | session.Stderr = os.Stderr 110 | // session.Stdin = os.Stdin 111 | log.Printf("DEBUG running scp command: %s %s\n", "scp-config", localFile) 112 | writer, err := session.StdinPipe() 113 | if err != nil { 114 | return err 115 | } 116 | f, err := os.Open(localFile) 117 | if err != nil { 118 | log.Printf("ERROR cannot open file: %s\n", localFile) 119 | return err 120 | } 121 | _, err = io.Copy(writer, f) 122 | if err != nil { 123 | log.Printf("ERROR failed to send file: %s\n", localFile) 124 | return err 125 | } 126 | go func() { 127 | time.Sleep(3 * time.Second) 128 | writer.Close() 129 | }() 130 | if len(serverLocationOptions) > 0 { 131 | log.Printf("DEBUG running scp command: %s %s%s\n", "scp-config", basefileName, serverLocationOptions) 132 | err = session.Run(fmt.Sprintf("scp-config %s%s", basefileName, serverLocationOptions)) 133 | } else { 134 | log.Printf("DEBUG running scp command: %s %s\n", "scp-config", basefileName) 135 | err = session.Run(fmt.Sprintf("scp-config %s", basefileName)) 136 | } 137 | 138 | if err != nil { 139 | return err 140 | } 141 | return nil 142 | } 143 | 144 | // RunKatago runs the ssh as katago 145 | func (kataSSHSession *KataSSHSession) RunKatago(sshoptions model.SSHOptions, cmd string, inputReader io.Reader, outputWriter io.Writer, stderrWriter io.Writer, useRawData bool, onReady func()) error { 146 | config := &ssh.ClientConfig{ 147 | Timeout: 30 * time.Second, 148 | User: sshoptions.User, 149 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 150 | } 151 | 152 | config.Auth = []ssh.AuthMethod{ssh.Password(sshoptions.Password)} 153 | addr := fmt.Sprintf("%s:%d", sshoptions.Host, sshoptions.Port) 154 | sshClient, err := ssh.Dial("tcp", addr, config) 155 | if err != nil { 156 | log.Printf("DEBUG failed to connect to %s with password: %s : %v", addr, sshoptions.Password, err) 157 | return err 158 | } 159 | defer sshClient.Close() 160 | 161 | session, err := sshClient.NewSession() 162 | if err != nil { 163 | log.Printf("DEBUG failed to create session: %v", err) 164 | return err 165 | } 166 | defer session.Close() 167 | if kataSSHSession.Stopped { 168 | log.Printf("DEBUG session has been stopped before running") 169 | return nil 170 | } 171 | kataSSHSession.Session = session 172 | 173 | session.Stderr = stderrWriter 174 | session.Stdin = inputReader 175 | reader, err := session.StdoutPipe() 176 | if err != nil { 177 | log.Printf("DEBUG pipe stdout: %v", err) 178 | return err 179 | } 180 | go func() { 181 | buf := make([]byte, 4096) 182 | var theReader io.Reader = nil 183 | var gtpReader *GTPReader = nil 184 | if !useRawData { 185 | gtpReader = NewGTPReader(reader) 186 | } else { 187 | theReader = reader 188 | } 189 | 190 | for { 191 | var n int = 0 192 | var err error = nil 193 | if !useRawData { 194 | n, err = gtpReader.Read(buf) 195 | } else { 196 | n, err = theReader.Read(buf) 197 | } 198 | 199 | outputWriter.Write(buf[:n]) 200 | if err != nil { 201 | if err == io.EOF { 202 | break 203 | } else { 204 | log.Printf("ERROR failed to read from buffer, %+v\n", err) 205 | return 206 | } 207 | } 208 | } 209 | }() 210 | 211 | // keep alive 212 | go func() { 213 | for { 214 | if kataSSHSession.Stopped { 215 | break 216 | } 217 | _, err := session.SendRequest("keepalive@ikatago.com", true, nil) 218 | if err != nil { 219 | break 220 | } 221 | time.Sleep(15 * time.Second) 222 | } 223 | }() 224 | 225 | //log.Printf("DEBUG running equal commad: ssh -p %d %s@%s %s\n", sshoptions.Port, sshoptions.User, sshoptions.Host, cmd) 226 | if onReady != nil { 227 | onReady() 228 | } 229 | err = session.Run(cmd) 230 | if err != nil { 231 | return err 232 | } 233 | return nil 234 | } 235 | 236 | func (kataSSHSession *KataSSHSession) Stop() { 237 | kataSSHSession.Stopped = true 238 | if kataSSHSession.Session != nil { 239 | kataSSHSession.Session.Close() 240 | kataSSHSession.Session = nil 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kinfkong/ikatago-client/a2d978455d813e31113bf9370eb04c2268710eba/main -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/jessevdk/go-flags" 10 | "github.com/kinfkong/ikatago-client/client" 11 | "github.com/kinfkong/ikatago-client/ikatagosdk" 12 | "github.com/kinfkong/ikatago-client/model" 13 | "github.com/kinfkong/ikatago-client/utils" 14 | ) 15 | 16 | const ( 17 | AppVersion = "1.7.1" 18 | ) 19 | 20 | var opts = model.AllOpts{} 21 | 22 | type MockDataCallback struct { 23 | } 24 | 25 | func (callback *MockDataCallback) Callback(content []byte) { 26 | log.Printf(string(content)) 27 | 28 | } 29 | func (callback *MockDataCallback) StderrCallback(content []byte) { 30 | log.Printf(string(content)) 31 | } 32 | func (callback *MockDataCallback) OnReady() { 33 | } 34 | 35 | func TestSDK() { 36 | client, _ := ikatagosdk.NewClient("", "all", "zz-xxxx", "xxxx") 37 | client.SetExtraArgs("--gpu-type 6x --kata-weight 60b") 38 | // query server 39 | result, _ := client.QueryServer() 40 | log.Printf("DEBUG query result: %v", result) 41 | // run katago 42 | runner, _ := client.CreateKatagoRunner() 43 | var callback ikatagosdk.DataCallback = &MockDataCallback{} 44 | go func() { 45 | time.Sleep(time.Second * 15) 46 | runner.SendGTPCommand("version\n") 47 | }() 48 | runner.Run(callback) 49 | 50 | } 51 | 52 | func main() { 53 | l := log.New(os.Stderr, "", 0) 54 | fmt.Fprintln(os.Stderr, "ikatago version: ", AppVersion) 55 | // parse args 56 | subCommands, err := flags.Parse(&opts) 57 | if err != nil { 58 | log.Fatal("Cannot parse args: ", err) 59 | } 60 | defaultWorld := utils.WorldURL 61 | if opts.World == nil { 62 | opts.World = &defaultWorld 63 | } 64 | l.Printf("DEBUG the world is: %s\n", *opts.World) 65 | l.Printf("DEBUG Platform: [%s] User: [%s]\n", opts.Platform, opts.Username) 66 | remoteClient, err := client.NewClient(client.Options{ 67 | World: *opts.World, 68 | Platform: opts.Platform, 69 | Username: opts.Username, 70 | Password: opts.Password, 71 | EngineType: opts.EngineType, 72 | ForceNode: opts.ForceNode, 73 | GpuType: opts.GpuType, 74 | Token: opts.Token, 75 | }) 76 | if err != nil { 77 | log.Fatal("Failed to create client.", err) 78 | } 79 | if opts.Command == "run-katago" { 80 | sessionResult, err := remoteClient.RunKatago(client.RunKatagoOptions{ 81 | NoCompress: opts.NoCompress, 82 | RefreshInterval: opts.RefreshInterval, 83 | TransmitMoveNum: opts.TransmitMoveNum, 84 | KataLocalConfig: opts.KataLocalConfig, 85 | KataOverrideConfig: opts.KataOverrideConfig, 86 | KataConfig: opts.KataConfig, 87 | KataWeight: opts.KataWeight, 88 | KataName: opts.KataName, 89 | ExtraInfo: opts.ExtraInfo, 90 | ClientID: opts.ClientID, 91 | UseRawData: false, 92 | }, subCommands, os.Stdin, os.Stdout, os.Stderr, nil) 93 | if err != nil { 94 | l.Printf("ERROR run katago failed: %v", err) 95 | l.Fatal("Failed to run katago.", err) 96 | } 97 | sessionResult.Wait() 98 | } else if opts.Command == "preload-katago" { 99 | sessionResult, err := remoteClient.PreloadKatago(client.RunKatagoOptions{ 100 | NoCompress: opts.NoCompress, 101 | RefreshInterval: opts.RefreshInterval, 102 | TransmitMoveNum: opts.TransmitMoveNum, 103 | KataLocalConfig: opts.KataLocalConfig, 104 | KataOverrideConfig: opts.KataOverrideConfig, 105 | KataConfig: opts.KataConfig, 106 | KataWeight: opts.KataWeight, 107 | KataName: opts.KataName, 108 | UseRawData: false, 109 | }, subCommands, os.Stdin, os.Stdout, os.Stderr, nil) 110 | if err != nil { 111 | l.Printf("ERROR preload katago failed: %v", err) 112 | l.Fatal("Failed to preload katago.", err) 113 | } 114 | sessionResult.Wait() 115 | } else if opts.Command == "query-server" { 116 | // run katago command 117 | err := remoteClient.QueryServer(os.Stdout) 118 | if err != nil { 119 | l.Fatal("Failed to query server.", err) 120 | } 121 | } else if opts.Command == "view-config" { 122 | err := remoteClient.ViewConfig(client.RunKatagoOptions{ 123 | NoCompress: opts.NoCompress, 124 | RefreshInterval: opts.RefreshInterval, 125 | TransmitMoveNum: opts.TransmitMoveNum, 126 | KataLocalConfig: opts.KataLocalConfig, 127 | KataOverrideConfig: opts.KataOverrideConfig, 128 | KataConfig: opts.KataConfig, 129 | KataWeight: opts.KataWeight, 130 | KataName: opts.KataName, 131 | UseRawData: false, 132 | }, subCommands, os.Stdout) 133 | if err != nil { 134 | l.Printf("ERROR view katago config failed: %v", err) 135 | l.Fatal("Failed to view katago config.", err) 136 | } 137 | } else { 138 | l.Fatal(fmt.Sprintf("Unknown command: [%s]", opts.Command)) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // SSHOptions represents the ssh options 4 | type SSHOptions struct { 5 | Host string `json:"host"` 6 | Port int `json:"port"` 7 | User string `json:"user"` 8 | 9 | Password string `json:"password"` 10 | } 11 | type AllOpts struct { 12 | World *string `short:"w" long:"world" description:"The world url."` 13 | Platform string `short:"p" long:"platform" description:"The platform, like aistudio, colab" required:"true"` 14 | Username string `short:"u" long:"username" description:"Your username to connect" required:"true"` 15 | Password string `long:"password" description:"Your password to connect" required:"true"` 16 | NoCompress bool `long:"no-compress" description:"compress the data during transmission"` 17 | RefreshInterval int `long:"refresh-interval" description:"sets the refresh interval in cent seconds" default:"30"` 18 | EngineType *string `long:"engine-type" description:"sets the enginetype"` 19 | Token *string `long:"token" description:"sets the token"` 20 | GpuType *string `long:"gpu-type" description:"sets the gpu type"` 21 | TransmitMoveNum int `long:"transmit-move-num" description:"limits number of moves when transmission during analyze" default:"20"` 22 | KataLocalConfig *string `long:"kata-local-config" description:"The katago config file. like, gtp_example.cfg"` 23 | KataOverrideConfig *string `long:"kata-override-config" description:"The katago override-config, like: analysisPVLen=30,numSearchThreads=30"` 24 | 25 | KataName *string `long:"kata-name" description:"The katago binary name"` 26 | ForceNode *string `long:"force-node" description:"in cluster, force to a specific node."` 27 | KataWeight *string `long:"kata-weight" description:"The katago weight name"` 28 | KataConfig *string `long:"kata-config" description:"The katago config name"` 29 | ExtraInfo *string `long:"extra-info" description:"The extra info"` 30 | ClientID *string `long:"client-id" description:"The source client id"` 31 | Command string `long:"cmd" description:"The command to run the katago" default:"run-katago"` 32 | } 33 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CLI_VERSION=1.20.1 3 | rm -rf ./bin && mkdir ./bin 4 | 5 | 6 | # Mac OSX 7 | OUTPUT_PATH=ikatago-$CLI_VERSION-mac-osx 8 | mkdir -p ./bin/$OUTPUT_PATH 9 | GOOS=darwin GOARCH=amd64 go build -o ./bin/$OUTPUT_PATH/ikatago 10 | cd ./bin 11 | zip -r $OUTPUT_PATH.zip $OUTPUT_PATH 12 | cd - >/dev/null 13 | 14 | # linux 15 | OUTPUT_PATH=ikatago-$CLI_VERSION-linux 16 | mkdir -p ./bin/$OUTPUT_PATH 17 | GOOS=linux GOARCH=amd64 go build -o ./bin/$OUTPUT_PATH/ikatago 18 | cd ./bin 19 | zip -r $OUTPUT_PATH.zip $OUTPUT_PATH 20 | cd - >/dev/null 21 | 22 | # windows 64bit 23 | OUTPUT_PATH=ikatago-$CLI_VERSION-win64 24 | mkdir -p ./bin/$OUTPUT_PATH 25 | GOOS=windows GOARCH=amd64 go build -o ./bin/$OUTPUT_PATH/ikatago.exe 26 | cd ./bin 27 | zip -r $OUTPUT_PATH.zip $OUTPUT_PATH 28 | cd - >/dev/null 29 | 30 | # windows 32bit 31 | OUTPUT_PATH=ikatago-$CLI_VERSION-win32 32 | mkdir -p ./bin/$OUTPUT_PATH 33 | GOOS=windows GOARCH=386 CGO_ENABLED=0 go build -o ./bin/$OUTPUT_PATH/ikatago.exe 34 | cd ./bin 35 | zip -r $OUTPUT_PATH.zip $OUTPUT_PATH 36 | cd - >/dev/null 37 | -------------------------------------------------------------------------------- /platform/platform.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | // Oss represents the oss bucket of this platform 4 | type Oss struct { 5 | BucketEndpoint string `json:"bucketEndpoint"` 6 | Bucket string `json:"Bucket"` 7 | } 8 | 9 | type Http struct { 10 | GetUrl *string `json:"getUrl"` 11 | GetPostUrl *string `json:"getPostUrl"` 12 | } 13 | 14 | // Platform represents the platform 15 | type Platform struct { 16 | Name string `json:"name"` 17 | Oss Oss `json:"oss"` 18 | Http *Http `json:"http"` 19 | } 20 | -------------------------------------------------------------------------------- /utils/consts.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const ( 4 | // WorldURL represents the root url of the default world 5 | WorldURL = "https://ikatago-fairyland.oss-cn-beijing.aliyuncs.com/world.json" 6 | ) 7 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "os" 11 | 12 | "moul.io/http2curl/v2" 13 | ) 14 | 15 | // DoHTTPRequest Sends generic http request 16 | func DoHTTPRequest(method string, url string, headers map[string]string, body []byte) (responseBody string, err error) { 17 | tr := &http.Transport{ 18 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 19 | } 20 | httpClient := &http.Client{Transport: tr} 21 | req, _ := http.NewRequest(method, url, bytes.NewBuffer(body)) 22 | req.Close = true 23 | if headers != nil { 24 | for k, v := range headers { 25 | req.Header.Set(k, v) 26 | } 27 | } 28 | 29 | command, _ := http2curl.GetCurlCommand(req) 30 | 31 | response, err := httpClient.Do(req) 32 | if err != nil { 33 | log.Printf("ERROR error requesting with http: %s, error: %v\n", command, err) 34 | err = errors.New("failed_do_request") 35 | return 36 | } 37 | bodyBytes, err := ioutil.ReadAll(response.Body) 38 | response.Body.Close() 39 | 40 | if err != nil { 41 | log.Printf("ERROR error requesting with http: %s, error: %v\n", command, err) 42 | err = errors.New("failed_read_body") 43 | return 44 | } 45 | 46 | responseBody = string(bodyBytes) 47 | 48 | if response.StatusCode < 200 || response.StatusCode >= 300 { 49 | log.Printf("ERROR error requesting with http: %s, status code: %v, response: %s\n", command, response.StatusCode, responseBody) 50 | err = errors.New("invalid_status") 51 | return 52 | } 53 | 54 | return 55 | } 56 | 57 | // FileExists checks if a file exists and is not a directory before we 58 | func FileExists(filename string) bool { 59 | info, err := os.Stat(filename) 60 | if os.IsNotExist(err) { 61 | return false 62 | } 63 | return !info.IsDir() 64 | } 65 | 66 | // GetFileSize gets the fie size 67 | func GetFileSize(filename string) (int64, error) { 68 | info, err := os.Stat(filename) 69 | if os.IsNotExist(err) || info.IsDir() { 70 | return 0, errors.New("file_not_found") 71 | } 72 | return info.Size(), nil 73 | } 74 | --------------------------------------------------------------------------------