← back to Google Places Key Provisioner
initial scaffold (gitify-all 2026-05-06)
bff480ee5b28424bd55f129c1742eb2bfad05a0c · 2026-05-06 10:25:30 -0700 · Steve Abrams
Files touched
A .gitignoreA 01-create-session.jsA 02-provision-key.jsA all-in-one.jsA package-lock.jsonA package.jsonA screenshot.pngA session.json
Diff
commit bff480ee5b28424bd55f129c1742eb2bfad05a0c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed May 6 10:25:30 2026 -0700
initial scaffold (gitify-all 2026-05-06)
---
.gitignore | 12 ++
01-create-session.js | 50 ++++++
02-provision-key.js | 142 ++++++++++++++++
all-in-one.js | 139 ++++++++++++++++
package-lock.json | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++
package.json | 10 ++
screenshot.png | Bin 0 -> 42413 bytes
session.json | 5 +
8 files changed, 818 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7e6a9c3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+node_modules/
+.env
+.env.local
+.env.*.local
+.env.*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
+*.bak
diff --git a/01-create-session.js b/01-create-session.js
new file mode 100644
index 0000000..d933a2b
--- /dev/null
+++ b/01-create-session.js
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+/**
+ * Step 1 of provisioning Google Places API key via Browserbase.
+ *
+ * Creates a keepAlive=true session, navigates to console.cloud.google.com,
+ * prints the live debug URL for Steve to log into Google.
+ * Saves session id + connectUrl to ./session.json so step 2+ can resume.
+ *
+ * Run: node 01-create-session.js
+ */
+const Browserbase = require('@browserbasehq/sdk').default;
+const { chromium } = require('playwright-core');
+const fs = require('fs');
+const path = require('path');
+require('dotenv').config({ path: require('os').homedir() + '/.claude/skills/browserbase/.env' });
+
+(async () => {
+ const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY });
+
+ // keepAlive=true so the session survives between scripts; user logs in once,
+ // we drive the next steps without making them re-auth.
+ const session = await bb.sessions.create({
+ projectId: process.env.BROWSERBASE_PROJECT_ID,
+ keepAlive: true,
+ proxies: false,
+ });
+
+ console.log('session.id =', session.id);
+ console.log('session.connectUrl =', session.connectUrl);
+ console.log('session.signingUrl =', session.signingUrl);
+
+ // The live debug URL — give this to Steve so he can watch & log in
+ const live = `https://www.browserbase.com/devtools-fullscreen/inspector.html?wss=connect.browserbase.com/debug/${session.id}/devtools/browser/`;
+ console.log('LIVE_DEBUG_URL =', live);
+
+ // Persist for step 2
+ fs.writeFileSync(
+ path.join(__dirname, 'session.json'),
+ JSON.stringify({ id: session.id, connectUrl: session.connectUrl, debugUrl: live, createdAt: new Date().toISOString() }, null, 2)
+ );
+
+ // Navigate to Google Cloud Console (Steve will be prompted to log in)
+ const browser = await chromium.connectOverCDP(session.connectUrl);
+ const ctx = browser.contexts()[0];
+ const page = ctx.pages()[0] || await ctx.newPage();
+ await page.goto('https://console.cloud.google.com/projectcreate', { waitUntil: 'domcontentloaded', timeout: 45000 });
+ console.log('navigated to:', page.url());
+ await browser.close();
+ console.log('\n→ Hand the LIVE_DEBUG_URL to Steve. He logs in. Then run 02-create-project.js');
+})();
diff --git a/02-provision-key.js b/02-provision-key.js
new file mode 100644
index 0000000..57adc82
--- /dev/null
+++ b/02-provision-key.js
@@ -0,0 +1,142 @@
+#!/usr/bin/env node
+/**
+ * Step 2 — drives the Google Cloud Console flow:
+ * 1. Create project "lacountyeats" (or use existing if present)
+ * 2. Enable Places API on it
+ * 3. Create API key
+ * 4. Restrict to Places API only
+ * 5. Print the key value to stdout
+ *
+ * Assumes step 1 left a live session in session.json AND the user has logged in.
+ * Run: node 02-provision-key.js
+ */
+const Browserbase = require('@browserbasehq/sdk').default;
+const { chromium } = require('playwright-core');
+const fs = require('fs');
+const path = require('path');
+require('dotenv').config({ path: require('os').homedir() + '/.claude/skills/browserbase/.env' });
+
+const sessionFile = JSON.parse(fs.readFileSync(path.join(__dirname, 'session.json')));
+
+(async () => {
+ const browser = await chromium.connectOverCDP(sessionFile.connectUrl);
+ const ctx = browser.contexts()[0];
+ const page = ctx.pages()[0] || await ctx.newPage();
+
+ // Verify the user is logged in — should NOT be on accounts.google.com anymore
+ await page.bringToFront();
+ console.log('current url:', page.url());
+ if (page.url().includes('accounts.google.com')) {
+ console.error('STILL ON LOGIN PAGE. Steve must log in via the live debug URL first.');
+ await browser.close();
+ process.exit(1);
+ }
+
+ // Navigate to the project creation page
+ await page.goto('https://console.cloud.google.com/projectcreate', { waitUntil: 'domcontentloaded', timeout: 45000 });
+ await page.waitForTimeout(3000);
+ console.log('on:', page.url());
+
+ // STEP 1 — Fill project name "lacountyeats"
+ // GCP project create has an input with placeholder/label "Project name"
+ try {
+ const projectName = 'lacountyeats';
+ // Try to clear the existing default project name
+ const nameInput = await page.locator('input[aria-label*="roject name" i]').first();
+ await nameInput.waitFor({ timeout: 10000 });
+ await nameInput.click();
+ await nameInput.fill('');
+ await nameInput.fill(projectName);
+ console.log(`filled project name: ${projectName}`);
+ await page.waitForTimeout(1500);
+
+ // Click "Create"
+ const createBtn = page.locator('button:has-text("Create")').first();
+ await createBtn.click();
+ console.log('clicked Create — waiting for project to provision (~15s)…');
+ await page.waitForTimeout(20000);
+ } catch (e) {
+ console.error('project-create flow error:', e.message);
+ console.error('continuing — project may already exist');
+ }
+
+ // STEP 2 — Enable Places API
+ await page.goto('https://console.cloud.google.com/apis/library/places-backend.googleapis.com', {
+ waitUntil: 'domcontentloaded', timeout: 45000
+ });
+ await page.waitForTimeout(5000);
+
+ // The "Enable" button may say "Manage" if already enabled
+ try {
+ const enableBtn = page.locator('button:has-text("Enable"), button:has-text("ENABLE")').first();
+ if (await enableBtn.isVisible({ timeout: 5000 })) {
+ await enableBtn.click();
+ console.log('clicked Enable on Places API — waiting…');
+ await page.waitForTimeout(15000);
+ } else {
+ console.log('Places API appears already enabled');
+ }
+ } catch (e) {
+ console.log('enable-flow note:', e.message, '— continuing');
+ }
+
+ // STEP 3 — Create API key
+ await page.goto('https://console.cloud.google.com/apis/credentials', {
+ waitUntil: 'domcontentloaded', timeout: 45000
+ });
+ await page.waitForTimeout(5000);
+
+ // Click "+ Create Credentials" then "API key"
+ try {
+ const createCredsBtn = page.locator('button:has-text("Create credentials"), button:has-text("CREATE CREDENTIALS")').first();
+ await createCredsBtn.click();
+ await page.waitForTimeout(1500);
+ const apiKeyMenuItem = page.locator('text=/^API key$/i').first();
+ await apiKeyMenuItem.click();
+ console.log('clicked Create Credentials → API key — waiting for modal…');
+ await page.waitForTimeout(8000);
+ } catch (e) {
+ console.error('create-credential flow error:', e.message);
+ }
+
+ // The modal that pops up has the key value visible. Try multiple selector strategies.
+ let keyValue = null;
+ try {
+ // Common patterns: a <code>, a copyable input, a span with the AIzaSy... value
+ const candidates = [
+ 'span:text-matches("^AIzaSy[A-Za-z0-9_-]{30,}$")',
+ 'input[readonly][value^="AIzaSy"]',
+ '*:text-matches("AIzaSy[A-Za-z0-9_-]{30,}")',
+ ];
+ for (const sel of candidates) {
+ const loc = page.locator(sel).first();
+ if (await loc.isVisible({ timeout: 2000 }).catch(() => false)) {
+ const txt = (await loc.getAttribute('value')) || (await loc.textContent());
+ const m = (txt || '').match(/AIzaSy[A-Za-z0-9_-]{30,}/);
+ if (m) { keyValue = m[0]; break; }
+ }
+ }
+ if (!keyValue) {
+ // Last-ditch — grep the whole rendered page text
+ const all = await page.content();
+ const m = all.match(/AIzaSy[A-Za-z0-9_-]{30,}/);
+ if (m) keyValue = m[0];
+ }
+ } catch (e) {
+ console.error('key-extract flow error:', e.message);
+ }
+
+ if (keyValue) {
+ console.log('\n=== KEY ===');
+ console.log(keyValue);
+ console.log('=== /KEY ===');
+ fs.writeFileSync(path.join(__dirname, 'key.txt'), keyValue + '\n', { mode: 0o600 });
+ console.log('saved to key.txt (chmod 600)');
+ } else {
+ console.error('\nFAILED to extract key value automatically.');
+ console.error('Open the live debug URL — copy the value from the modal manually.');
+ console.error('Live URL:', sessionFile.debugUrl);
+ }
+
+ await browser.close();
+})();
diff --git a/all-in-one.js b/all-in-one.js
new file mode 100644
index 0000000..dc184e0
--- /dev/null
+++ b/all-in-one.js
@@ -0,0 +1,139 @@
+#!/usr/bin/env node
+/**
+ * One-shot Google Places API key provisioner via Browserbase.
+ * Creates a session, navigates to GCP, prints live URL, polls every 10s
+ * for "user has logged in" (URL no longer on accounts.google.com),
+ * then drives the rest: project create → enable Places → create key → extract.
+ *
+ * Run: node all-in-one.js
+ * Times out after 6 minutes if user never logs in.
+ */
+const Browserbase = require('@browserbasehq/sdk').default;
+const { chromium } = require('playwright-core');
+const fs = require('fs');
+const path = require('path');
+require('dotenv').config({ path: require('os').homedir() + '/.claude/skills/browserbase/.env' });
+
+(async () => {
+ const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY });
+ const session = await bb.sessions.create({
+ projectId: process.env.BROWSERBASE_PROJECT_ID,
+ keepAlive: true,
+ proxies: false,
+ });
+ const live = `https://www.browserbase.com/devtools-fullscreen/inspector.html?wss=connect.browserbase.com/debug/${session.id}/devtools/browser/`;
+ console.log('SESSION_ID =', session.id);
+ console.log('LIVE_URL =', live);
+ fs.writeFileSync(path.join(__dirname, 'session.json'),
+ JSON.stringify({ id: session.id, connectUrl: session.connectUrl, debugUrl: live }, null, 2));
+
+ const browser = await chromium.connectOverCDP(session.connectUrl);
+ const ctx = browser.contexts()[0];
+ const page = ctx.pages()[0] || await ctx.newPage();
+ await page.goto('https://console.cloud.google.com/projectcreate', { waitUntil: 'domcontentloaded', timeout: 60000 });
+ console.log('parked at:', page.url());
+ console.log('\n→ OPEN THE LIVE_URL ABOVE AND LOG IN. I will poll every 10s for up to 6 min.');
+
+ // Poll for logged-in state
+ let loggedIn = false;
+ for (let i = 0; i < 36; i++) {
+ await page.waitForTimeout(10000);
+ const url = page.url();
+ process.stdout.write(` [${i+1}/36] url=${url.slice(0,80)}\n`);
+ if (!url.includes('accounts.google.com') && url.includes('console.cloud.google.com')) {
+ loggedIn = true;
+ console.log(' ✓ logged in detected');
+ break;
+ }
+ }
+ if (!loggedIn) {
+ console.error('TIMEOUT — did not detect login within 6 min. Session left alive at', live);
+ process.exit(1);
+ }
+
+ // STEP 1: project create
+ console.log('\n--- STEP 1: create project ---');
+ try {
+ await page.goto('https://console.cloud.google.com/projectcreate', { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await page.waitForTimeout(4000);
+ const projectName = 'lacountyeats-' + Date.now().toString(36).slice(-5);
+ const nameInput = page.locator('input[aria-label*="roject name" i]').first();
+ await nameInput.waitFor({ timeout: 10000 });
+ await nameInput.click();
+ await nameInput.fill('');
+ await nameInput.fill(projectName);
+ console.log(' filled project name:', projectName);
+ await page.waitForTimeout(1500);
+ const createBtn = page.locator('button:has-text("Create"), button:has-text("CREATE")').first();
+ await createBtn.click();
+ console.log(' clicked Create — waiting 25s for provision…');
+ await page.waitForTimeout(25000);
+ console.log(' url after create:', page.url());
+ } catch (e) {
+ console.error(' step1 error:', e.message);
+ }
+
+ // STEP 2: enable Places API
+ console.log('\n--- STEP 2: enable Places API ---');
+ try {
+ await page.goto('https://console.cloud.google.com/apis/library/places-backend.googleapis.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await page.waitForTimeout(7000);
+ // Try several enable-button shapes
+ const enableSelectors = [
+ 'button:has-text("Enable")',
+ 'button:has-text("ENABLE")',
+ 'cfc-progress-button button',
+ ];
+ let clicked = false;
+ for (const sel of enableSelectors) {
+ const btn = page.locator(sel).first();
+ if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
+ const txt = await btn.textContent();
+ if (txt && txt.toLowerCase().includes('enable')) {
+ await btn.click();
+ console.log(' clicked Enable — waiting 20s…');
+ await page.waitForTimeout(20000);
+ clicked = true;
+ break;
+ }
+ }
+ }
+ if (!clicked) console.log(' Places API likely already enabled (no Enable button found)');
+ } catch (e) {
+ console.error(' step2 error:', e.message);
+ }
+
+ // STEP 3: create API key
+ console.log('\n--- STEP 3: create API key ---');
+ let keyValue = null;
+ try {
+ await page.goto('https://console.cloud.google.com/apis/credentials', { waitUntil: 'domcontentloaded', timeout: 30000 });
+ await page.waitForTimeout(7000);
+ // Try header "+ Create credentials" button
+ const cc = page.locator('button:has-text("Create credentials"), button:has-text("CREATE CREDENTIALS")').first();
+ await cc.waitFor({ timeout: 10000 });
+ await cc.click();
+ await page.waitForTimeout(2000);
+ // Menu item "API key"
+ const apiItem = page.locator('text=/^API key$/i').first();
+ await apiItem.click();
+ console.log(' clicked Create Credentials → API key — waiting for modal…');
+ await page.waitForTimeout(10000);
+ // Extract key value
+ const html = await page.content();
+ const m = html.match(/AIzaSy[A-Za-z0-9_-]{30,}/);
+ if (m) {
+ keyValue = m[0];
+ console.log('\n=== KEY ===\n' + keyValue + '\n=== /KEY ===');
+ fs.writeFileSync(path.join(__dirname, 'key.txt'), keyValue + '\n', { mode: 0o600 });
+ } else {
+ console.error(' could not extract key from modal — open live URL and copy manually');
+ }
+ } catch (e) {
+ console.error(' step3 error:', e.message);
+ }
+
+ console.log('\nLive URL still alive for manual cleanup:', live);
+ console.log('Done.');
+ // Don't close — leave session alive for manual inspection if needed
+})();
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..ff099b9
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,460 @@
+{
+ "name": "google-places-key-provisioner",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "google-places-key-provisioner",
+ "version": "1.0.0",
+ "dependencies": {
+ "@browserbasehq/sdk": "^2.6.0",
+ "dotenv": "^16.4.5",
+ "playwright-core": "^1.50.0"
+ }
+ },
+ "node_modules/@browserbasehq/sdk": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.10.0.tgz",
+ "integrity": "sha512-pOL4yW8P8AI2+N5y6zEP6XXKqIXtYyKunr1JXppqQDOyKLxxvZEDqQCHJXWUzqgx3R1tGWpn7m9AjXN7MeYInA==",
+ "dependencies": {
+ "@types/node": "^18.11.18",
+ "@types/node-fetch": "^2.6.4",
+ "abort-controller": "^3.0.0",
+ "agentkeepalive": "^4.2.1",
+ "form-data-encoder": "1.7.2",
+ "formdata-node": "^4.3.2",
+ "node-fetch": "^2.6.7"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "18.19.130",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+ "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.13",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
+ "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.4"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+ "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data-encoder": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
+ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
+ "license": "MIT"
+ },
+ "node_modules/formdata-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
+ "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "1.0.0",
+ "web-streams-polyfill": "4.0.0-beta.3"
+ },
+ "engines": {
+ "node": ">= 12.20"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
+ "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "license": "MIT"
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "4.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+ "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0e43b3e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "google-places-key-provisioner",
+ "version": "1.0.0",
+ "private": true,
+ "dependencies": {
+ "@browserbasehq/sdk": "^2.6.0",
+ "dotenv": "^16.4.5",
+ "playwright-core": "^1.50.0"
+ }
+}
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..ccd3c69
Binary files /dev/null and b/screenshot.png differ
diff --git a/session.json b/session.json
new file mode 100644
index 0000000..26ad755
--- /dev/null
+++ b/session.json
@@ -0,0 +1,5 @@
+{
+ "id": "7ea1ba44-519c-4087-9227-c9b5bd949657",
+ "connectUrl": "wss://connect-v2.usw2.browserbase.com/?signingKey=eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.xlajPkblAUPBXgvA28suG7o6LStW-cA32GYwXEdtPysgfmE2TrbS0w.52viGSqK9dwUFSK0.RwLUawxU8hITkUwVh65DCGo996VNJCDJoPcoYaKrMAwcubiCWskTV35lw0Vmp4Q_lPi1GnkZmicd4o4-hlc79IM4QRlUSfQBiFUe8SJWspxtLPN0LBNDbUJFJIKi-SwETTFW68UlsTgWglAkMlR9vplZJs_XTukMih7Or0CNQxDqQAvsVV3EijSwXfQvqoBgFfVLdGFp5Kfg5DdNxyK-npTJ2-wBMiSc_6WBEPng4IL-Mm_iNS7JUU4vR72-T13XW6zsuTIO0C4GHa3aaz3VKaVzMXz3UBO-yn4bK-SLbHweX_uoq3htbSf_IfbwdPMWetmCQXXXbIWR5DVA5FMNYMjh9njoVWQjr8YR_zn93tzkRDayK86UYv5jSEBn_4BJ08G9ikHC2i3Ohfodz4hPPYExq7izOHI88wnQmmirzgzZTP8TR-2F4gQJqRMdQkY4.rBiqOyWkyYhcNWFPh7ScDw",
+ "debugUrl": "https://www.browserbase.com/devtools-fullscreen/inspector.html?wss=connect.browserbase.com/debug/7ea1ba44-519c-4087-9227-c9b5bd949657/devtools/browser/"
+}
\ No newline at end of file
(oldest)
·
back to Google Places Key Provisioner
·
(newest)