{"action":"created","comment":{"url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/comments/46710182","id":46710182,"diff_hunk":"@@ -0,0 +1,362 @@\n+/// <reference path='../../../../third_party/typings/node/node.d.ts' />\n+/// <reference path='../../../../third_party/typings/es6-promise/es6-promise.d.ts' />\n+/// <reference path='../../../../third_party/typings/freedom/freedom-module-env.d.ts' />\n+\n+const forge = require(\"forge-min\");\n+\n+const POLL_TIMEOUT: number = 5000; //milliseconds\n+\n+const STATUS_CODES: { [k: string]: string; } = {\n+  \"START\": \"Starting provisioner\",\n+  \"OAUTH_INIT\": \"Initializing oauth flow\",\n+  \"OAUTH_ERROR\": \"Error getting oauth token\",\n+  \"OAUTH_COMPLETE\": \"Got oauth token\",\n+  \"SSHKEY_RETRIEVED\": \"Retrieved SSH keys from storage\",\n+  \"SSHKEY_GENERATED\": \"Generated new SSH keys\",\n+  \"CLOUD_FAILED\": \"Failed to complete cloud operation\",\n+  \"CLOUD_INIT_ADDKEY\": \"Starting to add SSH key to cloud account\",\n+  \"CLOUD_DONE_ADDKEY\": \"Done adding SSH key to cloud account\",\n+  \"CLOUD_INIT_VM\": \"Starting to provision VM\",\n+  \"CLOUD_DONE_VM\": \"Done provisioning VMs\",\n+  \"CLOUD_WAITING_VM\": \"Waiting on VM\",\n+};\n+\n+const ERR_CODES: { [k: string]: string; } = {\n+  \"VM_DNE\": \"VM does not exist\",\n+  \"CLOUD_ERR\": \"Error from cloud provider\"\n+};\n+\n+const REDIRECT_URIS: [string] = [\n+  \"https://pjpcdnccaekokkkeheolmpkfifcbibnj.chromiumapp.org\"\n+  //  \"http://localhost:10101\"\n+];\n+\n+interface KeyPair {\n+  private: string;\n+  public: string;\n+}\n+\n+class Provisioner {\n+  private dispatch: Function;\n+  private state: any;\n+\n+  constructor(dispatchEvent: Function, state: any) {\n+    this.dispatch = dispatchEvent;\n+    this.state = state;\n+  }\n+\n+  /**\n+   * One-click setup of a VM\n+   * See freedom-module.json for return and error types\n+   * @param {String} name of VM to create\n+   * @return {Promise.<Object>}\n+   */\n+  public start = (name: string) : Promise<Object> => {\n+    this.sendStatus_(\"START\");\n+    // Do oAuth\n+    return this.doOAuth_().then((oauthObj: any) => {\n+      this.state.oauth = oauthObj;\n+      return this.getSshKey_(name);\n+    // Get SSH keys\n+    }).then((keys: KeyPair) => {\n+      this.state.ssh = keys;\n+      return this.setupDigitalOcean_(name);\n+    // Setup Digital Ocean (SSH key + droplet)\n+    }).then((actions: any) => {\n+      //console.log(actions);\n+      return this.doRequest_(\"GET\", \"droplets/\" + this.state.cloud.vm.id);\n+    // Get the droplet's configuration\n+    }).then((resp: any) => {\n+      this.sendStatus_(\"CLOUD_DONE_VM\");\n+      this.state.cloud.vm = resp.droplet;\n+      this.state.network.ssh_port = 22;\n+      // Retrieve public IPv4 address\n+      for (var i = 0; i < resp.droplet.networks.v4.length; i++) {\n+        if (resp.droplet.networks.v4[i].type === \"public\") {\n+          this.state.network['ipv4'] = resp.droplet.networks.v4[i].ip_address;\n+        }\n+      }\n+      // Retrieve public IPv6 address\n+      for (var i = 0; i < resp.droplet.networks.v6.length; i++) {\n+        if (resp.droplet.networks.v6[i].type === \"public\") {\n+          this.state.network['ipv6'] = resp.droplet.networks.v6[i].ip_address;\n+        }\n+      }\n+      console.log(this.state);\n+      return this.state;\n+    });\n+  }\n+\n+  /**\n+   * One-click destruction of a VM\n+   * See freedom-module.json for return and error types\n+   * @todo currently doesnt wait for destroy to complete before resolving\n+   * @param {String} name of VM to create\n+   * @return {Promise.<Object>}\n+   */\n+  public stop = (name: string) : Promise<Object> => {\n+    return this.doRequest_(\"GET\", \"droplets\").then((resp: any) => {\n+      for (var i = 0; i < resp.droplets.length; i++) {\n+        if (resp.droplets[i].name === name) {\n+          return Promise.resolve({\n+            droplet: resp.droplets[i]\n+          });\n+        }\n+      }\n+      return Promise.reject({\n+        \"errcode\": \"VM_DNE\",\n+        \"message\": \"Droplet with name,\" + name + \", doesnt exist\"\n+      });\n+    }).then((resp: { droplet: { id: string } } ) => {\n+      return this.doRequest_(\"DELETE\", \"droplets/\" + resp.droplet.id);\n+    });\n+  }\n+\n+  /**\n+   * Generates an RSA keypair using forge\n+   * @return {KeyPair} public and private SSH keys\n+   */\n+  private static generateKeyPair_ = () : KeyPair => {\n+    \"use strict\";\n+    var pair = forge.pki.rsa.generateKeyPair({bits: 2048, e: 0x10001});\n+    var publicKey = forge.ssh.publicKeyToOpenSSH(pair.publicKey, '');\n+    var privateKey = forge.ssh.privateKeyToOpenSSH(pair.privateKey, '');\n+    return { public: publicKey, private: privateKey };\n+  }\n+\n+  /**\n+   * Dispatches status events\n+   * events listed in STATUS_CODES\n+   * @param {String} code one of STATUS_CODES \n+   */\n+  private sendStatus_ = (code: string): void => {\n+    this.dispatch(\"status\", {\n+      \"code\": code,\n+      \"message\": STATUS_CODES[code]\n+    });\n+  }\n+\n+  /**\n+   * Initiates a Digital Ocean oAuth flow\n+   * @return {Promise.<Object>} oAuth response from Digital Ocean\n+   *  {\n+   *    access_token: \"..\",\n+   *    expires_in: \"..\",\n+   *    state: \"..\",\n+   *    token_type: \"..\"\n+   *  }\n+   */\n+  private doOAuth_ = () : Promise<Object> => {\n+    return new Promise((resolve: Function, reject: Function) => {\n+      var oauth = freedom[\"core.oauth\"]();\n+      this.sendStatus_(\"OAUTH_INIT\");\n+      oauth.initiateOAuth(REDIRECT_URIS).then((obj: any) => {\n+        var url = \"https://cloud.digitalocean.com/v1/oauth/authorize?\" +\n+                  \"client_id=c16837b5448cd6cf2582d2c2f767cfb7d11844ec395a91b43f26ca72513416c8&\" +\n+                  \"response_type=token&\" +\n+                  \"redirect_uri=\" + encodeURIComponent(obj.redirect) + \"&\" +\n+                  \"state=\" + encodeURIComponent(obj.state) + \"&\" +\n+                  \"scope=read%20write\";\n+        return oauth.launchAuthFlow(url, obj);\n+      }).then((responseUrl: string) => {\n+        var query = responseUrl.substr(responseUrl.indexOf('#') + 1),\n+            param: string,\n+            params: { [k: string]: string },\n+            keys = query.split('&'),\n+            i = 0;\n+        for (i = 0; i < keys.length; i++) {\n+          param = keys[i].substr(0, keys[i].indexOf('='));\n+          params[param] = keys[i].substr(keys[i].indexOf('=') + 1);\n+        }\n+        this.sendStatus_(\"OAUTH_COMPLETE\");\n+        resolve(params);\n+      }).catch((err: Error) => {\n+        console.error(\"oauth error: \" + JSON.stringify(err));\n+        this.sendStatus_(\"OAUTH_ERROR\");\n+        reject(err);\n+      });\n+    });\n+  }\n+\n+  /**\n+   * Try to retrieve SSH keys from storage.\n+   * If not found, generate new ones and store\n+   * @param {String} name name of the key (usually same as name of VM later)\n+   * @return {Promise.<Object>} the SSH keys retrieved\n+   * {\n+   *    public: \"...\",\n+   *    private: \"...\"\n+   * }\n+   */\n+  private getSshKey_ = (name: string) : Promise<Object> => {\n+    var storage = freedom[\"core.storage\"]();\n+    return new Promise((resolve: Function, reject: Function) => {","path":"src/cloud/provision/freedom-module.ts","position":193,"original_position":193,"commit_id":"602d458a4221ddefe6e4f830f3a7ad465921919d","original_commit_id":"602d458a4221ddefe6e4f830f3a7ad465921919d","user":{"login":"trevj","id":5142116,"avatar_url":"https://avatars.githubusercontent.com/u/5142116?v=3","gravatar_id":"","url":"https://api.github.com/users/trevj","html_url":"https://github.com/trevj","followers_url":"https://api.github.com/users/trevj/followers","following_url":"https://api.github.com/users/trevj/following{/other_user}","gists_url":"https://api.github.com/users/trevj/gists{/gist_id}","starred_url":"https://api.github.com/users/trevj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trevj/subscriptions","organizations_url":"https://api.github.com/users/trevj/orgs","repos_url":"https://api.github.com/users/trevj/repos","events_url":"https://api.github.com/users/trevj/events{/privacy}","received_events_url":"https://api.github.com/users/trevj/received_events","type":"User","site_admin":false},"body":"Again, ```new Promise((F, R) => {``` should work here.","created_at":"2015-12-04T17:51:47Z","updated_at":"2015-12-04T17:51:47Z","html_url":"https://github.com/uProxy/uproxy-lib/pull/331#discussion_r46710182","pull_request_url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331","_links":{"self":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/comments/46710182"},"html":{"href":"https://github.com/uProxy/uproxy-lib/pull/331#discussion_r46710182"},"pull_request":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331"}}},"pull_request":{"url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331","id":52661018,"html_url":"https://github.com/uProxy/uproxy-lib/pull/331","diff_url":"https://github.com/uProxy/uproxy-lib/pull/331.diff","patch_url":"https://github.com/uProxy/uproxy-lib/pull/331.patch","issue_url":"https://api.github.com/repos/uProxy/uproxy-lib/issues/331","number":331,"state":"open","locked":false,"title":"Translated cloud provision freedom module to TypeScript","user":{"login":"jamesliu96","id":2211002,"avatar_url":"https://avatars.githubusercontent.com/u/2211002?v=3","gravatar_id":"","url":"https://api.github.com/users/jamesliu96","html_url":"https://github.com/jamesliu96","followers_url":"https://api.github.com/users/jamesliu96/followers","following_url":"https://api.github.com/users/jamesliu96/following{/other_user}","gists_url":"https://api.github.com/users/jamesliu96/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesliu96/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesliu96/subscriptions","organizations_url":"https://api.github.com/users/jamesliu96/orgs","repos_url":"https://api.github.com/users/jamesliu96/repos","events_url":"https://api.github.com/users/jamesliu96/events{/privacy}","received_events_url":"https://api.github.com/users/jamesliu96/received_events","type":"User","site_admin":false},"body":"Translated from JavaScript, some types are still not clear enough.\n\n<!-- Reviewable:start -->\n[<img src=\"https://reviewable.io/review_button.png\" height=40 alt=\"Review on Reviewable\"/>](https://reviewable.io/reviews/uproxy/uproxy-lib/331)\n<!-- Reviewable:end -->\n","created_at":"2015-12-04T12:00:47Z","updated_at":"2015-12-04T17:51:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8e60527e1e714429c893cc3ad1aa35fc43918be2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331/commits","review_comments_url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331/comments","review_comment_url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/comments{/number}","comments_url":"https://api.github.com/repos/uProxy/uproxy-lib/issues/331/comments","statuses_url":"https://api.github.com/repos/uProxy/uproxy-lib/statuses/602d458a4221ddefe6e4f830f3a7ad465921919d","head":{"label":"jamesliu96:dev","ref":"dev","sha":"602d458a4221ddefe6e4f830f3a7ad465921919d","user":{"login":"jamesliu96","id":2211002,"avatar_url":"https://avatars.githubusercontent.com/u/2211002?v=3","gravatar_id":"","url":"https://api.github.com/users/jamesliu96","html_url":"https://github.com/jamesliu96","followers_url":"https://api.github.com/users/jamesliu96/followers","following_url":"https://api.github.com/users/jamesliu96/following{/other_user}","gists_url":"https://api.github.com/users/jamesliu96/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesliu96/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesliu96/subscriptions","organizations_url":"https://api.github.com/users/jamesliu96/orgs","repos_url":"https://api.github.com/users/jamesliu96/repos","events_url":"https://api.github.com/users/jamesliu96/events{/privacy}","received_events_url":"https://api.github.com/users/jamesliu96/received_events","type":"User","site_admin":false},"repo":{"id":47147790,"name":"uproxy-lib","full_name":"jamesliu96/uproxy-lib","owner":{"login":"jamesliu96","id":2211002,"avatar_url":"https://avatars.githubusercontent.com/u/2211002?v=3","gravatar_id":"","url":"https://api.github.com/users/jamesliu96","html_url":"https://github.com/jamesliu96","followers_url":"https://api.github.com/users/jamesliu96/followers","following_url":"https://api.github.com/users/jamesliu96/following{/other_user}","gists_url":"https://api.github.com/users/jamesliu96/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesliu96/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesliu96/subscriptions","organizations_url":"https://api.github.com/users/jamesliu96/orgs","repos_url":"https://api.github.com/users/jamesliu96/repos","events_url":"https://api.github.com/users/jamesliu96/events{/privacy}","received_events_url":"https://api.github.com/users/jamesliu96/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jamesliu96/uproxy-lib","description":"SOCKS proxy and other utilities for uProxy.","fork":true,"url":"https://api.github.com/repos/jamesliu96/uproxy-lib","forks_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/forks","keys_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/teams","hooks_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/hooks","issue_events_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/issues/events{/number}","events_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/events","assignees_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/assignees{/user}","branches_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/branches{/branch}","tags_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/tags","blobs_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/git/refs{/sha}","trees_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/statuses/{sha}","languages_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/languages","stargazers_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/stargazers","contributors_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/contributors","subscribers_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/subscribers","subscription_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/subscription","commits_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/commits{/sha}","git_commits_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/git/commits{/sha}","comments_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/comments{/number}","issue_comment_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/issues/comments{/number}","contents_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/contents/{+path}","compare_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/merges","archive_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/downloads","issues_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/issues{/number}","pulls_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/pulls{/number}","milestones_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/milestones{/number}","notifications_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/labels{/name}","releases_url":"https://api.github.com/repos/jamesliu96/uproxy-lib/releases{/id}","created_at":"2015-11-30T21:42:13Z","updated_at":"2015-11-30T21:42:14Z","pushed_at":"2015-12-04T11:56:09Z","git_url":"git://github.com/jamesliu96/uproxy-lib.git","ssh_url":"git@github.com:jamesliu96/uproxy-lib.git","clone_url":"https://github.com/jamesliu96/uproxy-lib.git","svn_url":"https://github.com/jamesliu96/uproxy-lib","homepage":"","size":2009,"stargazers_count":0,"watchers_count":0,"language":"TypeScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"dev"}},"base":{"label":"uProxy:dev","ref":"dev","sha":"82f039ab926b8b1b51da433322ca64174ef1e9df","user":{"login":"uProxy","id":6548286,"avatar_url":"https://avatars.githubusercontent.com/u/6548286?v=3","gravatar_id":"","url":"https://api.github.com/users/uProxy","html_url":"https://github.com/uProxy","followers_url":"https://api.github.com/users/uProxy/followers","following_url":"https://api.github.com/users/uProxy/following{/other_user}","gists_url":"https://api.github.com/users/uProxy/gists{/gist_id}","starred_url":"https://api.github.com/users/uProxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/uProxy/subscriptions","organizations_url":"https://api.github.com/users/uProxy/orgs","repos_url":"https://api.github.com/users/uProxy/repos","events_url":"https://api.github.com/users/uProxy/events{/privacy}","received_events_url":"https://api.github.com/users/uProxy/received_events","type":"Organization","site_admin":false},"repo":{"id":18026324,"name":"uproxy-lib","full_name":"uProxy/uproxy-lib","owner":{"login":"uProxy","id":6548286,"avatar_url":"https://avatars.githubusercontent.com/u/6548286?v=3","gravatar_id":"","url":"https://api.github.com/users/uProxy","html_url":"https://github.com/uProxy","followers_url":"https://api.github.com/users/uProxy/followers","following_url":"https://api.github.com/users/uProxy/following{/other_user}","gists_url":"https://api.github.com/users/uProxy/gists{/gist_id}","starred_url":"https://api.github.com/users/uProxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/uProxy/subscriptions","organizations_url":"https://api.github.com/users/uProxy/orgs","repos_url":"https://api.github.com/users/uProxy/repos","events_url":"https://api.github.com/users/uProxy/events{/privacy}","received_events_url":"https://api.github.com/users/uProxy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/uProxy/uproxy-lib","description":"SOCKS proxy and other utilities for uProxy.","fork":false,"url":"https://api.github.com/repos/uProxy/uproxy-lib","forks_url":"https://api.github.com/repos/uProxy/uproxy-lib/forks","keys_url":"https://api.github.com/repos/uProxy/uproxy-lib/keys{/key_id}","collaborators_url":"https://api.github.com/repos/uProxy/uproxy-lib/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/uProxy/uproxy-lib/teams","hooks_url":"https://api.github.com/repos/uProxy/uproxy-lib/hooks","issue_events_url":"https://api.github.com/repos/uProxy/uproxy-lib/issues/events{/number}","events_url":"https://api.github.com/repos/uProxy/uproxy-lib/events","assignees_url":"https://api.github.com/repos/uProxy/uproxy-lib/assignees{/user}","branches_url":"https://api.github.com/repos/uProxy/uproxy-lib/branches{/branch}","tags_url":"https://api.github.com/repos/uProxy/uproxy-lib/tags","blobs_url":"https://api.github.com/repos/uProxy/uproxy-lib/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/uProxy/uproxy-lib/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/uProxy/uproxy-lib/git/refs{/sha}","trees_url":"https://api.github.com/repos/uProxy/uproxy-lib/git/trees{/sha}","statuses_url":"https://api.github.com/repos/uProxy/uproxy-lib/statuses/{sha}","languages_url":"https://api.github.com/repos/uProxy/uproxy-lib/languages","stargazers_url":"https://api.github.com/repos/uProxy/uproxy-lib/stargazers","contributors_url":"https://api.github.com/repos/uProxy/uproxy-lib/contributors","subscribers_url":"https://api.github.com/repos/uProxy/uproxy-lib/subscribers","subscription_url":"https://api.github.com/repos/uProxy/uproxy-lib/subscription","commits_url":"https://api.github.com/repos/uProxy/uproxy-lib/commits{/sha}","git_commits_url":"https://api.github.com/repos/uProxy/uproxy-lib/git/commits{/sha}","comments_url":"https://api.github.com/repos/uProxy/uproxy-lib/comments{/number}","issue_comment_url":"https://api.github.com/repos/uProxy/uproxy-lib/issues/comments{/number}","contents_url":"https://api.github.com/repos/uProxy/uproxy-lib/contents/{+path}","compare_url":"https://api.github.com/repos/uProxy/uproxy-lib/compare/{base}...{head}","merges_url":"https://api.github.com/repos/uProxy/uproxy-lib/merges","archive_url":"https://api.github.com/repos/uProxy/uproxy-lib/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/uProxy/uproxy-lib/downloads","issues_url":"https://api.github.com/repos/uProxy/uproxy-lib/issues{/number}","pulls_url":"https://api.github.com/repos/uProxy/uproxy-lib/pulls{/number}","milestones_url":"https://api.github.com/repos/uProxy/uproxy-lib/milestones{/number}","notifications_url":"https://api.github.com/repos/uProxy/uproxy-lib/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/uProxy/uproxy-lib/labels{/name}","releases_url":"https://api.github.com/repos/uProxy/uproxy-lib/releases{/id}","created_at":"2014-03-23T04:17:00Z","updated_at":"2015-10-19T11:10:25Z","pushed_at":"2015-12-04T15:50:15Z","git_url":"git://github.com/uProxy/uproxy-lib.git","ssh_url":"git@github.com:uProxy/uproxy-lib.git","clone_url":"https://github.com/uProxy/uproxy-lib.git","svn_url":"https://github.com/uProxy/uproxy-lib","homepage":"","size":2107,"stargazers_count":11,"watchers_count":11,"language":"TypeScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":8,"forks":3,"open_issues":8,"watchers":11,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331"},"html":{"href":"https://github.com/uProxy/uproxy-lib/pull/331"},"issue":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/issues/331"},"comments":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/issues/331/comments"},"review_comments":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331/comments"},"review_comment":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/pulls/331/commits"},"statuses":{"href":"https://api.github.com/repos/uProxy/uproxy-lib/statuses/602d458a4221ddefe6e4f830f3a7ad465921919d"}}}}