robodev 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robodev",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "CLI for Robodev Starbase — create, auth, link, and deploy file-based APIs",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/api.ts CHANGED
@@ -55,6 +55,7 @@ async function refresh(refreshToken: string): Promise<Credentials | null> {
55
55
  return (await res.json()) as Credentials;
56
56
  }
57
57
 
58
+ /** Authenticated Starbase request. Reads `~/.robodev/credentials.json` and refreshes on 401. */
58
59
  export async function authed<T>(path: string, init: RequestInit = {}): Promise<T> {
59
60
  const credentials = await readCredentials();
60
61
  if (!credentials) {
package/src/config.ts CHANGED
@@ -15,18 +15,22 @@ export type ProjectLink = {
15
15
 
16
16
  const PROD_URL = "https://robodev.povio.dev";
17
17
 
18
+ /** Control-plane API. Override with `STARBASE_URL` for local Starbase. */
18
19
  export function starbaseUrl(): string {
19
20
  return (process.env.STARBASE_URL ?? PROD_URL).replace(/\/$/, "");
20
21
  }
21
22
 
23
+ /** Browser UI used for OAuth. Override with `STARBASE_FE_URL`. */
22
24
  export function feUrl(): string {
23
25
  return (process.env.STARBASE_FE_URL ?? process.env.STARBASE_URL ?? PROD_URL).replace(/\/$/, "");
24
26
  }
25
27
 
28
+ /** `~/.robodev/credentials.json` — tokens from `robodev auth`. */
26
29
  export function credentialsPath(): string {
27
30
  return join(homedir(), ".robodev", "credentials.json");
28
31
  }
29
32
 
33
+ /** Project link file written by `create` / `link`. */
30
34
  export function linkPath(cwd = process.cwd()): string {
31
35
  return join(cwd, ".robodev");
32
36
  }
@@ -61,6 +65,7 @@ export async function writeLink(link: ProjectLink, cwd = process.cwd()): Promise
61
65
  await writeFile(linkPath(cwd), `${JSON.stringify(link, null, 2)}\n`);
62
66
  }
63
67
 
68
+ /** Sets `VITE_API_URL` so the starter UI talks to the deployed project host. */
64
69
  export async function writeViteApiUrl(projectApiUrl: string, cwd = process.cwd()): Promise<void> {
65
70
  const envPath = join(cwd, ".env");
66
71
  let content = "";
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ function prompt(question: string): Promise<string> {
55
55
  return rl.question(question).finally(() => rl.close());
56
56
  }
57
57
 
58
+ /** Local loopback server that completes the browser OAuth handshake for `robodev auth`. */
58
59
  async function waitForOauthCode(): Promise<{ code: string; redirectUri: string }> {
59
60
  const state = randomBytes(16).toString("hex");
60
61
  return new Promise((resolve, reject) => {
@@ -184,6 +185,7 @@ async function runLink(projectId?: string): Promise<void> {
184
185
  console.log(`Frontend API ${project.apiUrl}`);
185
186
  }
186
187
 
188
+ /** Collects `database.ts` plus every `api/**/*.ts` file for a deploy upload. */
187
189
  async function collectFiles(root: string): Promise<{ path: string; content: string }[]> {
188
190
  const files: { path: string; content: string }[] = [];
189
191
  const database = join(root, "database.ts");
@@ -220,6 +222,7 @@ async function collectFiles(root: string): Promise<{ path: string; content: stri
220
222
  return files;
221
223
  }
222
224
 
225
+ /** Uploads schema + APIs. Destructive schema changes need confirmation or `--force`. */
223
226
  async function runDeploy(forceFlag: boolean, root = process.cwd()): Promise<void> {
224
227
  const link = await readLink(root);
225
228
  if (!link) {
@@ -356,6 +359,7 @@ async function npmInstall(root: string): Promise<void> {
356
359
  });
357
360
  }
358
361
 
362
+ /** Creates a Starbase project, copies the starter template, links, deploys, and installs deps. */
359
363
  async function runCreate(pathArg?: string): Promise<void> {
360
364
  const root = resolve(process.cwd(), pathArg ?? ".");
361
365
  await mkdir(root, { recursive: true });
@@ -1,6 +1,6 @@
1
1
  # {{NAME}}
2
2
 
3
- Example Robodev app: a `space` database (`planets`, `rockets`), APIs in `api/`, and a small React UI.
3
+ Starter app from `robodev create`: a `space` database (`planets`, `rockets`), file-based APIs in `api/`, and a small React UI.
4
4
 
5
5
  The backend is already on Starbase. Run the frontend:
6
6
 
@@ -10,6 +10,8 @@ npm run dev
10
10
 
11
11
  It reads `VITE_API_URL` from `.env` (written by `robodev create` / `robodev link`).
12
12
 
13
+ Docs: https://robodev.povio.dev/docs
14
+
13
15
  Add tables in `database.ts` and routes as `api/<name>.ts`, then:
14
16
 
15
17
  ```bash
@@ -1,4 +1,5 @@
1
- import { defineApi, z } from "robodev-lib";
1
+ /** GET /health liveness check for the deployed project host. */
2
+ import { defineApi, z } from "@robodev-ai/sdk";
2
3
 
3
4
  export const get = defineApi({
4
5
  response: z.object({ ok: z.literal(true) }),
@@ -1,4 +1,6 @@
1
- import { defineApi, z } from "robodev-lib";
1
+ /** POST /launch marks a rocket as launched. */
2
+ import { rockets } from "../database";
3
+ import { defineApi, eq, z } from "@robodev-ai/sdk";
2
4
 
3
5
  const Rocket = z.object({
4
6
  id: z.string(),
@@ -13,10 +15,11 @@ export const post = defineApi({
13
15
  }),
14
16
  response: Rocket,
15
17
  handler: async ({ db, body }) => {
16
- const rows = await db.from("rockets").update({
17
- where: { id: body.id },
18
- data: { launched: true },
19
- });
18
+ const rows = await db
19
+ .update(rockets)
20
+ .set({ launched: true })
21
+ .where(eq(rockets.id, body.id))
22
+ .returning();
20
23
  const row = rows[0];
21
24
  if (!row) {
22
25
  throw new Error("Rocket not found");
@@ -1,4 +1,6 @@
1
- import { defineApi, z } from "robodev-lib";
1
+ /** GET/POST /planets list (seeds sample rows on first GET) and create planets. */
2
+ import { planets, rockets } from "../database";
3
+ import { defineApi, z } from "@robodev-ai/sdk";
2
4
 
3
5
  const EARTH = "11111111-1111-1111-1111-111111111111";
4
6
  const MARS = "22222222-2222-2222-2222-222222222222";
@@ -16,29 +18,23 @@ const Planet = z.object({
16
18
  export const get = defineApi({
17
19
  response: z.array(Planet),
18
20
  handler: async ({ db }) => {
19
- const existing = await db.from("planets").findMany({ take: 1 });
21
+ const existing = await db.select().from(planets).limit(1);
20
22
  if (existing.length === 0) {
21
23
  try {
22
- await db.from("planets").create({
23
- data: { id: EARTH, name: "Earth", climate: "temperate", moons: 1 },
24
- });
25
- await db.from("planets").create({
26
- data: { id: MARS, name: "Mars", climate: "arid", moons: 2 },
27
- });
28
- await db.from("planets").create({
29
- data: { id: KEPLER, name: "Kepler-452b", climate: "unknown", moons: 0 },
30
- });
31
- await db.from("rockets").create({
32
- data: { id: VOYAGER, name: "Voyager", destinationId: MARS, launched: false },
33
- });
34
- await db.from("rockets").create({
35
- data: { id: ODYSSEY, name: "Odyssey", destinationId: KEPLER, launched: true },
36
- });
24
+ await db.insert(planets).values([
25
+ { id: EARTH, name: "Earth", climate: "temperate", moons: 1 },
26
+ { id: MARS, name: "Mars", climate: "arid", moons: 2 },
27
+ { id: KEPLER, name: "Kepler-452b", climate: "unknown", moons: 0 },
28
+ ]);
29
+ await db.insert(rockets).values([
30
+ { id: VOYAGER, name: "Voyager", destinationId: MARS, launched: false },
31
+ { id: ODYSSEY, name: "Odyssey", destinationId: KEPLER, launched: true },
32
+ ]);
37
33
  } catch {
38
34
  // First request won the seed race.
39
35
  }
40
36
  }
41
- const rows = await db.from("planets").findMany();
37
+ const rows = await db.select().from(planets);
42
38
  return Planet.array().parse(rows);
43
39
  },
44
40
  });
@@ -51,14 +47,14 @@ export const post = defineApi({
51
47
  }),
52
48
  response: Planet,
53
49
  handler: async ({ db, body }) => {
54
- const row = await db.from("planets").create({
55
- data: {
56
- id: crypto.randomUUID(),
50
+ const [row] = await db
51
+ .insert(planets)
52
+ .values({
57
53
  name: body.name,
58
54
  climate: body.climate,
59
55
  moons: body.moons,
60
- },
61
- });
56
+ })
57
+ .returning();
62
58
  return Planet.parse(row);
63
59
  },
64
60
  });
@@ -1,4 +1,6 @@
1
- import { defineApi, z } from "robodev-lib";
1
+ /** GET/POST /rockets list (optional `?destinationId=`) and create rockets. */
2
+ import { rockets } from "../database";
3
+ import { defineApi, eq, z } from "@robodev-ai/sdk";
2
4
 
3
5
  const Rocket = z.object({
4
6
  id: z.string(),
@@ -13,9 +15,9 @@ export const get = defineApi({
13
15
  }),
14
16
  response: z.array(Rocket),
15
17
  handler: async ({ db, query }) => {
16
- const rows = await db.from("rockets").findMany({
17
- where: query.destinationId ? { destinationId: query.destinationId } : undefined,
18
- });
18
+ const rows = query.destinationId
19
+ ? await db.select().from(rockets).where(eq(rockets.destinationId, query.destinationId))
20
+ : await db.select().from(rockets);
19
21
  return Rocket.array().parse(rows);
20
22
  },
21
23
  });
@@ -28,14 +30,14 @@ export const post = defineApi({
28
30
  }),
29
31
  response: Rocket,
30
32
  handler: async ({ db, body }) => {
31
- const row = await db.from("rockets").create({
32
- data: {
33
- id: crypto.randomUUID(),
33
+ const [row] = await db
34
+ .insert(rockets)
35
+ .values({
34
36
  name: body.name,
35
37
  destinationId: body.destinationId,
36
38
  launched: body.launched ?? false,
37
- },
38
- });
39
+ })
40
+ .returning();
39
41
  return Rocket.parse(row);
40
42
  },
41
43
  });
@@ -1,20 +1,27 @@
1
- import { boolean, defineDatabase, integer, table, text, timestamp, uuid } from "robodev-lib";
1
+ /**
2
+ * Starter schema. `robodev deploy` creates the `space` database and these tables
3
+ * (no migration files). Edit here and deploy again to sync.
4
+ */
5
+ import { boolean, defineDatabase, integer, pgTable, text, timestamp, uuid } from "@robodev-ai/sdk";
6
+
7
+ export const planets = pgTable("planets", {
8
+ id: uuid("id").primaryKey().defaultRandom(),
9
+ name: text("name").notNull(),
10
+ climate: text("climate").notNull(),
11
+ moons: integer("moons").default(0),
12
+ });
13
+
14
+ export const rockets = pgTable("rockets", {
15
+ id: uuid("id").primaryKey().defaultRandom(),
16
+ name: text("name").notNull(),
17
+ destinationId: uuid("destinationId")
18
+ .notNull()
19
+ .references(() => planets.id, { onDelete: "cascade" }),
20
+ launched: boolean("launched").default(false),
21
+ createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
22
+ });
2
23
 
3
24
  export default defineDatabase({
4
25
  name: "space",
5
- tables: {
6
- planets: table({
7
- id: uuid({ primaryKey: true }),
8
- name: text({ notNull: true }),
9
- climate: text({ notNull: true }),
10
- moons: integer({ default: 0 }),
11
- }),
12
- rockets: table({
13
- id: uuid({ primaryKey: true }),
14
- name: text({ notNull: true }),
15
- destinationId: uuid({ notNull: true }),
16
- launched: boolean({ default: false }),
17
- createdAt: timestamp({ default: "now()" }),
18
- }),
19
- },
26
+ tables: { planets, rockets },
20
27
  });
@@ -10,13 +10,13 @@
10
10
  "dependencies": {
11
11
  "react": "^18.3.1",
12
12
  "react-dom": "^18.3.1",
13
- "robodev-lib": "^0.1.0"
13
+ "@robodev-ai/sdk": "^0.2.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/react": "^18.3.24",
17
17
  "@types/react-dom": "^18.3.7",
18
18
  "@vitejs/plugin-react": "^4.7.0",
19
- "robodev": "^0.1.0",
19
+ "robodev": "^0.2.0",
20
20
  "typescript": "^5.9.2",
21
21
  "vite": "^6.3.5"
22
22
  }
@@ -1,3 +1,4 @@
1
+ /** Starter UI. Calls the deployed project host using `VITE_API_URL` from `.env`. */
1
2
  import { StrictMode, useEffect, useState, type FormEvent } from "react";
2
3
  import { createRoot } from "react-dom/client";
3
4