Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"peerDependencies": {
"typescript": "^5.0.0"
},
"main": "src/index.ts",
"scripts": {
"test": "bun test",
"test:verbose": "BUN_TEST_VERBOSE=1 bun test",
Expand Down
37 changes: 37 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
showSession,
login,
} from './config';
import { ServeAccessToken } from './serve-access-token';

// Create and run the CLI
const cli = new Cli({
Expand Down Expand Up @@ -507,9 +508,45 @@ class LoginCommand extends Command {
}
}

class ServeAccessTokenCommand extends Command {
static paths = [['serve-access-token']];
static usage = Command.Usage({
description: 'Start a development server for access token generation',
details: `Starts a local HTTP server that provides an /access-token endpoint for development purposes.

This server generates access tokens using your HUME_API_KEY and HUME_SECRET_KEY environment variables.

⚠️ This is a demo server meant for development use only. On production servers, implement an /access-token endpoint on your own backend infrastructure, following the directions in https://dev.hume.ai/docs/introduction/api-key#token-authentication.`,
examples: [['Start the access token server', 'serve-access-token']],
});

port = Option.String('--port', {
description: 'Port to listen on (default: 8080)',
validator: t.isNumber(),
});

host = Option.String('--host', {
description: 'Host to listen on (default: localhost)',
});

apiKey = Option.String('--api-key', {
description: usageDescriptions.apiKey,
});

secretKey = Option.String('--secret-key', {
description: 'Override the default secret key',
});

async execute() {
const serveAccessToken = new ServeAccessToken();
await serveAccessToken.serve(this);
}
}

cli.register(RootCommand);
cli.register(TtsCommand);
cli.register(LoginCommand);
cli.register(ServeAccessTokenCommand);
cli.register(SessionRootCommand);
cli.register(SaveVoiceCommand);
cli.register(ListVoicesCommand);
Expand Down
113 changes: 113 additions & 0 deletions src/serve-access-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { createServer } from 'http';
import { fetchAccessToken } from 'hume/wrapper/fetchAccessToken';
import { type CommonOpts, getSettings } from './common';

export interface ServeAccessTokenOpts extends CommonOpts {
port?: number;
host?: string;
secretKey?: string;
}

export class ServeAccessToken {
async serve(opts: ServeAccessTokenOpts): Promise<void> {
const { globalConfig, session, env, reporter } = await getSettings(opts);

const port = opts.port ?? 8080;
const host = opts.host ?? 'localhost';

// Resolve API key and secret key with priority: globalConfig > session > env > opts
const apiKey = opts.apiKey ?? globalConfig.apiKey ?? session.apiKey ?? env.HUME_API_KEY;
const secretKey = opts.secretKey ?? env.HUME_SECRET_KEY;

if (!apiKey || !secretKey) {
let warningMessage = '⚠️ Missing required credentials:\n';

if (!apiKey) {
warningMessage += ' API key not found. You can set it via:\n';
warningMessage += ' - HUME_API_KEY environment variable\n';
warningMessage += ' - --api-key command line option\n';
warningMessage += ' - hume config set apiKey <your_key>\n';
}

if (!secretKey) {
warningMessage += ' Secret key not found. You can set it via:\n';
warningMessage += ' - HUME_SECRET_KEY environment variable\n';
warningMessage += ' - --secret-key command line option\n';
}

warningMessage += '\nExample usage:\n';
warningMessage += ' export HUME_API_KEY=your_api_key_here\n';
warningMessage += ' export HUME_SECRET_KEY=your_secret_key_here\n';
warningMessage += ' hume serve-access-token\n\n';
warningMessage += 'Or with command line options:\n';
warningMessage +=
' hume serve-access-token --api-key your_api_key --secret-key your_secret_key';

reporter.warn(warningMessage);
process.exit(1);
}

const server = createServer(async (req, res) => {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

if (req.url === '/access-token' && req.method === 'GET') {
try {
const accessToken = await fetchAccessToken({
apiKey,
secretKey,
host: opts.baseUrl,
});

res.setHeader('Content-Type', 'application/json');
res.writeHead(200);
res.end(JSON.stringify({ access_token: accessToken }));
return;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
res.setHeader('Content-Type', 'application/json');
res.writeHead(500);
res.end(JSON.stringify({ error: errorMessage }));
return;
}
}

if (req.url === '/' && req.method === 'GET') {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
res.end(infoMessage);
return;
}

res.writeHead(404);
res.end('Not Found');
});

const infoMessage = `🚀 Hume access token server listening on http://${host}:${port}

⚠️ This is a demo server meant for development use only.
On production servers, implement an /access-token endpoint
on your own backend infrastructure, following the directions at:
https://dev.hume.ai/docs/introduction/api-key#token-authentication

Press Ctrl+C to stop the server.`;

server.listen(port, host, () => {
reporter.info(infoMessage);
});

// Handle graceful shutdown
const shutdown = () => {
reporter.info('\n📡 Shutting down server...');
server.close(() => {
reporter.info('✅ Server stopped.');
process.exit(0);
});
};

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
}
}