mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
chore(freehire-search): point at freehire.me (domain migrated from freehire.dev) (#229)
freehire moved its primary domain from freehire.dev to freehire.me. Update the freehire-search skill's default API base URL, help text, docs, and examples. Backward-compatible: FREEHIRE_API_URL still overrides the base (self-hosting), and normalizeSlug is host-agnostic so pasted freehire.dev/jobs/<slug> URLs still resolve. The GitHub repo link (github.com/strelov1/freehire) is unchanged. All 27 CLI tests pass; the freehire.me API answers 200 for /jobs/search + /jobs/facets.
This commit is contained in:
@@ -3,7 +3,7 @@ name: freehire-search
|
||||
version: 1.0.0
|
||||
description: >
|
||||
Use this skill to search live software / tech / data / engineering job listings
|
||||
across many countries and markets (and remote) via the freehire.dev aggregator's
|
||||
across many countries and markets (and remote) via the freehire.me aggregator's
|
||||
public API, or to look up a specific posting. It aggregates roles from ~50 ATS
|
||||
platforms into one schema, so a single skill covers many markets — but its faceted
|
||||
filtering (skills, category, seniority) is tuned tech-first, so scope triggers to
|
||||
@@ -17,7 +17,7 @@ allowed-tools: Bash(bun run .agents/skills/freehire-search/cli/src/cli.ts *)
|
||||
|
||||
# freehire Search Skill
|
||||
|
||||
Search live job listings from the **[freehire.dev](https://freehire.dev)** job
|
||||
Search live job listings from the **[freehire.me](https://freehire.me)** job
|
||||
aggregator — an open-source IT job board that normalizes postings from ~50 ATS
|
||||
platforms across many countries into one schema. No authentication, no API key,
|
||||
and **zero runtime dependencies** — it runs with just `bun`. The market is chosen
|
||||
@@ -40,10 +40,10 @@ coverage exists but is still maturing; don't rely on this skill for general
|
||||
|
||||
## ℹ️ Hosted-service dependency (best-effort, no SLA)
|
||||
|
||||
This skill depends on a third-party hosted service, freehire.dev. Reads are
|
||||
This skill depends on a third-party hosted service, freehire.me. Reads are
|
||||
**public and unauthenticated** — the same zero-signup bar as `linkedin-search`.
|
||||
|
||||
**freehire.dev is a personal project but actively maintained; it runs on a
|
||||
**freehire.me is a personal project but actively maintained; it runs on a
|
||||
best-effort basis (no formal SLA).** If the API is unreachable, the CLI fails
|
||||
gracefully — a non-zero exit with a clear error message — so an outage degrades
|
||||
this source rather than breaking the surrounding workflow.
|
||||
@@ -52,7 +52,7 @@ this source rather than breaking the surrounding workflow.
|
||||
MIT-licensed repo — [`strelov1/freehire`](https://github.com/strelov1/freehire)
|
||||
(Go + PostgreSQL + Meilisearch) — that stands up with one command via Docker
|
||||
Compose (`make up` → API on `:8080`, same `/api/v1/...` paths). The skill honors a
|
||||
base-URL env var, `FREEHIRE_API_URL` (default `https://freehire.dev`), so pointing
|
||||
base-URL env var, `FREEHIRE_API_URL` (default `https://freehire.me`), so pointing
|
||||
it at a local instance is a one-line change:
|
||||
|
||||
```bash
|
||||
@@ -99,7 +99,7 @@ Facet filters (values come from freehire's controlled vocabularies; comma-separa
|
||||
> **Location is a facet, not free text.** Unlike `linkedin-search`'s `--location`,
|
||||
> freehire filters geography through the structured `--region`/`--country`/`--city`
|
||||
> facets. Discover the live values for a market at
|
||||
> [`/api/v1/jobs/facets`](https://freehire.dev/api/v1/jobs/facets) (append `?q=<role>`
|
||||
> [`/api/v1/jobs/facets`](https://freehire.me/api/v1/jobs/facets) (append `?q=<role>`
|
||||
> to scope it) — never invent facet values.
|
||||
|
||||
### Fetch full job detail
|
||||
@@ -109,7 +109,7 @@ bun run .agents/skills/freehire-search/cli/src/cli.ts detail <slug|url> [--forma
|
||||
```
|
||||
|
||||
`slug` is the `id` from a `search` result (e.g. `golang-zensar-2bxu6dxm`). You may
|
||||
also pass a full `https://freehire.dev/jobs/<slug>` URL. Returns the full (HTML-stripped)
|
||||
also pass a full `https://freehire.me/jobs/<slug>` URL. Returns the full (HTML-stripped)
|
||||
description, skills, region/country, and — when the posting is enriched — seniority,
|
||||
category, employment type, and salary.
|
||||
|
||||
@@ -163,7 +163,7 @@ dictionaries never guess). So:
|
||||
|
||||
## Notes
|
||||
|
||||
- Data is from freehire.dev's public API — no credentials required. Only per-user
|
||||
- Data is from freehire.me's public API — no credentials required. Only per-user
|
||||
tracking (apply/save) needs a key, and this skill deliberately does not touch it:
|
||||
it is **search + detail only**.
|
||||
- `id` in search results is the freehire `public_slug` — pass it as-is to `detail`.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# freehire-cli
|
||||
|
||||
CLI for searching the [freehire.dev](https://freehire.dev) job aggregator across
|
||||
CLI for searching the [freehire.me](https://freehire.me) job aggregator across
|
||||
**many markets** (tech-focused), via its public JSON API.
|
||||
|
||||
**Data source**: freehire.dev REST API (`/api/v1/jobs/search`, `/api/v1/jobs/facets`, `/api/v1/jobs/{slug}`).
|
||||
**Data source**: freehire.me REST API (`/api/v1/jobs/search`, `/api/v1/jobs/facets`, `/api/v1/jobs/{slug}`).
|
||||
**Authentication**: None required — reads are public (only tracking mutations need a key, and those are out of scope here).
|
||||
**Dependencies**: None (plain `bun` + `fetch`). `bun install` is optional and only pulls dev type defs.
|
||||
|
||||
> **Hosted-service dependency.** This skill talks to freehire.dev, a personal
|
||||
> **Hosted-service dependency.** This skill talks to freehire.me, a personal
|
||||
> project maintained on a **best-effort basis with no formal SLA**. If the API is
|
||||
> unreachable the CLI exits non-zero with a clear error rather than hanging, so an
|
||||
> outage degrades gracefully instead of breaking the caller. Point `FREEHIRE_API_URL`
|
||||
@@ -25,7 +25,7 @@ The CLI runs without any install because it has zero runtime dependencies.
|
||||
|
||||
## Self-hosting / base URL
|
||||
|
||||
The base URL defaults to `https://freehire.dev` and is overridable with an env var:
|
||||
The base URL defaults to `https://freehire.me` and is overridable with an env var:
|
||||
|
||||
```bash
|
||||
FREEHIRE_API_URL=http://localhost:8080 bun run src/cli.ts search -q "go"
|
||||
@@ -83,5 +83,5 @@ See `../SKILL.md` for the full flag reference and the hosted-dependency note.
|
||||
|
||||
Facet values come from freehire's controlled vocabularies. Discover the live
|
||||
values (with counts) for a market at
|
||||
[`/api/v1/jobs/facets`](https://freehire.dev/api/v1/jobs/facets), or narrow it,
|
||||
e.g. `https://freehire.dev/api/v1/jobs/facets?q=react`.
|
||||
[`/api/v1/jobs/facets`](https://freehire.me/api/v1/jobs/facets), or narrow it,
|
||||
e.g. `https://freehire.me/api/v1/jobs/facets?q=react`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "freehire-cli",
|
||||
"version": "1.0.0",
|
||||
"description": "CLI for searching the freehire.dev job aggregator's public JSON API across many markets (tech-focused) — no authentication, zero runtime dependencies. Base URL is swappable via FREEHIRE_API_URL for self-hosting.",
|
||||
"description": "CLI for searching the freehire.me job aggregator's public JSON API across many markets (tech-focused) — no authentication, zero runtime dependencies. Base URL is swappable via FREEHIRE_API_URL for self-hosting.",
|
||||
"type": "module",
|
||||
"main": "src/cli.ts",
|
||||
"bin": {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bun
|
||||
// Self-contained CLI for searching the freehire.dev aggregator's public JSON API.
|
||||
// Self-contained CLI for searching the freehire.me aggregator's public JSON API.
|
||||
// No external CLI framework and zero runtime dependencies, so it runs anywhere
|
||||
// `bun` is available with nothing installed beyond the repo clone.
|
||||
//
|
||||
// Hosted-service dependency: reads are public (no API key), but they hit
|
||||
// freehire.dev — a personal project maintained best-effort (no formal SLA). Point
|
||||
// freehire.me — a personal project maintained best-effort (no formal SLA). Point
|
||||
// FREEHIRE_API_URL at a self-hosted freehire backend to swap the source.
|
||||
|
||||
import { runSearch, type SearchOpts } from "./commands/search.js"
|
||||
@@ -69,7 +69,7 @@ function commaList(raw: FlagValue): string[] {
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
const HELP = `freehire-cli — search the freehire.dev job aggregator (many markets, tech-focused)
|
||||
const HELP = `freehire-cli — search the freehire.me job aggregator (many markets, tech-focused)
|
||||
|
||||
USAGE
|
||||
bun run src/cli.ts search [-q "<keywords>"] [facet flags] [--format json|table|plain]
|
||||
@@ -82,7 +82,7 @@ SEARCH FLAGS
|
||||
--limit, -n <n> Results per page (API limit). Default 25.
|
||||
--format <fmt> json (default) | table | plain.
|
||||
|
||||
FACET FILTERS (values from freehire.dev's controlled vocabularies; comma = OR)
|
||||
FACET FILTERS (values from freehire.me's controlled vocabularies; comma = OR)
|
||||
--region <codes> Macro-region: global, eu, us, apac, latam, cis, ... e.g. --region eu,us
|
||||
--country <codes> ISO-3166 alpha-2, e.g. --country DE,GB
|
||||
--city <names> City name(s), e.g. --city Berlin
|
||||
@@ -95,7 +95,7 @@ FACET FILTERS (values from freehire.dev's controlled vocabularies; comma = OR)
|
||||
|
||||
DETAIL
|
||||
<slug|url> A freehire public slug (from a search result's id/slug)
|
||||
or a full https://freehire.dev/jobs/<slug> URL.
|
||||
or a full https://freehire.me/jobs/<slug> URL.
|
||||
|
||||
EXAMPLES
|
||||
bun run src/cli.ts search -q "backend engineer" --seniority senior --limit 10 --format table
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Data source: the freehire.dev public REST API (JSON, `{data, meta}` envelope).
|
||||
// Data source: the freehire.me public REST API (JSON, `{data, meta}` envelope).
|
||||
// Reads are unauthenticated — no API key, the same bar as linkedin-search — and
|
||||
// unlike the HTML-scraping portals there is no markup to parse: we fetch JSON and
|
||||
// reshape it into the portal-skill contract's result fields. The base URL is
|
||||
// swappable via FREEHIRE_API_URL for self-hosting.
|
||||
|
||||
export const DEFAULT_BASE_URL = "https://freehire.dev"
|
||||
export const DEFAULT_BASE_URL = "https://freehire.me"
|
||||
|
||||
/** API base URL: FREEHIRE_API_URL (for a self-hosted instance) or the default. */
|
||||
export function baseUrl(): string {
|
||||
@@ -16,7 +16,7 @@ export function writeError(error: string, code: string): void {
|
||||
process.stderr.write(JSON.stringify({ error, code }) + "\n")
|
||||
}
|
||||
|
||||
const UA = "freehire-search-skill/1.0 (+https://freehire.dev)"
|
||||
const UA = "freehire-search-skill/1.0 (+https://freehire.me)"
|
||||
|
||||
/** The shared API response envelope: {data, meta, error}. */
|
||||
export interface Envelope<T> {
|
||||
|
||||
@@ -93,7 +93,7 @@ describe("normalizeSlug", () => {
|
||||
expect(normalizeSlug("golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
|
||||
});
|
||||
test("extracts the slug from a /jobs/<slug> URL", () => {
|
||||
expect(normalizeSlug("https://freehire.dev/jobs/golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
|
||||
expect(normalizeSlug("https://freehire.me/jobs/golang-zensar-2bxu6dxm")).toBe("golang-zensar-2bxu6dxm");
|
||||
});
|
||||
test("rejects a non-slug string", () => {
|
||||
expect(normalizeSlug("not a slug!")).toBeNull();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# freehire.dev API reference
|
||||
# freehire.me API reference
|
||||
|
||||
The endpoints, parameters, and response shapes this skill depends on. This is the
|
||||
file to update if the freehire API changes. Base URL defaults to
|
||||
`https://freehire.dev` and is overridable via the `FREEHIRE_API_URL` env var.
|
||||
`https://freehire.me` and is overridable via the `FREEHIRE_API_URL` env var.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
Reference in New Issue
Block a user