builtwithcodex
Advanced tools
@@ -10,3 +10,3 @@ #!/usr/bin/env node | ||
| if (!existsSync(script)) { | ||
| console.error("Missing bundled Launchboard publish script."); | ||
| console.error("Missing bundled Built with Codex publish script."); | ||
| process.exit(1); | ||
@@ -13,0 +13,0 @@ } |
+3
-4
| { | ||
| "name": "builtwithcodex", | ||
| "version": "0.1.1", | ||
| "description": "Publish repos, SaaS products, Codex skills, agents, games, and devtools to Launchboard from Codex or the terminal.", | ||
| "version": "0.1.3", | ||
| "description": "Publish Codex skills to Built with Codex from Codex or the terminal.", | ||
| "bin": { | ||
@@ -19,5 +19,4 @@ "builtwithcodex": "bin/launchboard-publish.js", | ||
| "marketplace", | ||
| "product-hunt", | ||
| "launchboard", | ||
| "developer-tools" | ||
| "codex-skill" | ||
| ], | ||
@@ -24,0 +23,0 @@ "author": "Francesco Mistero", |
+19
-14
| # builtwithcodex | ||
| Publish a repo, SaaS URL, Codex skill, agent, game, experiment, or devtool to Launchboard from Codex or the terminal. | ||
| Publish a Codex skill to Built with Codex from Codex or the terminal. | ||
| ## Usage | ||
| Publish the current repo: | ||
| Publish the current skill repo: | ||
@@ -13,18 +13,18 @@ ```bash | ||
| Publish a live SaaS/app URL: | ||
| Attach docs/demo URL: | ||
| ```bash | ||
| npx builtwithcodex \ | ||
| --url "https://your-app.com" \ | ||
| --repo-url "https://github.com/you/your-app" | ||
| --url "https://github.com/you/your-skill#readme" \ | ||
| --repo-url "https://github.com/you/your-skill" | ||
| ``` | ||
| Publish from a GitHub repo URL: | ||
| Publish from a GitHub skill repo URL: | ||
| ```bash | ||
| npx builtwithcodex \ | ||
| --repo-url "https://github.com/you/your-project" | ||
| --repo-url "https://github.com/you/your-skill" | ||
| ``` | ||
| Use a custom Launchboard instance: | ||
| Use a custom Built with Codex instance: | ||
@@ -42,6 +42,5 @@ ```bash | ||
| --path "$PWD" \ | ||
| --category Apps \ | ||
| --demo-url "https://your-app.com" \ | ||
| --screenshot-url "https://your-app.com/og.png" \ | ||
| --codex-skill-url "https://your-app.com/skills/my-skill.zip" | ||
| --demo-url "https://github.com/you/your-skill#readme" \ | ||
| --screenshot-url "https://raw.githubusercontent.com/you/your-skill/main/screenshot.png" \ | ||
| --codex-skill-url "https://github.com/you/your-skill" | ||
| ``` | ||
@@ -60,7 +59,13 @@ | ||
| ```text | ||
| Use the builtwithcodex npm package to publish this project to Launchboard. | ||
| Use the builtwithcodex npm package to publish this Codex skill. | ||
| ``` | ||
| The package opens a prefilled Launchboard submit page. The user still signs in with GitHub and confirms publish, so ownership, limits, and payments stay enforced by Launchboard. | ||
| The package opens a prefilled Built with Codex submit page. The user still signs in with GitHub and confirms publish, so ownership, limits, and payments stay enforced by the marketplace. | ||
| By default, the package opens: | ||
| ```text | ||
| https://codex-marketplace.vercel.app | ||
| ``` | ||
| ## Bundled Codex Skill | ||
@@ -67,0 +72,0 @@ |
@@ -14,7 +14,8 @@ #!/usr/bin/env python3 | ||
| from urllib.request import Request, urlopen | ||
| from urllib.parse import urlencode | ||
| from urllib.parse import urlencode, urljoin | ||
| CATEGORIES = {"Apps", "Skills", "DevTools", "Games", "Agents", "Experiments"} | ||
| CATEGORIES = {"Skills"} | ||
| IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".gif"} | ||
| DEFAULT_MARKETPLACE_URL = "https://codex-marketplace.vercel.app" | ||
@@ -78,2 +79,13 @@ | ||
| def github_owner(url: str) -> str: | ||
| normalized = normalize_github_url(url) | ||
| match = re.match(r"https://github\.com/([^/]+)/", normalized) | ||
| return match.group(1) if match else "" | ||
| def github_avatar_url(repo_url: str) -> str: | ||
| owner = github_owner(repo_url) | ||
| return f"https://github.com/{owner}.png" if owner else "" | ||
| def clone_repo(repo_url: str) -> Path: | ||
@@ -164,21 +176,12 @@ normalized = normalize_github_url(repo_url) | ||
| } | ||
| def infer_category(path: Path, package: dict, readme: str) -> str: | ||
| focused_readme = "\n".join(readme.splitlines()[:20]) | ||
| text = " ".join([str(package.get("name", "")), str(package.get("description", "")), focused_readme]).lower() | ||
| if "marketplace" in text or "saas" in text or "web app" in text: | ||
| return "Apps" | ||
| if "skill.md" in text or (path / "SKILL.md").exists(): | ||
| return "Skills" | ||
| if "phaser" in text or "game" in text: | ||
| return "Games" | ||
| if "cli" in text or "devtool" in text or "developer" in text: | ||
| return "DevTools" | ||
| if "agent" in text: | ||
| return "Agents" | ||
| if "experiment" in text or "prototype" in text: | ||
| return "Experiments" | ||
| return "Apps" | ||
| def infer_category(path: Path, package: dict, readme: str, repo_url: str = "") -> str: | ||
| return "Skills" | ||
| def install_command(package: dict, path: Path) -> str: | ||
| readme = read(path / "README.md") | ||
| readme_command = infer_install_command_from_readme(readme) | ||
| if readme_command: | ||
| return readme_command | ||
| if package: | ||
@@ -205,4 +208,38 @@ manager = "npm" | ||
| def infer_install_command_from_readme(readme: str) -> str: | ||
| if not readme: | ||
| return "" | ||
| commands = re.findall(r"```(?:bash|sh|shell|zsh)?\n(.*?)```", readme, re.DOTALL | re.IGNORECASE) | ||
| flattened = [] | ||
| for block in commands: | ||
| for line in block.splitlines(): | ||
| value = line.strip() | ||
| if value and not value.startswith("#"): | ||
| flattened.append(value) | ||
| preferred_patterns = ( | ||
| r"^npx\s+.*", | ||
| r"^npm\s+(?:i|install)\s+-g\s+.*", | ||
| r"^npm\s+(?:i|install)\s+.*@", | ||
| r"^pnpm\s+dlx\s+.*", | ||
| r"^bunx\s+.*", | ||
| r"^uvx\s+.*", | ||
| r"^pipx\s+install\s+.*", | ||
| ) | ||
| for pattern in preferred_patterns: | ||
| for command in flattened: | ||
| if re.match(pattern, command): | ||
| return command | ||
| for command in flattened: | ||
| if "install" in command.lower() and not command.startswith(("mkdir ", "cp ", "ls ")): | ||
| return command | ||
| return "" | ||
| def infer_tags(path: Path, package: dict, category: str) -> list[str]: | ||
| tags = ["codex", category.lower()] | ||
| tags = ["codex", "skill"] | ||
| dependencies = {} | ||
@@ -255,2 +292,4 @@ for key in ("dependencies", "devDependencies"): | ||
| value = match.group(0) | ||
| if "github.com" in value.lower(): | ||
| return "" | ||
| return "" if is_badge_url(value) else value | ||
@@ -301,11 +340,13 @@ | ||
| or first_paragraph(skill_md) | ||
| or f"{name} is ready to launch." | ||
| or f"{name} is a Codex skill ready to launch." | ||
| ) | ||
| description = clean(description, 1200) | ||
| tagline = clean(description.split(".")[0], 140) | ||
| category = category_override if category_override in CATEGORIES else infer_category(path, package, readme) | ||
| repo_url = git_remote(path) | ||
| category = "Skills" | ||
| inferred_demo_url = demo_url or infer_demo_url(package, readme) | ||
| inferred_screenshot_url = screenshot_url or infer_screenshot_url(path, readme) | ||
| inferred_skill_url = codex_skill_url or (repo_url if category == "Skills" and repo_url else "") | ||
| inferred_skill_url = codex_skill_url or repo_url | ||
| if inferred_demo_url.startswith("https://github.com/"): | ||
| inferred_demo_url = "" | ||
@@ -315,4 +356,4 @@ return { | ||
| "tagline": tagline if len(tagline) >= 8 else f"{name} is ready to launch", | ||
| "description": description if len(description) >= 20 else f"{name} is a project published on Launchboard.", | ||
| "category": category if category in CATEGORIES else "Apps", | ||
| "description": description if len(description) >= 20 else f"{name} is a Codex skill published on Built with Codex.", | ||
| "category": "Skills", | ||
| "tags": infer_tags(path, package, category), | ||
@@ -344,4 +385,6 @@ "repoUrl": repo_url, | ||
| prefill["repoUrl"] = normalize_github_url(repo_url) | ||
| if prefill["category"] == "Skills" and not prefill.get("codexSkillUrl"): | ||
| if not prefill.get("codexSkillUrl"): | ||
| prefill["codexSkillUrl"] = normalize_github_url(repo_url) | ||
| if not prefill.get("screenshotUrl"): | ||
| prefill["screenshotUrl"] = github_avatar_url(repo_url) | ||
| return prefill | ||
@@ -365,10 +408,10 @@ finally: | ||
| image = parser.meta.get("og:image") or parser.meta.get("twitter:image") or "" | ||
| category = category_override if category_override in CATEGORIES else "Apps" | ||
| if image: | ||
| image = urljoin(url, image) | ||
| return { | ||
| "name": title, | ||
| "tagline": clean(description.split(".")[0], 140), | ||
| "description": description if len(description) >= 20 else f"{title} is a SaaS product published on Launchboard.", | ||
| "category": category, | ||
| "tags": ["codex", category.lower(), "saas"], | ||
| "description": description if len(description) >= 20 else f"{title} is a Codex skill published on Built with Codex.", | ||
| "category": "Skills", | ||
| "tags": ["codex", "skill"], | ||
| "repoUrl": repo_url, | ||
@@ -383,3 +426,3 @@ "demoUrl": url, | ||
| def print_preview(prefill: dict, url: str) -> None: | ||
| print("Launchboard prefill") | ||
| print("Built with Codex skill prefill") | ||
| print(f"Name: {prefill.get('name', '')}") | ||
@@ -401,5 +444,6 @@ print(f"Tagline: {prefill.get('tagline', '')}") | ||
| parser.add_argument("--path", default=os.getcwd()) | ||
| parser.add_argument("--url", default="", help="Live SaaS/app URL to launch instead of a local path") | ||
| parser.add_argument("--repo-url", default="", help="GitHub repo URL to inspect, or to attach when launching a live URL") | ||
| parser.add_argument("--marketplace-url", default="http://localhost:3007") | ||
| parser.add_argument("--url", default="", help="Docs or demo URL to attach instead of inspecting a local path") | ||
| parser.add_argument("--repo-url", default="", help="GitHub skill repo URL to inspect, or to attach when using --url") | ||
| parser.add_argument("--repo", default="", help=argparse.SUPPRESS) | ||
| parser.add_argument("--marketplace-url", default=DEFAULT_MARKETPLACE_URL) | ||
| parser.add_argument("--demo-url", default="") | ||
@@ -411,3 +455,6 @@ parser.add_argument("--screenshot-url", default="") | ||
| parser.add_argument("--no-open", action="store_true") | ||
| parser.add_argument("--open", action="store_true", help=argparse.SUPPRESS) | ||
| parser.add_argument("--prefill-url", action="store_true", help=argparse.SUPPRESS) | ||
| args = parser.parse_args() | ||
| repo_url = args.repo_url or args.repo | ||
@@ -417,9 +464,9 @@ if args.url: | ||
| args.url, | ||
| repo_url=args.repo_url, | ||
| repo_url=repo_url, | ||
| category_override=args.category, | ||
| codex_skill_url=args.codex_skill_url, | ||
| ) | ||
| elif args.repo_url: | ||
| elif repo_url: | ||
| prefill = build_prefill_from_repo_url( | ||
| args.repo_url, | ||
| repo_url, | ||
| demo_url=args.demo_url, | ||
@@ -426,0 +473,0 @@ screenshot_url=args.screenshot_url, |
+15
-17
| --- | ||
| name: builtwithcodex | ||
| description: Inspect the current project and open a prefilled Launchboard marketplace submission page. | ||
| description: Inspect the current Codex skill repo and open a prefilled Built with Codex marketplace submission page. | ||
| --- | ||
@@ -8,3 +8,3 @@ | ||
| Use this skill when the user wants to publish the current repo, app, Codex skill, agent, game, experiment, or devtool to the marketplace. | ||
| Use this skill when the user wants to publish a Codex skill from the current repo, a GitHub repo URL, or an attached docs/demo URL to the marketplace. | ||
@@ -23,11 +23,11 @@ ## Workflow | ||
| - tagline | ||
| - category | ||
| - category, always `Skills` | ||
| - tags | ||
| - description | ||
| - GitHub repo URL | ||
| - demo URL | ||
| - Codex skill download/install URL when the project itself is a skill | ||
| - docs/demo URL | ||
| - Codex skill source or install URL | ||
| - screenshot URL if present in README or common asset folders | ||
| - install command | ||
| 3. Open the marketplace submit page with a `prefill` query parameter. | ||
| 3. Open the Built with Codex submit page with a `prefill` query parameter. | ||
| 4. The user must sign in with GitHub before publishing. Do not bypass marketplace authentication or limits. | ||
@@ -38,3 +38,3 @@ | ||
| ```bash | ||
| python3 scripts/submit_project.py --path "$PWD" --marketplace-url "http://localhost:3007" | ||
| python3 scripts/submit_project.py --path "$PWD" | ||
| ``` | ||
@@ -47,15 +47,13 @@ | ||
| --path "$PWD" \ | ||
| --marketplace-url "http://localhost:3007" \ | ||
| --demo-url "https://your-app.vercel.app" \ | ||
| --screenshot-url "https://your-app.vercel.app/og.png" \ | ||
| --codex-skill-url "https://your-domain.com/skills/my-skill.zip" \ | ||
| --category Apps | ||
| --demo-url "https://your-docs-or-demo.com" \ | ||
| --screenshot-url "https://your-docs-or-demo.com/og.png" \ | ||
| --codex-skill-url "https://github.com/you/your-skill" | ||
| ``` | ||
| Launch a live SaaS URL: | ||
| Attach a docs/demo URL: | ||
| ```bash | ||
| python3 scripts/submit_project.py \ | ||
| --url "https://your-app.vercel.app" \ | ||
| --repo-url "https://github.com/you/your-app" | ||
| --url "https://your-docs-or-demo.com" \ | ||
| --repo-url "https://github.com/you/your-skill" | ||
| ``` | ||
@@ -67,3 +65,3 @@ | ||
| python3 scripts/submit_project.py \ | ||
| --repo-url "https://github.com/you/your-project" | ||
| --repo-url "https://github.com/you/your-skill" | ||
| ``` | ||
@@ -80,3 +78,3 @@ | ||
| ```bash | ||
| python3 scripts/submit_project.py --path "$PWD" --marketplace-url "https://YOUR_DOMAIN" | ||
| python3 scripts/submit_project.py --path "$PWD" | ||
| ``` |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
20988
5.37%427
9.21%77
6.94%4
33.33%