cmd.go 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. package cmd
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "context"
  6. "crypto/sha256"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "log"
  12. "math"
  13. "net"
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "os/signal"
  18. "path/filepath"
  19. "regexp"
  20. "runtime"
  21. "slices"
  22. "strings"
  23. "syscall"
  24. "time"
  25. "github.com/containerd/console"
  26. "github.com/mattn/go-runewidth"
  27. "github.com/olekukonko/tablewriter"
  28. "github.com/spf13/cobra"
  29. "golang.org/x/crypto/ssh"
  30. "golang.org/x/term"
  31. "github.com/ollama/ollama/api"
  32. "github.com/ollama/ollama/auth"
  33. "github.com/ollama/ollama/envconfig"
  34. "github.com/ollama/ollama/format"
  35. "github.com/ollama/ollama/parser"
  36. "github.com/ollama/ollama/progress"
  37. "github.com/ollama/ollama/server"
  38. "github.com/ollama/ollama/types/errtypes"
  39. "github.com/ollama/ollama/types/model"
  40. "github.com/ollama/ollama/version"
  41. )
  42. func CreateHandler(cmd *cobra.Command, args []string) error {
  43. filename, _ := cmd.Flags().GetString("file")
  44. filename, err := filepath.Abs(filename)
  45. if err != nil {
  46. return err
  47. }
  48. client, err := api.ClientFromEnvironment()
  49. if err != nil {
  50. return err
  51. }
  52. p := progress.NewProgress(os.Stderr)
  53. defer p.Stop()
  54. f, err := os.Open(filename)
  55. if err != nil {
  56. return err
  57. }
  58. defer f.Close()
  59. modelfile, err := parser.ParseFile(f)
  60. if err != nil {
  61. return err
  62. }
  63. home, err := os.UserHomeDir()
  64. if err != nil {
  65. return err
  66. }
  67. status := "transferring model data"
  68. spinner := progress.NewSpinner(status)
  69. p.Add(status, spinner)
  70. for i := range modelfile.Commands {
  71. switch modelfile.Commands[i].Name {
  72. case "model", "adapter":
  73. path := modelfile.Commands[i].Args
  74. if path == "~" {
  75. path = home
  76. } else if strings.HasPrefix(path, "~/") {
  77. path = filepath.Join(home, path[2:])
  78. }
  79. if !filepath.IsAbs(path) {
  80. path = filepath.Join(filepath.Dir(filename), path)
  81. }
  82. fi, err := os.Stat(path)
  83. if errors.Is(err, os.ErrNotExist) && modelfile.Commands[i].Name == "model" {
  84. continue
  85. } else if err != nil {
  86. return err
  87. }
  88. if fi.IsDir() {
  89. // this is likely a safetensors or pytorch directory
  90. // TODO make this work w/ adapters
  91. tempfile, err := tempZipFiles(path)
  92. if err != nil {
  93. return err
  94. }
  95. defer os.RemoveAll(tempfile)
  96. path = tempfile
  97. }
  98. digest, err := createBlob(cmd, client, path)
  99. if err != nil {
  100. return err
  101. }
  102. modelfile.Commands[i].Args = "@" + digest
  103. }
  104. }
  105. bars := make(map[string]*progress.Bar)
  106. fn := func(resp api.ProgressResponse) error {
  107. if resp.Digest != "" {
  108. spinner.Stop()
  109. bar, ok := bars[resp.Digest]
  110. if !ok {
  111. bar = progress.NewBar(fmt.Sprintf("pulling %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  112. bars[resp.Digest] = bar
  113. p.Add(resp.Digest, bar)
  114. }
  115. bar.Set(resp.Completed)
  116. } else if status != resp.Status {
  117. spinner.Stop()
  118. status = resp.Status
  119. spinner = progress.NewSpinner(status)
  120. p.Add(status, spinner)
  121. }
  122. return nil
  123. }
  124. quantize, _ := cmd.Flags().GetString("quantize")
  125. request := api.CreateRequest{Name: args[0], Modelfile: modelfile.String(), Quantize: quantize}
  126. if err := client.Create(cmd.Context(), &request, fn); err != nil {
  127. return err
  128. }
  129. return nil
  130. }
  131. func tempZipFiles(path string) (string, error) {
  132. tempfile, err := os.CreateTemp("", "ollama-tf")
  133. if err != nil {
  134. return "", err
  135. }
  136. defer tempfile.Close()
  137. detectContentType := func(path string) (string, error) {
  138. f, err := os.Open(path)
  139. if err != nil {
  140. return "", err
  141. }
  142. defer f.Close()
  143. var b bytes.Buffer
  144. b.Grow(512)
  145. if _, err := io.CopyN(&b, f, 512); err != nil && !errors.Is(err, io.EOF) {
  146. return "", err
  147. }
  148. contentType, _, _ := strings.Cut(http.DetectContentType(b.Bytes()), ";")
  149. return contentType, nil
  150. }
  151. glob := func(pattern, contentType string) ([]string, error) {
  152. matches, err := filepath.Glob(pattern)
  153. if err != nil {
  154. return nil, err
  155. }
  156. for _, safetensor := range matches {
  157. if ct, err := detectContentType(safetensor); err != nil {
  158. return nil, err
  159. } else if ct != contentType {
  160. return nil, fmt.Errorf("invalid content type: expected %s for %s", ct, safetensor)
  161. }
  162. }
  163. return matches, nil
  164. }
  165. var files []string
  166. if st, _ := glob(filepath.Join(path, "model*.safetensors"), "application/octet-stream"); len(st) > 0 {
  167. // safetensors files might be unresolved git lfs references; skip if they are
  168. // covers model-x-of-y.safetensors, model.fp32-x-of-y.safetensors, model.safetensors
  169. files = append(files, st...)
  170. } else if pt, _ := glob(filepath.Join(path, "pytorch_model*.bin"), "application/zip"); len(pt) > 0 {
  171. // pytorch files might also be unresolved git lfs references; skip if they are
  172. // covers pytorch_model-x-of-y.bin, pytorch_model.fp32-x-of-y.bin, pytorch_model.bin
  173. files = append(files, pt...)
  174. } else if pt, _ := glob(filepath.Join(path, "consolidated*.pth"), "application/zip"); len(pt) > 0 {
  175. // pytorch files might also be unresolved git lfs references; skip if they are
  176. // covers consolidated.x.pth, consolidated.pth
  177. files = append(files, pt...)
  178. } else {
  179. return "", errors.New("no safetensors or torch files found")
  180. }
  181. // add configuration files, json files are detected as text/plain
  182. js, err := glob(filepath.Join(path, "*.json"), "text/plain")
  183. if err != nil {
  184. return "", err
  185. }
  186. files = append(files, js...)
  187. if tks, _ := glob(filepath.Join(path, "tokenizer.model"), "application/octet-stream"); len(tks) > 0 {
  188. // add tokenizer.model if it exists, tokenizer.json is automatically picked up by the previous glob
  189. // tokenizer.model might be a unresolved git lfs reference; error if it is
  190. files = append(files, tks...)
  191. } else if tks, _ := glob(filepath.Join(path, "**/tokenizer.model"), "text/plain"); len(tks) > 0 {
  192. // some times tokenizer.model is in a subdirectory (e.g. meta-llama/Meta-Llama-3-8B)
  193. files = append(files, tks...)
  194. }
  195. zipfile := zip.NewWriter(tempfile)
  196. defer zipfile.Close()
  197. for _, file := range files {
  198. f, err := os.Open(file)
  199. if err != nil {
  200. return "", err
  201. }
  202. defer f.Close()
  203. fi, err := f.Stat()
  204. if err != nil {
  205. return "", err
  206. }
  207. zfi, err := zip.FileInfoHeader(fi)
  208. if err != nil {
  209. return "", err
  210. }
  211. zf, err := zipfile.CreateHeader(zfi)
  212. if err != nil {
  213. return "", err
  214. }
  215. if _, err := io.Copy(zf, f); err != nil {
  216. return "", err
  217. }
  218. }
  219. return tempfile.Name(), nil
  220. }
  221. func createBlob(cmd *cobra.Command, client *api.Client, path string) (string, error) {
  222. bin, err := os.Open(path)
  223. if err != nil {
  224. return "", err
  225. }
  226. defer bin.Close()
  227. hash := sha256.New()
  228. if _, err := io.Copy(hash, bin); err != nil {
  229. return "", err
  230. }
  231. if _, err := bin.Seek(0, io.SeekStart); err != nil {
  232. return "", err
  233. }
  234. digest := fmt.Sprintf("sha256:%x", hash.Sum(nil))
  235. // Here, we want to check if the server is local
  236. // If true, call, createBlobLocal
  237. // This should find the model directory, copy blob over, and return the digest
  238. // If this fails, just upload it
  239. // If this is successful, return the digest
  240. // Resolve server to IP
  241. // Check if server is local
  242. /* if client.IsLocal() {
  243. digest = strings.ReplaceAll(digest, ":", "-")
  244. config, err := client.HeadBlob(cmd.Context(), digest)
  245. if err != nil {
  246. return "", err
  247. }
  248. modelDir := config.ModelDir
  249. // Get blob destination
  250. dest := filepath.Join(modelDir, "blobs", digest)
  251. err = createBlobLocal(path, dest)
  252. if err == nil {
  253. return digest, nil
  254. }
  255. } */
  256. if client.IsLocal() {
  257. config, err := getLocalPath(cmd.Context(), digest)
  258. if err != nil {
  259. return "", err
  260. }
  261. if config == nil {
  262. fmt.Println("config is nil")
  263. return digest, nil
  264. }
  265. fmt.Println("HI")
  266. dest := config.ModelDir
  267. fmt.Println("dest is ", dest)
  268. err = createBlobLocal(path, dest)
  269. if err == nil {
  270. fmt.Println("createlocalblob succeed")
  271. return digest, nil
  272. }
  273. fmt.Println("err is ", err)
  274. fmt.Println("createlocalblob faileds")
  275. }
  276. fmt.Println("DEFAULT")
  277. if err = client.CreateBlob(cmd.Context(), digest, false, bin); err != nil {
  278. return "", err
  279. }
  280. return digest, nil
  281. }
  282. func getLocalPath(ctx context.Context, digest string) (*api.ServerConfig, error) {
  283. ollamaHost := envconfig.Host
  284. client := http.DefaultClient
  285. base := &url.URL{
  286. Scheme: ollamaHost.Scheme,
  287. Host: net.JoinHostPort(ollamaHost.Host, ollamaHost.Port),
  288. }
  289. var reqBody io.Reader
  290. var respData api.ServerConfig
  291. data, err := json.Marshal(digest)
  292. if err != nil {
  293. return nil, err
  294. }
  295. reqBody = bytes.NewReader(data)
  296. path := fmt.Sprintf("/api/blobs/%s", digest)
  297. requestURL := base.JoinPath(path)
  298. request, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURL.String(), reqBody)
  299. if err != nil {
  300. return nil, err
  301. }
  302. request.Header.Set("Content-Type", "application/json")
  303. request.Header.Set("Accept", "application/json")
  304. request.Header.Set("User-Agent", fmt.Sprintf("ollama/%s (%s %s) Go/%s", version.Version, runtime.GOARCH, runtime.GOOS, runtime.Version()))
  305. request.Header.Set("X-Redirect-Create", "1")
  306. fmt.Println("request", request)
  307. resp, err := client.Do(request)
  308. if err != nil {
  309. return nil, err
  310. }
  311. defer resp.Body.Close()
  312. fmt.Println("made it here")
  313. fmt.Println("resp", resp)
  314. if resp.StatusCode == http.StatusTemporaryRedirect {
  315. fmt.Println("redirect")
  316. if err := json.Unmarshal([]byte(resp.Header.Get("loc")), &respData); err != nil {
  317. fmt.Println("error unmarshalling response data")
  318. return nil, err
  319. }
  320. }
  321. fmt.Println("!!!!!!!!!!")
  322. fmt.Println(respData)
  323. return &respData, nil
  324. }
  325. func createBlobLocal(path string, dest string) error {
  326. // This function should be called if the server is local
  327. // It should find the model directory, copy the blob over, and return the digest
  328. dirPath := filepath.Dir(dest)
  329. fmt.Println("dirpath is ", dirPath)
  330. if err := os.MkdirAll(dirPath, 0o755); err != nil {
  331. fmt.Println("failed to create directory")
  332. return err
  333. }
  334. // Copy blob over
  335. sourceFile, err := os.Open(path)
  336. if err != nil {
  337. return fmt.Errorf("could not open source file: %v", err)
  338. }
  339. defer sourceFile.Close()
  340. destFile, err := os.Create(dest)
  341. if err != nil {
  342. return fmt.Errorf("could not create destination file: %v", err)
  343. }
  344. defer destFile.Close()
  345. _, err = io.CopyBuffer(destFile, sourceFile, make([]byte, 4*1024*1024))
  346. if err != nil {
  347. return fmt.Errorf("error copying file: %v", err)
  348. }
  349. err = destFile.Sync()
  350. if err != nil {
  351. return fmt.Errorf("error flushing file: %v", err)
  352. }
  353. return nil
  354. }
  355. func RunHandler(cmd *cobra.Command, args []string) error {
  356. interactive := true
  357. opts := runOptions{
  358. Model: args[0],
  359. WordWrap: os.Getenv("TERM") == "xterm-256color",
  360. Options: map[string]interface{}{},
  361. }
  362. format, err := cmd.Flags().GetString("format")
  363. if err != nil {
  364. return err
  365. }
  366. opts.Format = format
  367. keepAlive, err := cmd.Flags().GetString("keepalive")
  368. if err != nil {
  369. return err
  370. }
  371. if keepAlive != "" {
  372. d, err := time.ParseDuration(keepAlive)
  373. if err != nil {
  374. return err
  375. }
  376. opts.KeepAlive = &api.Duration{Duration: d}
  377. }
  378. prompts := args[1:]
  379. // prepend stdin to the prompt if provided
  380. if !term.IsTerminal(int(os.Stdin.Fd())) {
  381. in, err := io.ReadAll(os.Stdin)
  382. if err != nil {
  383. return err
  384. }
  385. prompts = append([]string{string(in)}, prompts...)
  386. opts.WordWrap = false
  387. interactive = false
  388. }
  389. opts.Prompt = strings.Join(prompts, " ")
  390. if len(prompts) > 0 {
  391. interactive = false
  392. }
  393. nowrap, err := cmd.Flags().GetBool("nowordwrap")
  394. if err != nil {
  395. return err
  396. }
  397. opts.WordWrap = !nowrap
  398. // Fill out the rest of the options based on information about the
  399. // model.
  400. client, err := api.ClientFromEnvironment()
  401. if err != nil {
  402. return err
  403. }
  404. name := args[0]
  405. info, err := func() (*api.ShowResponse, error) {
  406. showReq := &api.ShowRequest{Name: name}
  407. info, err := client.Show(cmd.Context(), showReq)
  408. var se api.StatusError
  409. if errors.As(err, &se) && se.StatusCode == http.StatusNotFound {
  410. if err := PullHandler(cmd, []string{name}); err != nil {
  411. return nil, err
  412. }
  413. return client.Show(cmd.Context(), &api.ShowRequest{Name: name})
  414. }
  415. return info, err
  416. }()
  417. if err != nil {
  418. return err
  419. }
  420. opts.MultiModal = slices.Contains(info.Details.Families, "clip")
  421. opts.ParentModel = info.Details.ParentModel
  422. opts.Messages = append(opts.Messages, info.Messages...)
  423. if interactive {
  424. return generateInteractive(cmd, opts)
  425. }
  426. return generate(cmd, opts)
  427. }
  428. func errFromUnknownKey(unknownKeyErr error) error {
  429. // find SSH public key in the error message
  430. sshKeyPattern := `ssh-\w+ [^\s"]+`
  431. re := regexp.MustCompile(sshKeyPattern)
  432. matches := re.FindStringSubmatch(unknownKeyErr.Error())
  433. if len(matches) > 0 {
  434. serverPubKey := matches[0]
  435. publicKey, err := auth.GetPublicKey()
  436. if err != nil {
  437. return unknownKeyErr
  438. }
  439. localPubKey := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey)))
  440. if runtime.GOOS == "linux" && serverPubKey != localPubKey {
  441. // try the ollama service public key
  442. svcPubKey, err := os.ReadFile("/usr/share/ollama/.ollama/id_ed25519.pub")
  443. if err != nil {
  444. return unknownKeyErr
  445. }
  446. localPubKey = strings.TrimSpace(string(svcPubKey))
  447. }
  448. // check if the returned public key matches the local public key, this prevents adding a remote key to the user's account
  449. if serverPubKey != localPubKey {
  450. return unknownKeyErr
  451. }
  452. var msg strings.Builder
  453. msg.WriteString(unknownKeyErr.Error())
  454. msg.WriteString("\n\nYour ollama key is:\n")
  455. msg.WriteString(localPubKey)
  456. msg.WriteString("\nAdd your key at:\n")
  457. msg.WriteString("https://ollama.com/settings/keys")
  458. return errors.New(msg.String())
  459. }
  460. return unknownKeyErr
  461. }
  462. func PushHandler(cmd *cobra.Command, args []string) error {
  463. client, err := api.ClientFromEnvironment()
  464. if err != nil {
  465. return err
  466. }
  467. insecure, err := cmd.Flags().GetBool("insecure")
  468. if err != nil {
  469. return err
  470. }
  471. p := progress.NewProgress(os.Stderr)
  472. defer p.Stop()
  473. bars := make(map[string]*progress.Bar)
  474. var status string
  475. var spinner *progress.Spinner
  476. fn := func(resp api.ProgressResponse) error {
  477. if resp.Digest != "" {
  478. if spinner != nil {
  479. spinner.Stop()
  480. }
  481. bar, ok := bars[resp.Digest]
  482. if !ok {
  483. bar = progress.NewBar(fmt.Sprintf("pushing %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  484. bars[resp.Digest] = bar
  485. p.Add(resp.Digest, bar)
  486. }
  487. bar.Set(resp.Completed)
  488. } else if status != resp.Status {
  489. if spinner != nil {
  490. spinner.Stop()
  491. }
  492. status = resp.Status
  493. spinner = progress.NewSpinner(status)
  494. p.Add(status, spinner)
  495. }
  496. return nil
  497. }
  498. request := api.PushRequest{Name: args[0], Insecure: insecure}
  499. if err := client.Push(cmd.Context(), &request, fn); err != nil {
  500. if spinner != nil {
  501. spinner.Stop()
  502. }
  503. if strings.Contains(err.Error(), "access denied") {
  504. return errors.New("you are not authorized to push to this namespace, create the model under a namespace you own")
  505. }
  506. host := model.ParseName(args[0]).Host
  507. isOllamaHost := strings.HasSuffix(host, ".ollama.ai") || strings.HasSuffix(host, ".ollama.com")
  508. if strings.Contains(err.Error(), errtypes.UnknownOllamaKeyErrMsg) && isOllamaHost {
  509. // the user has not added their ollama key to ollama.com
  510. // re-throw an error with a more user-friendly message
  511. return errFromUnknownKey(err)
  512. }
  513. return err
  514. }
  515. spinner.Stop()
  516. return nil
  517. }
  518. func ListHandler(cmd *cobra.Command, args []string) error {
  519. client, err := api.ClientFromEnvironment()
  520. if err != nil {
  521. return err
  522. }
  523. models, err := client.List(cmd.Context())
  524. if err != nil {
  525. return err
  526. }
  527. var data [][]string
  528. for _, m := range models.Models {
  529. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  530. data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), format.HumanTime(m.ModifiedAt, "Never")})
  531. }
  532. }
  533. table := tablewriter.NewWriter(os.Stdout)
  534. table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
  535. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  536. table.SetAlignment(tablewriter.ALIGN_LEFT)
  537. table.SetHeaderLine(false)
  538. table.SetBorder(false)
  539. table.SetNoWhiteSpace(true)
  540. table.SetTablePadding("\t")
  541. table.AppendBulk(data)
  542. table.Render()
  543. return nil
  544. }
  545. func ListRunningHandler(cmd *cobra.Command, args []string) error {
  546. client, err := api.ClientFromEnvironment()
  547. if err != nil {
  548. return err
  549. }
  550. models, err := client.ListRunning(cmd.Context())
  551. if err != nil {
  552. return err
  553. }
  554. var data [][]string
  555. for _, m := range models.Models {
  556. if len(args) == 0 || strings.HasPrefix(m.Name, args[0]) {
  557. var procStr string
  558. switch {
  559. case m.SizeVRAM == 0:
  560. procStr = "100% CPU"
  561. case m.SizeVRAM == m.Size:
  562. procStr = "100% GPU"
  563. case m.SizeVRAM > m.Size || m.Size == 0:
  564. procStr = "Unknown"
  565. default:
  566. sizeCPU := m.Size - m.SizeVRAM
  567. cpuPercent := math.Round(float64(sizeCPU) / float64(m.Size) * 100)
  568. procStr = fmt.Sprintf("%d%%/%d%% CPU/GPU", int(cpuPercent), int(100-cpuPercent))
  569. }
  570. data = append(data, []string{m.Name, m.Digest[:12], format.HumanBytes(m.Size), procStr, format.HumanTime(m.ExpiresAt, "Never")})
  571. }
  572. }
  573. table := tablewriter.NewWriter(os.Stdout)
  574. table.SetHeader([]string{"NAME", "ID", "SIZE", "PROCESSOR", "UNTIL"})
  575. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  576. table.SetAlignment(tablewriter.ALIGN_LEFT)
  577. table.SetHeaderLine(false)
  578. table.SetBorder(false)
  579. table.SetNoWhiteSpace(true)
  580. table.SetTablePadding("\t")
  581. table.AppendBulk(data)
  582. table.Render()
  583. return nil
  584. }
  585. func DeleteHandler(cmd *cobra.Command, args []string) error {
  586. client, err := api.ClientFromEnvironment()
  587. if err != nil {
  588. return err
  589. }
  590. for _, name := range args {
  591. req := api.DeleteRequest{Name: name}
  592. if err := client.Delete(cmd.Context(), &req); err != nil {
  593. return err
  594. }
  595. fmt.Printf("deleted '%s'\n", name)
  596. }
  597. return nil
  598. }
  599. func ShowHandler(cmd *cobra.Command, args []string) error {
  600. client, err := api.ClientFromEnvironment()
  601. if err != nil {
  602. return err
  603. }
  604. license, errLicense := cmd.Flags().GetBool("license")
  605. modelfile, errModelfile := cmd.Flags().GetBool("modelfile")
  606. parameters, errParams := cmd.Flags().GetBool("parameters")
  607. system, errSystem := cmd.Flags().GetBool("system")
  608. template, errTemplate := cmd.Flags().GetBool("template")
  609. for _, boolErr := range []error{errLicense, errModelfile, errParams, errSystem, errTemplate} {
  610. if boolErr != nil {
  611. return errors.New("error retrieving flags")
  612. }
  613. }
  614. flagsSet := 0
  615. showType := ""
  616. if license {
  617. flagsSet++
  618. showType = "license"
  619. }
  620. if modelfile {
  621. flagsSet++
  622. showType = "modelfile"
  623. }
  624. if parameters {
  625. flagsSet++
  626. showType = "parameters"
  627. }
  628. if system {
  629. flagsSet++
  630. showType = "system"
  631. }
  632. if template {
  633. flagsSet++
  634. showType = "template"
  635. }
  636. if flagsSet > 1 {
  637. return errors.New("only one of '--license', '--modelfile', '--parameters', '--system', or '--template' can be specified")
  638. }
  639. req := api.ShowRequest{Name: args[0]}
  640. resp, err := client.Show(cmd.Context(), &req)
  641. if err != nil {
  642. return err
  643. }
  644. if flagsSet == 1 {
  645. switch showType {
  646. case "license":
  647. fmt.Println(resp.License)
  648. case "modelfile":
  649. fmt.Println(resp.Modelfile)
  650. case "parameters":
  651. fmt.Println(resp.Parameters)
  652. case "system":
  653. fmt.Println(resp.System)
  654. case "template":
  655. fmt.Println(resp.Template)
  656. }
  657. return nil
  658. }
  659. showInfo(resp)
  660. return nil
  661. }
  662. func showInfo(resp *api.ShowResponse) {
  663. arch := resp.ModelInfo["general.architecture"].(string)
  664. modelData := [][]string{
  665. {"arch", arch},
  666. {"parameters", resp.Details.ParameterSize},
  667. {"quantization", resp.Details.QuantizationLevel},
  668. {"context length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.context_length", arch)].(float64))},
  669. {"embedding length", fmt.Sprintf("%v", resp.ModelInfo[fmt.Sprintf("%s.embedding_length", arch)].(float64))},
  670. }
  671. mainTableData := [][]string{
  672. {"Model"},
  673. {renderSubTable(modelData, false)},
  674. }
  675. if resp.ProjectorInfo != nil {
  676. projectorData := [][]string{
  677. {"arch", "clip"},
  678. {"parameters", format.HumanNumber(uint64(resp.ProjectorInfo["general.parameter_count"].(float64)))},
  679. }
  680. if projectorType, ok := resp.ProjectorInfo["clip.projector_type"]; ok {
  681. projectorData = append(projectorData, []string{"projector type", projectorType.(string)})
  682. }
  683. projectorData = append(projectorData,
  684. []string{"embedding length", fmt.Sprintf("%v", resp.ProjectorInfo["clip.vision.embedding_length"].(float64))},
  685. []string{"projection dimensionality", fmt.Sprintf("%v", resp.ProjectorInfo["clip.vision.projection_dim"].(float64))},
  686. )
  687. mainTableData = append(mainTableData,
  688. []string{"Projector"},
  689. []string{renderSubTable(projectorData, false)},
  690. )
  691. }
  692. if resp.Parameters != "" {
  693. mainTableData = append(mainTableData, []string{"Parameters"}, []string{formatParams(resp.Parameters)})
  694. }
  695. if resp.System != "" {
  696. mainTableData = append(mainTableData, []string{"System"}, []string{renderSubTable(twoLines(resp.System), true)})
  697. }
  698. if resp.License != "" {
  699. mainTableData = append(mainTableData, []string{"License"}, []string{renderSubTable(twoLines(resp.License), true)})
  700. }
  701. table := tablewriter.NewWriter(os.Stdout)
  702. table.SetAutoWrapText(false)
  703. table.SetBorder(false)
  704. table.SetAlignment(tablewriter.ALIGN_LEFT)
  705. for _, v := range mainTableData {
  706. table.Append(v)
  707. }
  708. table.Render()
  709. }
  710. func renderSubTable(data [][]string, file bool) string {
  711. var buf bytes.Buffer
  712. table := tablewriter.NewWriter(&buf)
  713. table.SetAutoWrapText(!file)
  714. table.SetBorder(false)
  715. table.SetNoWhiteSpace(true)
  716. table.SetTablePadding("\t")
  717. table.SetAlignment(tablewriter.ALIGN_LEFT)
  718. for _, v := range data {
  719. table.Append(v)
  720. }
  721. table.Render()
  722. renderedTable := buf.String()
  723. lines := strings.Split(renderedTable, "\n")
  724. for i, line := range lines {
  725. lines[i] = "\t" + line
  726. }
  727. return strings.Join(lines, "\n")
  728. }
  729. func twoLines(s string) [][]string {
  730. lines := strings.Split(s, "\n")
  731. res := [][]string{}
  732. count := 0
  733. for _, line := range lines {
  734. line = strings.TrimSpace(line)
  735. if line != "" {
  736. count++
  737. res = append(res, []string{line})
  738. if count == 2 {
  739. return res
  740. }
  741. }
  742. }
  743. return res
  744. }
  745. func formatParams(s string) string {
  746. lines := strings.Split(s, "\n")
  747. table := [][]string{}
  748. for _, line := range lines {
  749. table = append(table, strings.Fields(line))
  750. }
  751. return renderSubTable(table, false)
  752. }
  753. func CopyHandler(cmd *cobra.Command, args []string) error {
  754. client, err := api.ClientFromEnvironment()
  755. if err != nil {
  756. return err
  757. }
  758. req := api.CopyRequest{Source: args[0], Destination: args[1]}
  759. if err := client.Copy(cmd.Context(), &req); err != nil {
  760. return err
  761. }
  762. fmt.Printf("copied '%s' to '%s'\n", args[0], args[1])
  763. return nil
  764. }
  765. func PullHandler(cmd *cobra.Command, args []string) error {
  766. insecure, err := cmd.Flags().GetBool("insecure")
  767. if err != nil {
  768. return err
  769. }
  770. client, err := api.ClientFromEnvironment()
  771. if err != nil {
  772. return err
  773. }
  774. p := progress.NewProgress(os.Stderr)
  775. defer p.Stop()
  776. bars := make(map[string]*progress.Bar)
  777. var status string
  778. var spinner *progress.Spinner
  779. fn := func(resp api.ProgressResponse) error {
  780. if resp.Digest != "" {
  781. if spinner != nil {
  782. spinner.Stop()
  783. }
  784. bar, ok := bars[resp.Digest]
  785. if !ok {
  786. bar = progress.NewBar(fmt.Sprintf("pulling %s...", resp.Digest[7:19]), resp.Total, resp.Completed)
  787. bars[resp.Digest] = bar
  788. p.Add(resp.Digest, bar)
  789. }
  790. bar.Set(resp.Completed)
  791. } else if status != resp.Status {
  792. if spinner != nil {
  793. spinner.Stop()
  794. }
  795. status = resp.Status
  796. spinner = progress.NewSpinner(status)
  797. p.Add(status, spinner)
  798. }
  799. return nil
  800. }
  801. request := api.PullRequest{Name: args[0], Insecure: insecure}
  802. if err := client.Pull(cmd.Context(), &request, fn); err != nil {
  803. return err
  804. }
  805. return nil
  806. }
  807. type generateContextKey string
  808. type runOptions struct {
  809. Model string
  810. ParentModel string
  811. Prompt string
  812. Messages []api.Message
  813. WordWrap bool
  814. Format string
  815. System string
  816. Images []api.ImageData
  817. Options map[string]interface{}
  818. MultiModal bool
  819. KeepAlive *api.Duration
  820. }
  821. type displayResponseState struct {
  822. lineLength int
  823. wordBuffer string
  824. }
  825. func displayResponse(content string, wordWrap bool, state *displayResponseState) {
  826. termWidth, _, _ := term.GetSize(int(os.Stdout.Fd()))
  827. if wordWrap && termWidth >= 10 {
  828. for _, ch := range content {
  829. if state.lineLength+1 > termWidth-5 {
  830. if runewidth.StringWidth(state.wordBuffer) > termWidth-10 {
  831. fmt.Printf("%s%c", state.wordBuffer, ch)
  832. state.wordBuffer = ""
  833. state.lineLength = 0
  834. continue
  835. }
  836. // backtrack the length of the last word and clear to the end of the line
  837. a := runewidth.StringWidth(state.wordBuffer)
  838. if a > 0 {
  839. fmt.Printf("\x1b[%dD", a)
  840. }
  841. fmt.Printf("\x1b[K\n")
  842. fmt.Printf("%s%c", state.wordBuffer, ch)
  843. chWidth := runewidth.RuneWidth(ch)
  844. state.lineLength = runewidth.StringWidth(state.wordBuffer) + chWidth
  845. } else {
  846. fmt.Print(string(ch))
  847. state.lineLength += runewidth.RuneWidth(ch)
  848. if runewidth.RuneWidth(ch) >= 2 {
  849. state.wordBuffer = ""
  850. continue
  851. }
  852. switch ch {
  853. case ' ':
  854. state.wordBuffer = ""
  855. case '\n':
  856. state.lineLength = 0
  857. default:
  858. state.wordBuffer += string(ch)
  859. }
  860. }
  861. }
  862. } else {
  863. fmt.Printf("%s%s", state.wordBuffer, content)
  864. if len(state.wordBuffer) > 0 {
  865. state.wordBuffer = ""
  866. }
  867. }
  868. }
  869. func chat(cmd *cobra.Command, opts runOptions) (*api.Message, error) {
  870. client, err := api.ClientFromEnvironment()
  871. if err != nil {
  872. return nil, err
  873. }
  874. p := progress.NewProgress(os.Stderr)
  875. defer p.StopAndClear()
  876. spinner := progress.NewSpinner("")
  877. p.Add("", spinner)
  878. cancelCtx, cancel := context.WithCancel(cmd.Context())
  879. defer cancel()
  880. sigChan := make(chan os.Signal, 1)
  881. signal.Notify(sigChan, syscall.SIGINT)
  882. go func() {
  883. <-sigChan
  884. cancel()
  885. }()
  886. var state *displayResponseState = &displayResponseState{}
  887. var latest api.ChatResponse
  888. var fullResponse strings.Builder
  889. var role string
  890. fn := func(response api.ChatResponse) error {
  891. p.StopAndClear()
  892. latest = response
  893. role = response.Message.Role
  894. content := response.Message.Content
  895. fullResponse.WriteString(content)
  896. displayResponse(content, opts.WordWrap, state)
  897. return nil
  898. }
  899. req := &api.ChatRequest{
  900. Model: opts.Model,
  901. Messages: opts.Messages,
  902. Format: opts.Format,
  903. Options: opts.Options,
  904. }
  905. if opts.KeepAlive != nil {
  906. req.KeepAlive = opts.KeepAlive
  907. }
  908. if err := client.Chat(cancelCtx, req, fn); err != nil {
  909. if errors.Is(err, context.Canceled) {
  910. return nil, nil
  911. }
  912. return nil, err
  913. }
  914. if len(opts.Messages) > 0 {
  915. fmt.Println()
  916. fmt.Println()
  917. }
  918. verbose, err := cmd.Flags().GetBool("verbose")
  919. if err != nil {
  920. return nil, err
  921. }
  922. if verbose {
  923. latest.Summary()
  924. }
  925. return &api.Message{Role: role, Content: fullResponse.String()}, nil
  926. }
  927. func generate(cmd *cobra.Command, opts runOptions) error {
  928. client, err := api.ClientFromEnvironment()
  929. if err != nil {
  930. return err
  931. }
  932. p := progress.NewProgress(os.Stderr)
  933. defer p.StopAndClear()
  934. spinner := progress.NewSpinner("")
  935. p.Add("", spinner)
  936. var latest api.GenerateResponse
  937. generateContext, ok := cmd.Context().Value(generateContextKey("context")).([]int)
  938. if !ok {
  939. generateContext = []int{}
  940. }
  941. ctx, cancel := context.WithCancel(cmd.Context())
  942. defer cancel()
  943. sigChan := make(chan os.Signal, 1)
  944. signal.Notify(sigChan, syscall.SIGINT)
  945. go func() {
  946. <-sigChan
  947. cancel()
  948. }()
  949. var state *displayResponseState = &displayResponseState{}
  950. fn := func(response api.GenerateResponse) error {
  951. p.StopAndClear()
  952. latest = response
  953. content := response.Response
  954. displayResponse(content, opts.WordWrap, state)
  955. return nil
  956. }
  957. if opts.MultiModal {
  958. opts.Prompt, opts.Images, err = extractFileData(opts.Prompt)
  959. if err != nil {
  960. return err
  961. }
  962. }
  963. request := api.GenerateRequest{
  964. Model: opts.Model,
  965. Prompt: opts.Prompt,
  966. Context: generateContext,
  967. Images: opts.Images,
  968. Format: opts.Format,
  969. System: opts.System,
  970. Options: opts.Options,
  971. KeepAlive: opts.KeepAlive,
  972. }
  973. if err := client.Generate(ctx, &request, fn); err != nil {
  974. if errors.Is(err, context.Canceled) {
  975. return nil
  976. }
  977. return err
  978. }
  979. if opts.Prompt != "" {
  980. fmt.Println()
  981. fmt.Println()
  982. }
  983. if !latest.Done {
  984. return nil
  985. }
  986. verbose, err := cmd.Flags().GetBool("verbose")
  987. if err != nil {
  988. return err
  989. }
  990. if verbose {
  991. latest.Summary()
  992. }
  993. ctx = context.WithValue(cmd.Context(), generateContextKey("context"), latest.Context)
  994. cmd.SetContext(ctx)
  995. return nil
  996. }
  997. func RunServer(cmd *cobra.Command, _ []string) error {
  998. if _, err := auth.GetPublicKey(); err != nil {
  999. return err
  1000. }
  1001. ln, err := net.Listen("tcp", net.JoinHostPort(envconfig.Host.Host, envconfig.Host.Port))
  1002. if err != nil {
  1003. return err
  1004. }
  1005. err = server.Serve(ln)
  1006. if errors.Is(err, http.ErrServerClosed) {
  1007. return nil
  1008. }
  1009. return err
  1010. }
  1011. func checkServerHeartbeat(cmd *cobra.Command, _ []string) error {
  1012. client, err := api.ClientFromEnvironment()
  1013. if err != nil {
  1014. return err
  1015. }
  1016. if err := client.Heartbeat(cmd.Context()); err != nil {
  1017. if !strings.Contains(err.Error(), " refused") {
  1018. return err
  1019. }
  1020. if err := startApp(cmd.Context(), client); err != nil {
  1021. return fmt.Errorf("could not connect to ollama app, is it running?")
  1022. }
  1023. }
  1024. return nil
  1025. }
  1026. func versionHandler(cmd *cobra.Command, _ []string) {
  1027. client, err := api.ClientFromEnvironment()
  1028. if err != nil {
  1029. return
  1030. }
  1031. serverVersion, err := client.Version(cmd.Context())
  1032. if err != nil {
  1033. fmt.Println("Warning: could not connect to a running Ollama instance")
  1034. }
  1035. if serverVersion != "" {
  1036. fmt.Printf("ollama version is %s\n", serverVersion)
  1037. }
  1038. if serverVersion != version.Version {
  1039. fmt.Printf("Warning: client version is %s\n", version.Version)
  1040. }
  1041. }
  1042. func appendEnvDocs(cmd *cobra.Command, envs []envconfig.EnvVar) {
  1043. if len(envs) == 0 {
  1044. return
  1045. }
  1046. envUsage := `
  1047. Environment Variables:
  1048. `
  1049. for _, e := range envs {
  1050. envUsage += fmt.Sprintf(" %-24s %s\n", e.Name, e.Description)
  1051. }
  1052. cmd.SetUsageTemplate(cmd.UsageTemplate() + envUsage)
  1053. }
  1054. func NewCLI() *cobra.Command {
  1055. log.SetFlags(log.LstdFlags | log.Lshortfile)
  1056. cobra.EnableCommandSorting = false
  1057. if runtime.GOOS == "windows" {
  1058. console.ConsoleFromFile(os.Stdin) //nolint:errcheck
  1059. }
  1060. rootCmd := &cobra.Command{
  1061. Use: "ollama",
  1062. Short: "Large language model runner",
  1063. SilenceUsage: true,
  1064. SilenceErrors: true,
  1065. CompletionOptions: cobra.CompletionOptions{
  1066. DisableDefaultCmd: true,
  1067. },
  1068. Run: func(cmd *cobra.Command, args []string) {
  1069. if version, _ := cmd.Flags().GetBool("version"); version {
  1070. versionHandler(cmd, args)
  1071. return
  1072. }
  1073. cmd.Print(cmd.UsageString())
  1074. },
  1075. }
  1076. rootCmd.Flags().BoolP("version", "v", false, "Show version information")
  1077. createCmd := &cobra.Command{
  1078. Use: "create MODEL",
  1079. Short: "Create a model from a Modelfile",
  1080. Args: cobra.ExactArgs(1),
  1081. PreRunE: checkServerHeartbeat,
  1082. RunE: CreateHandler,
  1083. }
  1084. createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile")
  1085. createCmd.Flags().StringP("quantize", "q", "", "Quantize model to this level (e.g. q4_0)")
  1086. showCmd := &cobra.Command{
  1087. Use: "show MODEL",
  1088. Short: "Show information for a model",
  1089. Args: cobra.ExactArgs(1),
  1090. PreRunE: checkServerHeartbeat,
  1091. RunE: ShowHandler,
  1092. }
  1093. showCmd.Flags().Bool("license", false, "Show license of a model")
  1094. showCmd.Flags().Bool("modelfile", false, "Show Modelfile of a model")
  1095. showCmd.Flags().Bool("parameters", false, "Show parameters of a model")
  1096. showCmd.Flags().Bool("template", false, "Show template of a model")
  1097. showCmd.Flags().Bool("system", false, "Show system message of a model")
  1098. runCmd := &cobra.Command{
  1099. Use: "run MODEL [PROMPT]",
  1100. Short: "Run a model",
  1101. Args: cobra.MinimumNArgs(1),
  1102. PreRunE: checkServerHeartbeat,
  1103. RunE: RunHandler,
  1104. }
  1105. runCmd.Flags().String("keepalive", "", "Duration to keep a model loaded (e.g. 5m)")
  1106. runCmd.Flags().Bool("verbose", false, "Show timings for response")
  1107. runCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  1108. runCmd.Flags().Bool("nowordwrap", false, "Don't wrap words to the next line automatically")
  1109. runCmd.Flags().String("format", "", "Response format (e.g. json)")
  1110. serveCmd := &cobra.Command{
  1111. Use: "serve",
  1112. Aliases: []string{"start"},
  1113. Short: "Start ollama",
  1114. Args: cobra.ExactArgs(0),
  1115. RunE: RunServer,
  1116. }
  1117. pullCmd := &cobra.Command{
  1118. Use: "pull MODEL",
  1119. Short: "Pull a model from a registry",
  1120. Args: cobra.ExactArgs(1),
  1121. PreRunE: checkServerHeartbeat,
  1122. RunE: PullHandler,
  1123. }
  1124. pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  1125. pushCmd := &cobra.Command{
  1126. Use: "push MODEL",
  1127. Short: "Push a model to a registry",
  1128. Args: cobra.ExactArgs(1),
  1129. PreRunE: checkServerHeartbeat,
  1130. RunE: PushHandler,
  1131. }
  1132. pushCmd.Flags().Bool("insecure", false, "Use an insecure registry")
  1133. listCmd := &cobra.Command{
  1134. Use: "list",
  1135. Aliases: []string{"ls"},
  1136. Short: "List models",
  1137. PreRunE: checkServerHeartbeat,
  1138. RunE: ListHandler,
  1139. }
  1140. psCmd := &cobra.Command{
  1141. Use: "ps",
  1142. Short: "List running models",
  1143. PreRunE: checkServerHeartbeat,
  1144. RunE: ListRunningHandler,
  1145. }
  1146. copyCmd := &cobra.Command{
  1147. Use: "cp SOURCE DESTINATION",
  1148. Short: "Copy a model",
  1149. Args: cobra.ExactArgs(2),
  1150. PreRunE: checkServerHeartbeat,
  1151. RunE: CopyHandler,
  1152. }
  1153. deleteCmd := &cobra.Command{
  1154. Use: "rm MODEL [MODEL...]",
  1155. Short: "Remove a model",
  1156. Args: cobra.MinimumNArgs(1),
  1157. PreRunE: checkServerHeartbeat,
  1158. RunE: DeleteHandler,
  1159. }
  1160. envVars := envconfig.AsMap()
  1161. envs := []envconfig.EnvVar{envVars["OLLAMA_HOST"]}
  1162. for _, cmd := range []*cobra.Command{
  1163. createCmd,
  1164. showCmd,
  1165. runCmd,
  1166. pullCmd,
  1167. pushCmd,
  1168. listCmd,
  1169. psCmd,
  1170. copyCmd,
  1171. deleteCmd,
  1172. serveCmd,
  1173. } {
  1174. switch cmd {
  1175. case runCmd:
  1176. appendEnvDocs(cmd, []envconfig.EnvVar{envVars["OLLAMA_HOST"], envVars["OLLAMA_NOHISTORY"]})
  1177. case serveCmd:
  1178. appendEnvDocs(cmd, []envconfig.EnvVar{
  1179. envVars["OLLAMA_DEBUG"],
  1180. envVars["OLLAMA_HOST"],
  1181. envVars["OLLAMA_KEEP_ALIVE"],
  1182. envVars["OLLAMA_MAX_LOADED_MODELS"],
  1183. envVars["OLLAMA_MAX_QUEUE"],
  1184. envVars["OLLAMA_MODELS"],
  1185. envVars["OLLAMA_NUM_PARALLEL"],
  1186. envVars["OLLAMA_NOPRUNE"],
  1187. envVars["OLLAMA_ORIGINS"],
  1188. envVars["OLLAMA_TMPDIR"],
  1189. envVars["OLLAMA_FLASH_ATTENTION"],
  1190. envVars["OLLAMA_LLM_LIBRARY"],
  1191. })
  1192. default:
  1193. appendEnvDocs(cmd, envs)
  1194. }
  1195. }
  1196. rootCmd.AddCommand(
  1197. serveCmd,
  1198. createCmd,
  1199. showCmd,
  1200. runCmd,
  1201. pullCmd,
  1202. pushCmd,
  1203. listCmd,
  1204. psCmd,
  1205. copyCmd,
  1206. deleteCmd,
  1207. )
  1208. return rootCmd
  1209. }