diff --git a/.github/workflows/update_protos.yml b/.github/workflows/update_protos.yml index 21cac1023..d58f22d61 100644 --- a/.github/workflows/update_protos.yml +++ b/.github/workflows/update_protos.yml @@ -20,6 +20,7 @@ jobs: - name: Update api run: | + echo ${{ github.event.client_payload.tag }} > api_version.lock sudo chown -R testbot . sudo -u testbot bash -lc 'make update-buf' diff --git a/.gitignore b/.gitignore index 9272fcde9..ea7fd584f 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,6 @@ bin/ # typedoc documentation docs/dist + +# build-time outputs +src/api-version.ts diff --git a/Makefile b/Makefile index b6e7ead61..7f01bcc2e 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ update-buf: $(node_modules) .PHONY: build-buf build-buf: $(node_modules) clean-buf $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/googleapis/googleapis) - $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/viamrobotics/api) --path common,component,robot,service,app,provisioning,tagger,stream + $(buf) generate buf.build/viamrobotics/api:$$(cat api_version.lock) --path common,component,robot,service,app,provisioning,tagger,stream $(buf) generate $$(./scripts/get-buf-lock-version.js buf.build/viamrobotics/goutils) # js targets diff --git a/api_version.lock b/api_version.lock new file mode 100644 index 000000000..1bb9d2dab --- /dev/null +++ b/api_version.lock @@ -0,0 +1 @@ +v0.1.330 diff --git a/buf.lock b/buf.lock index c84d9d463..683ac62d3 100644 --- a/buf.lock +++ b/buf.lock @@ -4,13 +4,13 @@ deps: - remote: buf.build owner: googleapis repository: googleapis - commit: 2bbd25900cb34c79bae97d85c948d3cf - digest: shake256:a6446e23f4408160217c22738a0c8c370ff3ff966f601d678960b803829cf53b5cf5998d20fcb3f9c9be944b24337843e7beb6e681ddbf9d036fb6491c0e47e7 + commit: 8bc2c51e08c447cd8886cdea48a73e14 + digest: shake256:a969155953a5cedc5b2df5b42c368f2bc66ff8ce1804bc96e0f14ff2ee8a893687963058909df844d1643cdbc98ff099d2daa6bc9f9f5b8886c49afdc60e19af - remote: buf.build owner: viamrobotics repository: api - commit: 50ed3af8a5ba4ae1b2cff6c9536163f7 - digest: shake256:c7a5a1a961713ab6316f08c4c0ea8308cf5b068516229d4c74ea78b7e2233ba51011616021e431a49a71ea8f7944f55bc70deb7a8dd2800ecdbb1107f026244f + commit: 611b5d52df6843bdb8f407dd7aa3e2fa + digest: shake256:c53316f527a616e8c4d9d96df342040d41a51f575b8410c091940eb774cfe5b9474ed888226d25a0464f48053e032746c60022df2a7bd90f29e85607c2545949 - remote: buf.build owner: viamrobotics repository: goutils diff --git a/examples/vanilla/package-lock.json b/examples/vanilla/package-lock.json index b432016f4..4cc37e056 100644 --- a/examples/vanilla/package-lock.json +++ b/examples/vanilla/package-lock.json @@ -19,7 +19,7 @@ }, "../..": { "name": "@viamrobotics/sdk", - "version": "0.16.0", + "version": "0.19.1", "license": "Apache-2.0", "dependencies": { "@viamrobotics/rpc": "^0.2.5", @@ -49,6 +49,7 @@ "prettier": "^3.1.1", "prettier-plugin-jsdoc": "^1.1.1", "protoc-gen-js": "^3.21.2", + "ts-node": "^10.9.2", "ts-protoc-gen": "^0.15.0", "typedoc": "^0.25.4", "typescript": "^5.3.3", diff --git a/package-lock.json b/package-lock.json index 2b1213660..c486463fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "prettier": "^3.1.1", "prettier-plugin-jsdoc": "^1.1.1", "protoc-gen-js": "^3.21.2", + "ts-node": "^10.9.2", "ts-protoc-gen": "^0.15.0", "typedoc": "^0.25.4", "typescript": "^5.3.3", @@ -514,6 +515,30 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@devexpress/error-stack-parser": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@devexpress/error-stack-parser/-/error-stack-parser-2.0.6.tgz", @@ -1358,6 +1383,34 @@ "node": ">=6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2035,6 +2088,13 @@ "node": ">=4" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2958,6 +3018,13 @@ "node": ">= 6" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3492,6 +3559,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -5732,6 +5809,13 @@ "semver": "bin/semver" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -8386,6 +8470,50 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/ts-protoc-gen": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.15.0.tgz", @@ -8631,6 +8759,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9166,6 +9301,16 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 458df95e2..c03097e18 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ } }, "scripts": { + "prebuild": "ts-node ./scripts/write-versions.ts", "build": "npm run build:bundle && npm run build:types && npm run build:copy-dts", "build:bundle": "vite build", "build:types": "tsc --project tsconfig.build.json", @@ -72,6 +73,7 @@ "prettier": "^3.1.1", "prettier-plugin-jsdoc": "^1.1.1", "protoc-gen-js": "^3.21.2", + "ts-node": "^10.9.2", "ts-protoc-gen": "^0.15.0", "typedoc": "^0.25.4", "typescript": "^5.3.3", diff --git a/scripts/write-versions.ts b/scripts/write-versions.ts new file mode 100644 index 000000000..f894ee9de --- /dev/null +++ b/scripts/write-versions.ts @@ -0,0 +1,7 @@ +const fs = require('node:fs'); + +const apiVersion = fs.readFileSync('./api_version.lock').toString().trim(); +fs.writeFileSync( + './src/api-version.ts', + `export const apiVersion = "${apiVersion}";` +); diff --git a/src/app/viam-transport.ts b/src/app/viam-transport.ts index 6a6c9a0b5..3a4a04ab9 100644 --- a/src/app/viam-transport.ts +++ b/src/app/viam-transport.ts @@ -3,6 +3,7 @@ import { dialDirect } from '@viamrobotics/rpc'; import { AuthenticateRequest, Credentials } from '../gen/proto/rpc/v1/auth_pb'; import { AuthServiceClient } from '../gen/proto/rpc/v1/auth_pb_service'; +import { MetadataTransport } from '../utils'; /** A credential that can be exchanged to obtain an access token */ export interface Credential { @@ -94,33 +95,13 @@ const createWithCredential = async ( new ViamTransport(transportFactory, opts, accessToken); }; -export class ViamTransport implements grpc.Transport { - private accessToken: string; - protected readonly transport: grpc.Transport; - +export class ViamTransport extends MetadataTransport { constructor( transportFactory: grpc.TransportFactory, opts: grpc.TransportOptions, accessToken: string ) { - this.transport = transportFactory(opts); - this.accessToken = accessToken; - } - - public start(metadata: grpc.Metadata): void { - metadata.set('authorization', `Bearer ${this.accessToken}`); - this.transport.start(metadata); - } - - public sendMessage(msgBytes: Uint8Array): void { - this.transport.sendMessage(msgBytes); - } - - public finishSend(): void { - this.transport.finishSend(); - } - - public cancel(): void { - this.transport.cancel(); + const md = new grpc.Metadata({ authorization: `Bearer ${accessToken}` }); + super(transportFactory, opts, md); } } diff --git a/src/robot/client.ts b/src/robot/client.ts index 94e363238..33087de82 100644 --- a/src/robot/client.ts +++ b/src/robot/client.ts @@ -36,7 +36,7 @@ import { SensorsServiceClient } from '../gen/service/sensors/v1/sensors_pb_servi import { SLAMServiceClient } from '../gen/service/slam/v1/slam_pb_service'; import { VisionServiceClient } from '../gen/service/vision/v1/vision_pb_service'; import { ViamResponseStream } from '../responses'; -import { encodeResourceName, promisify } from '../utils'; +import { encodeResourceName, promisify, MetadataTransport } from '../utils'; import GRPCConnectionManager from './grpc-connection-manager'; import type { Robot, RobotStatusStream } from './robot'; import SessionManager from './session-manager'; @@ -525,9 +525,16 @@ export class RobotClient extends EventDispatcher implements Robot { await this.gRPCConnectionManager.start(); } - const clientTransportFactory = this.sessionOptions?.disabled - ? this.transportFactory - : this.sessionManager.transportFactory; + const ctf: grpc.TransportFactory = ( + options: grpc.TransportOptions + ): grpc.Transport => { + const tf = this.sessionOptions?.disabled + ? this.transportFactory + : this.sessionManager.transportFactory; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return new MetadataTransport(tf!, options); + }; + const clientTransportFactory = ctf; const grpcOptions = { transport: clientTransportFactory }; this.robotServiceClient = new RobotServiceClient( diff --git a/src/utils.ts b/src/utils.ts index 6ea296737..d68f1b759 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,6 +3,7 @@ import { Struct } from 'google-protobuf/google/protobuf/struct_pb'; import type { ServiceError } from './gen/robot/v1/robot_pb_service'; import common from './gen/common/v1/common_pb'; import type { Options, StructType, Vector3 } from './types'; +import { apiVersion } from './api-version'; type Callback = (error: ServiceError | null, response: T | null) => void; @@ -215,3 +216,41 @@ export const encodeGeoGeometry = ( return result; }; + +export class MetadataTransport implements grpc.Transport { + private metadata: grpc.Metadata; + protected readonly transport: grpc.Transport; + + constructor( + transportFactory: grpc.TransportFactory, + opts: grpc.TransportOptions, + metadata?: grpc.Metadata + ) { + this.transport = transportFactory(opts); + this.metadata = metadata ?? new grpc.Metadata(); + this.metadata.set( + 'viam-client', + `typescript;v${__VERSION__};${apiVersion}` + ); + } + + public start(metadata: grpc.Metadata): void { + // eslint-disable-next-line unicorn/no-array-for-each + this.metadata.forEach((key, values) => { + metadata.set(key, values); + }); + this.transport.start(metadata); + } + + public sendMessage(msgBytes: Uint8Array): void { + this.transport.sendMessage(msgBytes); + } + + public finishSend(): void { + this.transport.finishSend(); + } + + public cancel(): void { + this.transport.cancel(); + } +}