terminalhire 0.42.6 → 0.42.7
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/README.md +36 -35
- package/dist/bin/cache-store.js +5 -1
- package/dist/bin/jpi-bounties.js +25 -5
- package/dist/bin/jpi-chat.js +62 -12
- package/dist/bin/jpi-claim.js +33 -5
- package/dist/bin/jpi-config.js +5 -1
- package/dist/bin/jpi-contribute.js +23 -4
- package/dist/bin/jpi-devs.js +20 -1
- package/dist/bin/jpi-dispatch.js +5906 -749
- package/dist/bin/jpi-hub.js +25 -8
- package/dist/bin/jpi-inbox.js +62 -12
- package/dist/bin/jpi-init.js +3 -3
- package/dist/bin/jpi-intro.js +62 -12
- package/dist/bin/jpi-jobs.js +28 -6
- package/dist/bin/jpi-learn.js +20 -1
- package/dist/bin/jpi-link.js +5 -1
- package/dist/bin/jpi-login.js +20 -1
- package/dist/bin/jpi-mcp.js +33 -5
- package/dist/bin/jpi-profile.js +45 -6
- package/dist/bin/jpi-refresh.js +25 -2
- package/dist/bin/jpi-run.js +5106 -12
- package/dist/bin/jpi-save.js +20 -1
- package/dist/bin/jpi-sync.js +20 -1
- package/dist/bin/jpi-trajectory.js +21 -2
- package/dist/bin/proxy/egressProxy.js +117 -0
- package/dist/bin/proxy/proxyEntry.js +46 -0
- package/dist/sandbox/fence-install.sb +129 -0
- package/dist/sandbox/fence-offline.sb +185 -0
- package/dist/src/intro.js +62 -12
- package/dist/src/profile.js +19 -0
- package/dist/src/trajectory.js +21 -2
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# terminalhire — your code is your résumé
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Job + bounty matching in the terminal, and a **Proof of Work** credential earned from PRs that maintainers who aren't you merged into established repos — checkable on GitHub, impossible to self-report. Claim a scoped bounty and get paid when the poster accepts it.
|
|
4
4
|
|
|
5
5
|
**Domain:** [terminalhire.com](https://terminalhire.com)
|
|
6
6
|
|
|
@@ -57,10 +57,10 @@ terminalhire trajectory # trajectory from your local Claude Code corpus
|
|
|
57
57
|
terminalhire trajectory --export # write a derived score + Markdown to ~/.terminalhire/
|
|
58
58
|
terminalhire trajectory --inward # also show private rework/recovery (never exported)
|
|
59
59
|
|
|
60
|
-
terminalhire devs # opted-in peers & founders —
|
|
60
|
+
terminalhire devs # opted-in peers & founders — matched on your machine
|
|
61
61
|
terminalhire intro <login> # request a double-opt-in intro (typed-yes consent)
|
|
62
62
|
terminalhire inbox # unread chats, connections & pending intros in one place
|
|
63
|
-
terminalhire mcp --print-config #
|
|
63
|
+
terminalhire mcp --print-config # match + claim-record MCP config for AI hosts
|
|
64
64
|
|
|
65
65
|
terminalhire profile --show # inspect your encrypted local profile (incl. GitHub fields)
|
|
66
66
|
terminalhire profile --edit # set displayName, contactEmail, prefs
|
|
@@ -84,18 +84,19 @@ This runs the **GitHub OAuth Device Flow**:
|
|
|
84
84
|
|
|
85
85
|
### What GitHub enriches
|
|
86
86
|
|
|
87
|
-
| Signal
|
|
88
|
-
|
|
89
|
-
| Skill tags
|
|
87
|
+
| Signal | Source |
|
|
88
|
+
| ------------------ | ------------------------------------------------------------------------------------ |
|
|
89
|
+
| Skill tags | Repo languages + repo topics, filtered through `vocabulary.normalize()` |
|
|
90
90
|
| Seniority estimate | Account age × (repos + followers) per [documented thresholds](#seniority-thresholds) |
|
|
91
|
-
| Display name
|
|
92
|
-
| Contact email
|
|
91
|
+
| Display name | `user.name` if not already set in profile |
|
|
92
|
+
| Contact email | `user.email` if public and not already set in profile |
|
|
93
93
|
|
|
94
94
|
### Public scope guarantee
|
|
95
95
|
|
|
96
96
|
Scope requested: **`read:user`** — public profile + public repos only.
|
|
97
97
|
|
|
98
98
|
**Private-repo scopes are NEVER requested.** This means:
|
|
99
|
+
|
|
99
100
|
- No employer-IP risk from private repos.
|
|
100
101
|
- No code, secrets, or private file access.
|
|
101
102
|
- Only what is already publicly visible on your GitHub profile.
|
|
@@ -199,13 +200,13 @@ GitHub (optional enrichment):
|
|
|
199
200
|
|
|
200
201
|
## Privacy
|
|
201
202
|
|
|
202
|
-
| What happens locally
|
|
203
|
-
|
|
204
|
-
| Profile accumulated from personal project sessions
|
|
205
|
-
| Closed-vocab skill tags + seniority (encrypted at rest)
|
|
206
|
-
| GitHub token + public profile data (encrypted at rest)
|
|
207
|
-
| Employer-repo sessions: language tags only
|
|
208
|
-
| git email domain + remote host (employer detection only) |
|
|
203
|
+
| What happens locally | What crosses the wire |
|
|
204
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
205
|
+
| Profile accumulated from personal project sessions | GET /api/index — anonymous, no dev data |
|
|
206
|
+
| Closed-vocab skill tags + seniority (encrypted at rest) | POST /api/lead — ONLY after explicit per-role "yes" consent |
|
|
207
|
+
| GitHub token + public profile data (encrypted at rest) | GitHub fields in lead — ONLY after consent AND GitHub is connected |
|
|
208
|
+
| Employer-repo sessions: language tags only | Nothing else |
|
|
209
|
+
| git email domain + remote host (employer detection only) | |
|
|
209
210
|
|
|
210
211
|
**Employer-repo exclusion (default on):** if `git config user.email` is a corporate domain or `git remote` points to a non-personal host, only language-level tags (`typescript`, `python`, …) accumulate. Fine-grained framework/infra tags are excluded. The flag `hasEmployerSessions` is recorded locally but never emitted.
|
|
211
212
|
|
|
@@ -280,9 +281,9 @@ Typing anything other than `y` or `yes` aborts with no network call.
|
|
|
280
281
|
|
|
281
282
|
## Apply modes
|
|
282
283
|
|
|
283
|
-
| `applyMode`
|
|
284
|
-
|
|
285
|
-
| `direct`
|
|
284
|
+
| `applyMode` | What happens when you select a role |
|
|
285
|
+
| ------------ | ------------------------------------------------------------------------- |
|
|
286
|
+
| `direct` | Opens the employer's public URL in your terminal. No data sent. |
|
|
286
287
|
| `buyer-lead` | Triggers the named-entity consent flow above. Nothing sent without "yes". |
|
|
287
288
|
|
|
288
289
|
The two modes are never silently conflated.
|
|
@@ -295,13 +296,13 @@ node test/zero-egress.test.js
|
|
|
295
296
|
|
|
296
297
|
## Environment variables
|
|
297
298
|
|
|
298
|
-
| Variable
|
|
299
|
-
|
|
300
|
-
| `TERMINALHIRE_API_URL`
|
|
301
|
-
| `GITHUB_CLIENT_ID`
|
|
302
|
-
| `GITHUB_CLIENT_SECRET`
|
|
303
|
-
| `GITHUB_DEVICE_CLIENT_ID`
|
|
304
|
-
| `TERMINALHIRE_GITHUB_MOCK` | `0`
|
|
299
|
+
| Variable | Default | Description |
|
|
300
|
+
| -------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| `TERMINALHIRE_API_URL` | `https://terminalhire.com` | Base URL for `/api/index` and `/api/lead` (also accepts legacy `JPI_API_URL`) |
|
|
302
|
+
| `GITHUB_CLIENT_ID` | _(required for real OAuth)_ | GitHub OAuth App client ID (device flow) |
|
|
303
|
+
| `GITHUB_CLIENT_SECRET` | _(optional)_ | GitHub OAuth App client secret (public apps omit this) |
|
|
304
|
+
| `GITHUB_DEVICE_CLIENT_ID` | _(falls back to `GITHUB_CLIENT_ID`)_ | Device-flow specific client ID if different |
|
|
305
|
+
| `TERMINALHIRE_GITHUB_MOCK` | `0` | Set to `1` to skip real OAuth and use the fixture (dev/CI); also accepts legacy `JPI_GITHUB_MOCK` |
|
|
305
306
|
|
|
306
307
|
## File layout
|
|
307
308
|
|
|
@@ -326,13 +327,13 @@ apps/cli/
|
|
|
326
327
|
|
|
327
328
|
## How data is used: local default vs consented lead
|
|
328
329
|
|
|
329
|
-
| Data
|
|
330
|
-
|
|
331
|
-
| Skill tags
|
|
332
|
-
| Seniority
|
|
333
|
-
| Display name
|
|
334
|
-
| Contact email
|
|
335
|
-
| GitHub login / profileUrl | Stored in `profile.github` on `terminalhire login`
|
|
336
|
-
| GitHub topLanguages
|
|
337
|
-
| GitHub token
|
|
338
|
-
| Private repos
|
|
330
|
+
| Data | Default (local-enrichment) | Opt-in (consented lead) |
|
|
331
|
+
| ------------------------- | -------------------------------------------------------------- | --------------------------------------------------- |
|
|
332
|
+
| Skill tags | Stored encrypted locally, used for local matching only | Included in `approvedFields.skillTags` on "yes" |
|
|
333
|
+
| Seniority | Stored encrypted locally | Included in `approvedFields.seniorityBand` on "yes" |
|
|
334
|
+
| Display name | Stored encrypted locally (pre-filled from GitHub if public) | Included if set and "yes" |
|
|
335
|
+
| Contact email | Stored encrypted locally (pre-filled from GitHub public email) | Included if set and "yes" |
|
|
336
|
+
| GitHub login / profileUrl | Stored in `profile.github` on `terminalhire login` | Included in `approvedFields.github` ONLY on "yes" |
|
|
337
|
+
| GitHub topLanguages | Used locally for tag inference (closed vocab) | Included in `approvedFields.github` ONLY on "yes" |
|
|
338
|
+
| GitHub token | Encrypted at `~/.terminalhire/github-token.enc` | Never sent |
|
|
339
|
+
| Private repos | Never accessed (scope: read:user) | N/A |
|
package/dist/bin/cache-store.js
CHANGED
|
@@ -77,7 +77,11 @@ function updateIndexCache(patch) {
|
|
|
77
77
|
renameSync(tmp, INDEX_CACHE_FILE);
|
|
78
78
|
return entry;
|
|
79
79
|
}
|
|
80
|
+
function writeIndexCache(index) {
|
|
81
|
+
return updateIndexCache({ index, indexETag: "" });
|
|
82
|
+
}
|
|
80
83
|
export {
|
|
81
84
|
readCacheEntry,
|
|
82
|
-
updateIndexCache
|
|
85
|
+
updateIndexCache,
|
|
86
|
+
writeIndexCache
|
|
83
87
|
};
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -10540,7 +10540,8 @@ var init_state_dir = __esm({
|
|
|
10540
10540
|
var cache_store_exports = {};
|
|
10541
10541
|
__export(cache_store_exports, {
|
|
10542
10542
|
readCacheEntry: () => readCacheEntry,
|
|
10543
|
-
updateIndexCache: () => updateIndexCache
|
|
10543
|
+
updateIndexCache: () => updateIndexCache,
|
|
10544
|
+
writeIndexCache: () => writeIndexCache
|
|
10544
10545
|
});
|
|
10545
10546
|
import { readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
|
|
10546
10547
|
import { join as join2 } from "path";
|
|
@@ -10566,6 +10567,9 @@ function updateIndexCache(patch) {
|
|
|
10566
10567
|
renameSync(tmp, INDEX_CACHE_FILE);
|
|
10567
10568
|
return entry;
|
|
10568
10569
|
}
|
|
10570
|
+
function writeIndexCache(index) {
|
|
10571
|
+
return updateIndexCache({ index, indexETag: "" });
|
|
10572
|
+
}
|
|
10569
10573
|
var TERMINALHIRE_DIR, INDEX_CACHE_FILE, SCHEMA_VERSION2, tmpCounter;
|
|
10570
10574
|
var init_cache_store = __esm({
|
|
10571
10575
|
"bin/cache-store.js"() {
|
|
@@ -10861,6 +10865,7 @@ __export(profile_exports, {
|
|
|
10861
10865
|
accumulateTags: () => accumulateTags,
|
|
10862
10866
|
addSavedJob: () => addSavedJob,
|
|
10863
10867
|
deleteProfile: () => deleteProfile,
|
|
10868
|
+
isBlankProfile: () => isBlankProfile,
|
|
10864
10869
|
listSavedJobs: () => listSavedJobs,
|
|
10865
10870
|
profileToFingerprint: () => profileToFingerprint,
|
|
10866
10871
|
readProfile: () => readProfile,
|
|
@@ -10901,6 +10906,17 @@ function migrateTagWeights(profile) {
|
|
|
10901
10906
|
}
|
|
10902
10907
|
}
|
|
10903
10908
|
}
|
|
10909
|
+
function isBlankProfile(profile) {
|
|
10910
|
+
if (profile.skillTags.length > 0) return false;
|
|
10911
|
+
if (Object.keys(profile.tagWeights).length > 0) return false;
|
|
10912
|
+
for (const [key, value] of Object.entries(profile)) {
|
|
10913
|
+
if (DERIVED_KEYS.has(key)) continue;
|
|
10914
|
+
if (value === void 0 || value === null) continue;
|
|
10915
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
10916
|
+
return false;
|
|
10917
|
+
}
|
|
10918
|
+
return true;
|
|
10919
|
+
}
|
|
10904
10920
|
async function readProfile() {
|
|
10905
10921
|
const parsed = await profileStore.read();
|
|
10906
10922
|
migrateTagWeights(parsed);
|
|
@@ -10985,7 +11001,7 @@ function profileToFingerprint(profile) {
|
|
|
10985
11001
|
}
|
|
10986
11002
|
};
|
|
10987
11003
|
}
|
|
10988
|
-
var TERMINALHIRE_DIR3, PROFILE_FILE, profileStore, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
11004
|
+
var TERMINALHIRE_DIR3, PROFILE_FILE, profileStore, DERIVED_KEYS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
10989
11005
|
var init_profile = __esm({
|
|
10990
11006
|
"src/profile.ts"() {
|
|
10991
11007
|
"use strict";
|
|
@@ -10997,6 +11013,13 @@ var init_profile = __esm({
|
|
|
10997
11013
|
blank: blankProfile,
|
|
10998
11014
|
keyPolicy: "keytar-first-file-fallback"
|
|
10999
11015
|
});
|
|
11016
|
+
DERIVED_KEYS = /* @__PURE__ */ new Set([
|
|
11017
|
+
"version",
|
|
11018
|
+
"updatedAt",
|
|
11019
|
+
"skillTags",
|
|
11020
|
+
"tagWeights",
|
|
11021
|
+
"hasEmployerSessions"
|
|
11022
|
+
]);
|
|
11000
11023
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
11001
11024
|
"typescript",
|
|
11002
11025
|
"javascript",
|
|
@@ -11836,9 +11859,6 @@ function readIndexCache() {
|
|
|
11836
11859
|
return null;
|
|
11837
11860
|
}
|
|
11838
11861
|
}
|
|
11839
|
-
function writeIndexCache(index) {
|
|
11840
|
-
updateIndexCache({ index, indexETag: "" });
|
|
11841
|
-
}
|
|
11842
11862
|
async function fetchIndex() {
|
|
11843
11863
|
const cached = readIndexCache();
|
|
11844
11864
|
if (cached) return cached;
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -5591,6 +5591,7 @@ __export(profile_exports, {
|
|
|
5591
5591
|
accumulateTags: () => accumulateTags,
|
|
5592
5592
|
addSavedJob: () => addSavedJob,
|
|
5593
5593
|
deleteProfile: () => deleteProfile,
|
|
5594
|
+
isBlankProfile: () => isBlankProfile,
|
|
5594
5595
|
listSavedJobs: () => listSavedJobs,
|
|
5595
5596
|
profileToFingerprint: () => profileToFingerprint,
|
|
5596
5597
|
readProfile: () => readProfile,
|
|
@@ -5631,6 +5632,17 @@ function migrateTagWeights(profile) {
|
|
|
5631
5632
|
}
|
|
5632
5633
|
}
|
|
5633
5634
|
}
|
|
5635
|
+
function isBlankProfile(profile) {
|
|
5636
|
+
if (profile.skillTags.length > 0) return false;
|
|
5637
|
+
if (Object.keys(profile.tagWeights).length > 0) return false;
|
|
5638
|
+
for (const [key, value] of Object.entries(profile)) {
|
|
5639
|
+
if (DERIVED_KEYS.has(key)) continue;
|
|
5640
|
+
if (value === void 0 || value === null) continue;
|
|
5641
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
5642
|
+
return false;
|
|
5643
|
+
}
|
|
5644
|
+
return true;
|
|
5645
|
+
}
|
|
5634
5646
|
async function readProfile() {
|
|
5635
5647
|
const parsed = await profileStore.read();
|
|
5636
5648
|
migrateTagWeights(parsed);
|
|
@@ -5715,7 +5727,7 @@ function profileToFingerprint(profile) {
|
|
|
5715
5727
|
}
|
|
5716
5728
|
};
|
|
5717
5729
|
}
|
|
5718
|
-
var TERMINALHIRE_DIR7, PROFILE_FILE, profileStore, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
5730
|
+
var TERMINALHIRE_DIR7, PROFILE_FILE, profileStore, DERIVED_KEYS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
5719
5731
|
var init_profile = __esm({
|
|
5720
5732
|
"src/profile.ts"() {
|
|
5721
5733
|
"use strict";
|
|
@@ -5727,6 +5739,13 @@ var init_profile = __esm({
|
|
|
5727
5739
|
blank: blankProfile,
|
|
5728
5740
|
keyPolicy: "keytar-first-file-fallback"
|
|
5729
5741
|
});
|
|
5742
|
+
DERIVED_KEYS = /* @__PURE__ */ new Set([
|
|
5743
|
+
"version",
|
|
5744
|
+
"updatedAt",
|
|
5745
|
+
"skillTags",
|
|
5746
|
+
"tagWeights",
|
|
5747
|
+
"hasEmployerSessions"
|
|
5748
|
+
]);
|
|
5730
5749
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
5731
5750
|
"typescript",
|
|
5732
5751
|
"javascript",
|
|
@@ -5821,6 +5840,7 @@ function defaultIntroDeps() {
|
|
|
5821
5840
|
});
|
|
5822
5841
|
});
|
|
5823
5842
|
},
|
|
5843
|
+
isTTY: () => Boolean(process.stdin.isTTY),
|
|
5824
5844
|
fetchImpl: (...args) => globalThis.fetch(...args),
|
|
5825
5845
|
openBrowser: (url) => {
|
|
5826
5846
|
void Promise.resolve().then(() => (init_open_url(), open_url_exports)).then((m) => m.openInBrowser(url)).catch(() => {
|
|
@@ -5859,7 +5879,9 @@ async function runIntroRequest(args, overrides) {
|
|
|
5859
5879
|
const deps = { ...defaultIntroDeps(), ...overrides };
|
|
5860
5880
|
const targetLogin = args.targetLogin?.trim().replace(/^@/, "");
|
|
5861
5881
|
if (!targetLogin) {
|
|
5862
|
-
deps.errorLog(
|
|
5882
|
+
deps.errorLog(
|
|
5883
|
+
'\n Usage: terminalhire intro <github-login> [--note "..."] [--contact <email>] [--name "..."]\n'
|
|
5884
|
+
);
|
|
5863
5885
|
deps.exit(1);
|
|
5864
5886
|
return;
|
|
5865
5887
|
}
|
|
@@ -5886,7 +5908,16 @@ async function runIntroRequest(args, overrides) {
|
|
|
5886
5908
|
note: args.note
|
|
5887
5909
|
});
|
|
5888
5910
|
renderConsentCard(payload, deps);
|
|
5889
|
-
|
|
5911
|
+
if (!deps.isTTY()) {
|
|
5912
|
+
deps.errorLog(
|
|
5913
|
+
"\n stdin is not interactive \u2014 re-run `terminalhire intro` in a real terminal to confirm.\n"
|
|
5914
|
+
);
|
|
5915
|
+
deps.exit(1);
|
|
5916
|
+
return;
|
|
5917
|
+
}
|
|
5918
|
+
const answer = await deps.prompt(
|
|
5919
|
+
' Type "yes" to send this intro request (anything else cancels): '
|
|
5920
|
+
);
|
|
5890
5921
|
if (answer !== "yes") {
|
|
5891
5922
|
deps.log("\n Cancelled \u2014 nothing was sent.\n");
|
|
5892
5923
|
deps.exit(0);
|
|
@@ -5954,9 +5985,13 @@ async function runIntroRequest(args, overrides) {
|
|
|
5954
5985
|
} catch {
|
|
5955
5986
|
}
|
|
5956
5987
|
if (deduped) {
|
|
5957
|
-
deps.log(
|
|
5958
|
-
|
|
5959
|
-
|
|
5988
|
+
deps.log(
|
|
5989
|
+
`
|
|
5990
|
+
You already have a pending intro request to @${targetLogin} \u2014 nothing new was sent.`
|
|
5991
|
+
);
|
|
5992
|
+
deps.log(
|
|
5993
|
+
" They can accept it any time from `terminalhire intro --list` or their dashboard.\n"
|
|
5994
|
+
);
|
|
5960
5995
|
} else if (notified) {
|
|
5961
5996
|
deps.log(`
|
|
5962
5997
|
Intro request sent to @${targetLogin}. They will see only your public login`);
|
|
@@ -6001,9 +6036,11 @@ async function runIntroDecision(args, overrides) {
|
|
|
6001
6036
|
try {
|
|
6002
6037
|
intros = await fetchIntros(deps, cookie);
|
|
6003
6038
|
} catch (err) {
|
|
6004
|
-
deps.errorLog(
|
|
6039
|
+
deps.errorLog(
|
|
6040
|
+
`
|
|
6005
6041
|
Could not look up the request: ${err instanceof Error ? err.message : String(err)}
|
|
6006
|
-
`
|
|
6042
|
+
`
|
|
6043
|
+
);
|
|
6007
6044
|
deps.exit(1);
|
|
6008
6045
|
return;
|
|
6009
6046
|
}
|
|
@@ -6018,8 +6055,10 @@ async function runIntroDecision(args, overrides) {
|
|
|
6018
6055
|
return;
|
|
6019
6056
|
}
|
|
6020
6057
|
if (matches.length > 1) {
|
|
6021
|
-
deps.errorLog(
|
|
6022
|
-
|
|
6058
|
+
deps.errorLog(
|
|
6059
|
+
`
|
|
6060
|
+
${matches.length} pending requests from @${handle} \u2014 ${args.action === "accept" ? "accepting" : "declining"} one (the rest are redundant duplicates).`
|
|
6061
|
+
);
|
|
6023
6062
|
}
|
|
6024
6063
|
id = matches[0].id;
|
|
6025
6064
|
}
|
|
@@ -6041,7 +6080,16 @@ async function runIntroDecision(args, overrides) {
|
|
|
6041
6080
|
}
|
|
6042
6081
|
deps.log(" Nothing else is shared. Declining shares nothing.");
|
|
6043
6082
|
deps.log("");
|
|
6044
|
-
|
|
6083
|
+
if (!deps.isTTY()) {
|
|
6084
|
+
deps.errorLog(
|
|
6085
|
+
"\n stdin is not interactive \u2014 re-run `terminalhire intro --accept` in a real terminal to confirm.\n"
|
|
6086
|
+
);
|
|
6087
|
+
deps.exit(1);
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
6090
|
+
const answer = await deps.prompt(
|
|
6091
|
+
' Type "yes" to accept and share your contact (anything else cancels): '
|
|
6092
|
+
);
|
|
6045
6093
|
if (answer !== "yes") {
|
|
6046
6094
|
deps.log("\n Cancelled \u2014 nothing was sent.\n");
|
|
6047
6095
|
deps.exit(0);
|
|
@@ -6138,7 +6186,9 @@ async function runIntroList(overrides) {
|
|
|
6138
6186
|
switch (result.status) {
|
|
6139
6187
|
case "no-session":
|
|
6140
6188
|
deps.log("\n No linked web session found on this machine.");
|
|
6141
|
-
deps.log(
|
|
6189
|
+
deps.log(
|
|
6190
|
+
" Run `terminalhire link` to connect this terminal to your account, then re-run.\n"
|
|
6191
|
+
);
|
|
6142
6192
|
deps.exit(0);
|
|
6143
6193
|
return;
|
|
6144
6194
|
case "expired":
|
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -25186,6 +25186,7 @@ __export(profile_exports, {
|
|
|
25186
25186
|
accumulateTags: () => accumulateTags,
|
|
25187
25187
|
addSavedJob: () => addSavedJob,
|
|
25188
25188
|
deleteProfile: () => deleteProfile,
|
|
25189
|
+
isBlankProfile: () => isBlankProfile,
|
|
25189
25190
|
listSavedJobs: () => listSavedJobs,
|
|
25190
25191
|
profileToFingerprint: () => profileToFingerprint,
|
|
25191
25192
|
readProfile: () => readProfile,
|
|
@@ -25226,6 +25227,17 @@ function migrateTagWeights(profile) {
|
|
|
25226
25227
|
}
|
|
25227
25228
|
}
|
|
25228
25229
|
}
|
|
25230
|
+
function isBlankProfile(profile) {
|
|
25231
|
+
if (profile.skillTags.length > 0) return false;
|
|
25232
|
+
if (Object.keys(profile.tagWeights).length > 0) return false;
|
|
25233
|
+
for (const [key, value] of Object.entries(profile)) {
|
|
25234
|
+
if (DERIVED_KEYS.has(key)) continue;
|
|
25235
|
+
if (value === void 0 || value === null) continue;
|
|
25236
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
25237
|
+
return false;
|
|
25238
|
+
}
|
|
25239
|
+
return true;
|
|
25240
|
+
}
|
|
25229
25241
|
async function readProfile() {
|
|
25230
25242
|
const parsed = await profileStore.read();
|
|
25231
25243
|
migrateTagWeights(parsed);
|
|
@@ -25310,7 +25322,7 @@ function profileToFingerprint(profile) {
|
|
|
25310
25322
|
}
|
|
25311
25323
|
};
|
|
25312
25324
|
}
|
|
25313
|
-
var TERMINALHIRE_DIR7, PROFILE_FILE, profileStore, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
25325
|
+
var TERMINALHIRE_DIR7, PROFILE_FILE, profileStore, DERIVED_KEYS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
25314
25326
|
var init_profile = __esm({
|
|
25315
25327
|
"src/profile.ts"() {
|
|
25316
25328
|
"use strict";
|
|
@@ -25322,6 +25334,13 @@ var init_profile = __esm({
|
|
|
25322
25334
|
blank: blankProfile,
|
|
25323
25335
|
keyPolicy: "keytar-first-file-fallback"
|
|
25324
25336
|
});
|
|
25337
|
+
DERIVED_KEYS = /* @__PURE__ */ new Set([
|
|
25338
|
+
"version",
|
|
25339
|
+
"updatedAt",
|
|
25340
|
+
"skillTags",
|
|
25341
|
+
"tagWeights",
|
|
25342
|
+
"hasEmployerSessions"
|
|
25343
|
+
]);
|
|
25325
25344
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
25326
25345
|
"typescript",
|
|
25327
25346
|
"javascript",
|
|
@@ -25890,7 +25909,8 @@ var init_approved_claims_badge = __esm({
|
|
|
25890
25909
|
var cache_store_exports = {};
|
|
25891
25910
|
__export(cache_store_exports, {
|
|
25892
25911
|
readCacheEntry: () => readCacheEntry,
|
|
25893
|
-
updateIndexCache: () => updateIndexCache
|
|
25912
|
+
updateIndexCache: () => updateIndexCache,
|
|
25913
|
+
writeIndexCache: () => writeIndexCache
|
|
25894
25914
|
});
|
|
25895
25915
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, renameSync as renameSync5 } from "fs";
|
|
25896
25916
|
import { join as join16 } from "path";
|
|
@@ -25916,6 +25936,9 @@ function updateIndexCache(patch) {
|
|
|
25916
25936
|
renameSync5(tmp, INDEX_CACHE_FILE);
|
|
25917
25937
|
return entry;
|
|
25918
25938
|
}
|
|
25939
|
+
function writeIndexCache(index) {
|
|
25940
|
+
return updateIndexCache({ index, indexETag: "" });
|
|
25941
|
+
}
|
|
25919
25942
|
var TERMINALHIRE_DIR10, INDEX_CACHE_FILE, SCHEMA_VERSION2, tmpCounter;
|
|
25920
25943
|
var init_cache_store = __esm({
|
|
25921
25944
|
"bin/cache-store.js"() {
|
|
@@ -28849,6 +28872,7 @@ function renderRunView(run2, message) {
|
|
|
28849
28872
|
}
|
|
28850
28873
|
if (run2.previewUrl) lines.push(` preview: ${terminalSafeInline(run2.previewUrl)}`);
|
|
28851
28874
|
if (run2.roundTrips != null) lines.push(` round trips: ${run2.roundTrips}`);
|
|
28875
|
+
if (run2.passMeaning) lines.push(` ${terminalSafeInline(run2.passMeaning)}`);
|
|
28852
28876
|
if (run2.logTail) {
|
|
28853
28877
|
lines.push(" \u2500\u2500 log tail \u2500\u2500");
|
|
28854
28878
|
for (const l of terminalSafeLines(run2.logTail)) lines.push(` \u2502 ${l}`);
|
|
@@ -28914,8 +28938,7 @@ async function watchRunsLoop({
|
|
|
28914
28938
|
for (let i = 1; i <= attempts; i++) {
|
|
28915
28939
|
const r = await fetchOnce();
|
|
28916
28940
|
if (r.kind === "refusal") {
|
|
28917
|
-
|
|
28918
|
-
return { outcome: "refused" };
|
|
28941
|
+
return { outcome: "refused", status: r.status, body: r.body };
|
|
28919
28942
|
}
|
|
28920
28943
|
if (r.kind === "network") {
|
|
28921
28944
|
log(` (attempt ${i}/${attempts}: terminalhire unreachable \u2014 ${r.error}; retrying)`);
|
|
@@ -29139,7 +29162,12 @@ async function cmdRuns(id, flags = {}) {
|
|
|
29139
29162
|
` Watching CI for ${id} \u2014 up to ${RUNS_POLL_ATTEMPTS} checks, one every ${Math.round(RUNS_POLL_INTERVAL_MS / 1e3)}s.`
|
|
29140
29163
|
);
|
|
29141
29164
|
const out = await watchRunsLoop({ id, fetchOnce });
|
|
29142
|
-
if (out.outcome === "refused")
|
|
29165
|
+
if (out.outcome === "refused") {
|
|
29166
|
+
if (!retireRevokedReadCredential(out.status, out.body, "reading CI results")) {
|
|
29167
|
+
console.error(`terminalhire claim: ${renderServerRefusal(out.status, out.body)}`);
|
|
29168
|
+
}
|
|
29169
|
+
process.exit(1);
|
|
29170
|
+
}
|
|
29143
29171
|
return;
|
|
29144
29172
|
}
|
|
29145
29173
|
const r = await fetchOnce();
|
package/dist/bin/jpi-config.js
CHANGED
|
@@ -114,7 +114,8 @@ var init_founder_paid_badge = __esm({
|
|
|
114
114
|
var cache_store_exports = {};
|
|
115
115
|
__export(cache_store_exports, {
|
|
116
116
|
readCacheEntry: () => readCacheEntry,
|
|
117
|
-
updateIndexCache: () => updateIndexCache
|
|
117
|
+
updateIndexCache: () => updateIndexCache,
|
|
118
|
+
writeIndexCache: () => writeIndexCache
|
|
118
119
|
});
|
|
119
120
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync } from "fs";
|
|
120
121
|
import { join as join2 } from "path";
|
|
@@ -140,6 +141,9 @@ function updateIndexCache(patch) {
|
|
|
140
141
|
renameSync(tmp, INDEX_CACHE_FILE);
|
|
141
142
|
return entry;
|
|
142
143
|
}
|
|
144
|
+
function writeIndexCache(index) {
|
|
145
|
+
return updateIndexCache({ index, indexETag: "" });
|
|
146
|
+
}
|
|
143
147
|
var TERMINALHIRE_DIR2, INDEX_CACHE_FILE, SCHEMA_VERSION, tmpCounter;
|
|
144
148
|
var init_cache_store = __esm({
|
|
145
149
|
"bin/cache-store.js"() {
|
|
@@ -2854,6 +2854,7 @@ __export(profile_exports, {
|
|
|
2854
2854
|
accumulateTags: () => accumulateTags,
|
|
2855
2855
|
addSavedJob: () => addSavedJob,
|
|
2856
2856
|
deleteProfile: () => deleteProfile,
|
|
2857
|
+
isBlankProfile: () => isBlankProfile,
|
|
2857
2858
|
listSavedJobs: () => listSavedJobs,
|
|
2858
2859
|
profileToFingerprint: () => profileToFingerprint,
|
|
2859
2860
|
readProfile: () => readProfile,
|
|
@@ -2894,6 +2895,17 @@ function migrateTagWeights(profile) {
|
|
|
2894
2895
|
}
|
|
2895
2896
|
}
|
|
2896
2897
|
}
|
|
2898
|
+
function isBlankProfile(profile) {
|
|
2899
|
+
if (profile.skillTags.length > 0) return false;
|
|
2900
|
+
if (Object.keys(profile.tagWeights).length > 0) return false;
|
|
2901
|
+
for (const [key, value] of Object.entries(profile)) {
|
|
2902
|
+
if (DERIVED_KEYS.has(key)) continue;
|
|
2903
|
+
if (value === void 0 || value === null) continue;
|
|
2904
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
2905
|
+
return false;
|
|
2906
|
+
}
|
|
2907
|
+
return true;
|
|
2908
|
+
}
|
|
2897
2909
|
async function readProfile() {
|
|
2898
2910
|
const parsed = await profileStore.read();
|
|
2899
2911
|
migrateTagWeights(parsed);
|
|
@@ -2978,7 +2990,7 @@ function profileToFingerprint(profile) {
|
|
|
2978
2990
|
}
|
|
2979
2991
|
};
|
|
2980
2992
|
}
|
|
2981
|
-
var TERMINALHIRE_DIR4, PROFILE_FILE, profileStore, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
2993
|
+
var TERMINALHIRE_DIR4, PROFILE_FILE, profileStore, DERIVED_KEYS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
2982
2994
|
var init_profile = __esm({
|
|
2983
2995
|
"src/profile.ts"() {
|
|
2984
2996
|
"use strict";
|
|
@@ -2990,6 +3002,13 @@ var init_profile = __esm({
|
|
|
2990
3002
|
blank: blankProfile,
|
|
2991
3003
|
keyPolicy: "keytar-first-file-fallback"
|
|
2992
3004
|
});
|
|
3005
|
+
DERIVED_KEYS = /* @__PURE__ */ new Set([
|
|
3006
|
+
"version",
|
|
3007
|
+
"updatedAt",
|
|
3008
|
+
"skillTags",
|
|
3009
|
+
"tagWeights",
|
|
3010
|
+
"hasEmployerSessions"
|
|
3011
|
+
]);
|
|
2993
3012
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
2994
3013
|
"typescript",
|
|
2995
3014
|
"javascript",
|
|
@@ -3854,6 +3873,9 @@ function updateIndexCache(patch) {
|
|
|
3854
3873
|
renameSync(tmp, INDEX_CACHE_FILE);
|
|
3855
3874
|
return entry;
|
|
3856
3875
|
}
|
|
3876
|
+
function writeIndexCache(index) {
|
|
3877
|
+
return updateIndexCache({ index, indexETag: "" });
|
|
3878
|
+
}
|
|
3857
3879
|
|
|
3858
3880
|
// src/config.ts
|
|
3859
3881
|
init_state_dir();
|
|
@@ -4157,9 +4179,6 @@ function readIndexCache() {
|
|
|
4157
4179
|
return null;
|
|
4158
4180
|
}
|
|
4159
4181
|
}
|
|
4160
|
-
function writeIndexCache(index) {
|
|
4161
|
-
updateIndexCache({ index, indexETag: "" });
|
|
4162
|
-
}
|
|
4163
4182
|
async function fetchIndex(fetchImpl, useCache = true) {
|
|
4164
4183
|
if (useCache) {
|
|
4165
4184
|
const cached = readIndexCache();
|
package/dist/bin/jpi-devs.js
CHANGED
|
@@ -10809,6 +10809,7 @@ __export(profile_exports, {
|
|
|
10809
10809
|
accumulateTags: () => accumulateTags,
|
|
10810
10810
|
addSavedJob: () => addSavedJob,
|
|
10811
10811
|
deleteProfile: () => deleteProfile,
|
|
10812
|
+
isBlankProfile: () => isBlankProfile,
|
|
10812
10813
|
listSavedJobs: () => listSavedJobs,
|
|
10813
10814
|
profileToFingerprint: () => profileToFingerprint,
|
|
10814
10815
|
readProfile: () => readProfile,
|
|
@@ -10849,6 +10850,17 @@ function migrateTagWeights(profile) {
|
|
|
10849
10850
|
}
|
|
10850
10851
|
}
|
|
10851
10852
|
}
|
|
10853
|
+
function isBlankProfile(profile) {
|
|
10854
|
+
if (profile.skillTags.length > 0) return false;
|
|
10855
|
+
if (Object.keys(profile.tagWeights).length > 0) return false;
|
|
10856
|
+
for (const [key, value] of Object.entries(profile)) {
|
|
10857
|
+
if (DERIVED_KEYS.has(key)) continue;
|
|
10858
|
+
if (value === void 0 || value === null) continue;
|
|
10859
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
10860
|
+
return false;
|
|
10861
|
+
}
|
|
10862
|
+
return true;
|
|
10863
|
+
}
|
|
10852
10864
|
async function readProfile() {
|
|
10853
10865
|
const parsed = await profileStore.read();
|
|
10854
10866
|
migrateTagWeights(parsed);
|
|
@@ -10933,7 +10945,7 @@ function profileToFingerprint(profile) {
|
|
|
10933
10945
|
}
|
|
10934
10946
|
};
|
|
10935
10947
|
}
|
|
10936
|
-
var TERMINALHIRE_DIR3, PROFILE_FILE, profileStore, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
10948
|
+
var TERMINALHIRE_DIR3, PROFILE_FILE, profileStore, DERIVED_KEYS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
10937
10949
|
var init_profile = __esm({
|
|
10938
10950
|
"src/profile.ts"() {
|
|
10939
10951
|
"use strict";
|
|
@@ -10945,6 +10957,13 @@ var init_profile = __esm({
|
|
|
10945
10957
|
blank: blankProfile,
|
|
10946
10958
|
keyPolicy: "keytar-first-file-fallback"
|
|
10947
10959
|
});
|
|
10960
|
+
DERIVED_KEYS = /* @__PURE__ */ new Set([
|
|
10961
|
+
"version",
|
|
10962
|
+
"updatedAt",
|
|
10963
|
+
"skillTags",
|
|
10964
|
+
"tagWeights",
|
|
10965
|
+
"hasEmployerSessions"
|
|
10966
|
+
]);
|
|
10948
10967
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
10949
10968
|
"typescript",
|
|
10950
10969
|
"javascript",
|