March 27 2023

Things from March 27, 2023

March 27 2023

Thoughts

Arc browser is fucking awesome. Mini Arc while coding? Command Palette that just works? Command-Shift-C to copy a URL to clipboard? PiP for videos? Archiving unused tabs? Yes. Yes. YES.

https://bram-adams.ghost.io/content/images/2023/03/arc-mini.png
arc mini.png

Another reason GPT rocks: creating TS interfaces from bespoke objects (its even smart enough to handle lists and optionals!):

Me:

convert this into an interface

{
  openapi: '3.0.0',
  info: {
    title: 'NextJS Swagger',
    version: '0.1.0',
    description: 'A retrieval API for querying and filtering documents based on natural language queries and metadata'
  },
  paths: { '/api/hello-swagger': { get: [Object], post: [Object] } },
  components: {},
  tags: []
}

GPT:

interface OpenApi {
  openapi: string;
  info: OpenApiInfo;
  paths: Record<string, OpenApiPath>;
  components: Record<string, any>;
  tags: any[];
}

interface OpenApiInfo {
  title: string;
  version: string;
  description: string;
}

interface OpenApiPath {
  get?: OpenApiOperation;
  post?: OpenApiOperation;
  // Add other HTTP methods (e.g., put, delete) if needed
}

interface OpenApiOperation {
  // Define the structure of the OpenAPI operation here
  // For example, you can add properties like 'parameters', 'responses', etc.
  // Since the structure of the operation is not provided in the question, I'm leaving it as an empty object
  [key: string]: any;
}

// Example usage:
const api: OpenApi = {
  openapi: '3.0.0',
  info: {
    title: 'NextJS Swagger',
    version: '0.1.0',
    description: 'A retrieval API for querying and filtering documents based on natural language queries and metadata',
  },
  paths: {
    '/api/hello-swagger': {
      get: {},
      post: {},
    },
  },
  components: {},
  tags: [],
};

Photos

https://bram-adams.ghost.io/content/images/2023/03/barnacle-bridge.png
barnacle bridge.png
The Greek word typos generally means “trace,” and in this sense, such traces as those left on the beach by birds’ feet could be called typoi. At another level, the word refers to the way these tracks can be used as models for classifying the birds that have walked by. Finally, the word means that I myself have the capacity to make such bird tracks in the sand and so to distinguish and compare various kinds of birds. So typos refers to that which all bird tracks have in common (the typical); it means the universal behind all that is characteristic and distinctive. (Location 916)
https://bram-adams.ghost.io/content/images/2023/03/typo.png
typo.png

bramadams.dev is a reader-supported published Zettelkasten. Both free and paid subscriptions are available. If you want to support my work, the best way is by taking out a paid subscription.

Audio

on a melodic edm kick as of late

and some oliver francis oc!

Code

Yo dawg, I heard you like using ChatGPT to write a OpenAPI spec for ChatGPT plugins.

https://bram-adams.ghost.io/content/images/2023/03/yo-dawg.png
yo dawg.png
/* 
1. get the handler function from files
2. use gpt to infer the openapi yaml from the spec
3. return the result as a string
*/
import { glob } from "glob";
import { readJsonFile, writeJsonFile } from "./helpers";
import { loadEnvConfig } from "@next/env";
loadEnvConfig(process.cwd()); // Load environment variables

import { Configuration, OpenAIApi } from "openai";
const configuration = new Configuration({
  organization: "...",
  apiKey: process.env.OPENAI_API_KEY, // Use OpenAI API key from environment variables
});
const openai = new OpenAIApi(configuration); // Initialize OpenAI API client

import * as babel from "@babel/core";
// needs to be required instead of imported to work
const parser = require("@babel/parser"); // Import Babel parser

const getApiRoutes = async () => {
  const apiRoutes = await glob("pages/api/**/*.ts"); // Get all API route files
  return apiRoutes;
};

const getHandlerFunction = async (apiRoutes: string[]) => {
  const handlerFunctions = [];
  for (const apiRoute of apiRoutes) {
    const file = await readJsonFile(apiRoute); // Read each API route file

    const ast = parser.parse(file, {
      sourceType: "module",
      plugins: ["typescript"], // Parse TypeScript code
    });

    babel.traverse(ast, {
      ExportDefaultDeclaration(path) {
        const declaration = path.node.declaration;
        if (
          declaration.type === "FunctionDeclaration" &&
          declaration.id.name === "handler"
        ) {
          // Extract handler functions from the AST
          handlerFunctions.push({ content: file.slice(declaration.start, declaration.end), name: apiRoute.slice(10, -3) });
        }
      },
    });
  }
  return handlerFunctions; // Return an array of handler functions
};

const inferOpenApiSpec = async () => {
  const apiRoutes = await getApiRoutes(); // Get API routes
  const handlerFunctions = await getHandlerFunction(apiRoutes); // Get handler functions

  for (const handlerFunction of handlerFunctions) {
    const completion = await openai.createChatCompletion({
      model: "gpt-3.5-turbo",
      messages: [
        {
          role: "system",
          content:
            "I am a bot that converts handler functions into OpenAPI specs. I only return YAML",
        },
        {
          role: "user",
          content:
            `I want to convert this handler function with path /api/${handlerFunction.name} into an OpenAPI spec: ${handlerFunction.content}` // Request to convert handler function to OpenAPI spec
        },
      ],
    });
    console.log(completion.data.choices[0].message); // Log the generated OpenAPI spec
  }
};

inferOpenApiSpec(); // Run the inferOpenApiSpec function

My fellow Obsidian-ers, trust me when I say you'll need a script like this soon (trust me):

Bram:

can you write a nodejs script that will copy all markdown files to another folder?

  • only markdown
  • retained sub folder structure
  • original should be left intact

GPT:

const fs = require('fs');
const path = require('path');

function copyMarkdownFiles(srcDir, destDir) {
  // Ensure the destination directory exists
  if (!fs.existsSync(destDir)) {
    fs.mkdirSync(destDir, { recursive: true });
  }

  // Read the contents of the source directory
  const items = fs.readdirSync(srcDir, { withFileTypes: true });

  // Iterate over each item in the source directory
  for (const item of items) {
    const srcPath = path.join(srcDir, item.name);
    const destPath = path.join(destDir, item.name);

    if (item.isDirectory()) {
      // If the item is a directory, recursively copy its contents
      copyMarkdownFiles(srcPath, destPath);
    } else if (item.isFile() && path.extname(item.name) === '.md') {
      // If the item is a Markdown file, copy it to the destination directory
      fs.copyFileSync(srcPath, destPath);
    }
  }
}

// Example usage:
const srcDir = './source'; // Source directory (replace with your source directory path)
const destDir = './destination'; // Destination directory (replace with your destination directory path)
copyMarkdownFiles(srcDir, destDir);