Browse Source

auto updater for macos

Jeffrey Morgan 1 year ago
parent
commit
7bf3212c5b
4 changed files with 307 additions and 4 deletions
  1. 3 1
      app/src/index.ts
  2. 40 0
      web/app/api/update/route.ts
  3. 256 2
      web/package-lock.json
  4. 8 1
      web/package.json

+ 3 - 1
app/src/index.ts

@@ -82,7 +82,9 @@ app.on('activate', () => {
 
 
 // In this file you can include the rest of your app's specific main process
 // In this file you can include the rest of your app's specific main process
 // code. You can also put them in separate files and import them here.
 // code. You can also put them in separate files and import them here.
-autoUpdater.setFeedURL({ url: `https://updates.ollama.ai/update/${process.platform}/${app.getVersion()}` })
+autoUpdater.setFeedURL({
+  url: `https://ollama.ai/api/update?os=${process.platform}&arch=${process.arch}&version=${app.getVersion()}`,
+})
 
 
 autoUpdater.checkForUpdates()
 autoUpdater.checkForUpdates()
 setInterval(() => {
 setInterval(() => {

+ 40 - 0
web/app/api/update/route.ts

@@ -0,0 +1,40 @@
+import { NextResponse } from 'next/server'
+import semver from 'semver'
+import { Octokit } from '@octokit/rest'
+import { RequestError } from '@octokit/types'
+
+const octokit = new Octokit()
+
+export async function GET(req: Request) {
+  const { searchParams } = new URL(req.url)
+
+  const os = searchParams.get('os') || ''
+  const version = searchParams.get('version') || ''
+
+  try {
+    const { data } = await octokit.repos.getLatestRelease({
+      owner: 'jmorganca',
+      repo: 'ollama',
+    })
+
+    // todo: get the correct asset for the current arch/os
+    const asset = data.assets.find(a => a.name.toLowerCase().includes(os))
+
+    if (!asset) {
+      return new Response('up to date', { status: 204 })
+    }
+
+    if (semver.lt(version, data.tag_name)) {
+      return NextResponse.json({ version: data.tag_name, url: asset.browser_download_url })
+    }
+  } catch (error) {
+    const e = error as RequestError
+    if (e.status === 404) {
+      return new Response('not found', { status: 404 })
+    }
+
+    return new Response('internal server error', { status: 500 })
+  }
+
+  return new Response('up to date', { status: 204 })
+}

+ 256 - 2
web/package-lock.json

@@ -8,17 +8,24 @@
       "name": "web",
       "name": "web",
       "version": "0.1.0",
       "version": "0.1.0",
       "dependencies": {
       "dependencies": {
+        "@octokit/rest": "^19.0.13",
         "@types/node": "20.4.0",
         "@types/node": "20.4.0",
         "@types/react": "18.2.14",
         "@types/react": "18.2.14",
         "@types/react-dom": "18.2.6",
         "@types/react-dom": "18.2.6",
         "autoprefixer": "10.4.14",
         "autoprefixer": "10.4.14",
+        "encoding": "^0.1.13",
         "eslint": "8.44.0",
         "eslint": "8.44.0",
         "eslint-config-next": "13.4.7",
         "eslint-config-next": "13.4.7",
         "next": "13.4.7",
         "next": "13.4.7",
         "postcss": "8.4.24",
         "postcss": "8.4.24",
         "react": "18.2.0",
         "react": "18.2.0",
         "react-dom": "18.2.0",
         "react-dom": "18.2.0",
-        "tailwindcss": "3.3.2"
+        "semver": "^7.5.3",
+        "tailwindcss": "3.3.2",
+        "typescript": "5.1.6"
+      },
+      "devDependencies": {
+        "@types/semver": "^7.5.0"
       }
       }
     },
     },
     "node_modules/@aashutoshrathi/word-wrap": {
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -361,6 +368,163 @@
         "node": ">= 8"
         "node": ">= 8"
       }
       }
     },
     },
+    "node_modules/@octokit/auth-token": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz",
+      "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/core": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz",
+      "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==",
+      "dependencies": {
+        "@octokit/auth-token": "^3.0.0",
+        "@octokit/graphql": "^5.0.0",
+        "@octokit/request": "^6.0.0",
+        "@octokit/request-error": "^3.0.0",
+        "@octokit/types": "^9.0.0",
+        "before-after-hook": "^2.2.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/endpoint": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz",
+      "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==",
+      "dependencies": {
+        "@octokit/types": "^9.0.0",
+        "is-plain-object": "^5.0.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/graphql": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz",
+      "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==",
+      "dependencies": {
+        "@octokit/request": "^6.0.0",
+        "@octokit/types": "^9.0.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/openapi-types": {
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
+      "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
+    },
+    "node_modules/@octokit/plugin-paginate-rest": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz",
+      "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==",
+      "dependencies": {
+        "@octokit/tsconfig": "^1.0.2",
+        "@octokit/types": "^9.2.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "@octokit/core": ">=4"
+      }
+    },
+    "node_modules/@octokit/plugin-request-log": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
+      "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
+      "peerDependencies": {
+        "@octokit/core": ">=3"
+      }
+    },
+    "node_modules/@octokit/plugin-rest-endpoint-methods": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz",
+      "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==",
+      "dependencies": {
+        "@octokit/types": "^10.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "@octokit/core": ">=3"
+      }
+    },
+    "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz",
+      "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==",
+      "dependencies": {
+        "@octokit/openapi-types": "^18.0.0"
+      }
+    },
+    "node_modules/@octokit/request": {
+      "version": "6.2.8",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz",
+      "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==",
+      "dependencies": {
+        "@octokit/endpoint": "^7.0.0",
+        "@octokit/request-error": "^3.0.0",
+        "@octokit/types": "^9.0.0",
+        "is-plain-object": "^5.0.0",
+        "node-fetch": "^2.6.7",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/request-error": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
+      "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
+      "dependencies": {
+        "@octokit/types": "^9.0.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/rest": {
+      "version": "19.0.13",
+      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz",
+      "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==",
+      "dependencies": {
+        "@octokit/core": "^4.2.1",
+        "@octokit/plugin-paginate-rest": "^6.1.2",
+        "@octokit/plugin-request-log": "^1.0.4",
+        "@octokit/plugin-rest-endpoint-methods": "^7.1.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@octokit/tsconfig": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz",
+      "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA=="
+    },
+    "node_modules/@octokit/types": {
+      "version": "9.3.2",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz",
+      "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==",
+      "dependencies": {
+        "@octokit/openapi-types": "^18.0.0"
+      }
+    },
     "node_modules/@pkgr/utils": {
     "node_modules/@pkgr/utils": {
       "version": "2.4.1",
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
       "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
@@ -431,6 +595,12 @@
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
     },
     },
+    "node_modules/@types/semver": {
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
+      "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
+      "dev": true
+    },
     "node_modules/@typescript-eslint/parser": {
     "node_modules/@typescript-eslint/parser": {
       "version": "5.60.1",
       "version": "5.60.1",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz",
@@ -771,6 +941,11 @@
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
     },
+    "node_modules/before-after-hook": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
+    },
     "node_modules/big-integer": {
     "node_modules/big-integer": {
       "version": "1.6.51",
       "version": "1.6.51",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
@@ -1120,6 +1295,11 @@
         "url": "https://github.com/sponsors/ljharb"
         "url": "https://github.com/sponsors/ljharb"
       }
       }
     },
     },
+    "node_modules/deprecation": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
+    },
     "node_modules/dequal": {
     "node_modules/dequal": {
       "version": "2.0.3",
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
       "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -1170,6 +1350,14 @@
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
     },
     },
+    "node_modules/encoding": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+      "dependencies": {
+        "iconv-lite": "^0.6.2"
+      }
+    },
     "node_modules/enhanced-resolve": {
     "node_modules/enhanced-resolve": {
       "version": "5.15.0",
       "version": "5.15.0",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
@@ -2136,6 +2324,17 @@
         "node": ">=14.18.0"
         "node": ">=14.18.0"
       }
       }
     },
     },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/ignore": {
     "node_modules/ignore": {
       "version": "5.2.4",
       "version": "5.2.4",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -2371,6 +2570,14 @@
         "node": ">=8"
         "node": ">=8"
       }
       }
     },
     },
+    "node_modules/is-plain-object": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-regex": {
     "node_modules/is-regex": {
       "version": "1.1.4",
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -2798,6 +3005,25 @@
         "node": "^10 || ^12 || >=14"
         "node": "^10 || ^12 || >=14"
       }
       }
     },
     },
+    "node_modules/node-fetch": {
+      "version": "2.6.12",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
+      "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/node-releases": {
     "node_modules/node-releases": {
       "version": "2.0.12",
       "version": "2.0.12",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
@@ -3536,6 +3762,11 @@
         "url": "https://github.com/sponsors/ljharb"
         "url": "https://github.com/sponsors/ljharb"
       }
       }
     },
     },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
     "node_modules/scheduler": {
     "node_modules/scheduler": {
       "version": "0.23.0",
       "version": "0.23.0",
       "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
       "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -3910,6 +4141,11 @@
         "node": ">=8.0"
         "node": ">=8.0"
       }
       }
     },
     },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
     "node_modules/ts-interface-checker": {
     "node_modules/ts-interface-checker": {
       "version": "0.1.13",
       "version": "0.1.13",
       "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
       "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -3989,7 +4225,6 @@
       "version": "5.1.6",
       "version": "5.1.6",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
       "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
       "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
-      "peer": true,
       "bin": {
       "bin": {
         "tsc": "bin/tsc",
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
         "tsserver": "bin/tsserver"
@@ -4012,6 +4247,11 @@
         "url": "https://github.com/sponsors/ljharb"
         "url": "https://github.com/sponsors/ljharb"
       }
       }
     },
     },
+    "node_modules/universal-user-agent": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
+      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
+    },
     "node_modules/untildify": {
     "node_modules/untildify": {
       "version": "4.0.0",
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
       "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
@@ -4074,6 +4314,20 @@
         "node": ">=10.13.0"
         "node": ">=10.13.0"
       }
       }
     },
     },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "node_modules/which": {
     "node_modules/which": {
       "version": "2.0.2",
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

+ 8 - 1
web/package.json

@@ -8,16 +8,23 @@
     "lint": "next lint"
     "lint": "next lint"
   },
   },
   "dependencies": {
   "dependencies": {
+    "@octokit/rest": "^19.0.13",
     "@types/node": "20.4.0",
     "@types/node": "20.4.0",
     "@types/react": "18.2.14",
     "@types/react": "18.2.14",
     "@types/react-dom": "18.2.6",
     "@types/react-dom": "18.2.6",
     "autoprefixer": "10.4.14",
     "autoprefixer": "10.4.14",
+    "encoding": "^0.1.13",
     "eslint": "8.44.0",
     "eslint": "8.44.0",
     "eslint-config-next": "13.4.7",
     "eslint-config-next": "13.4.7",
     "next": "13.4.7",
     "next": "13.4.7",
     "postcss": "8.4.24",
     "postcss": "8.4.24",
     "react": "18.2.0",
     "react": "18.2.0",
     "react-dom": "18.2.0",
     "react-dom": "18.2.0",
-    "tailwindcss": "3.3.2"
+    "semver": "^7.5.3",
+    "tailwindcss": "3.3.2",
+    "typescript": "5.1.6"
+  },
+  "devDependencies": {
+    "@types/semver": "^7.5.0"
   }
   }
 }
 }