terminalhire 0.19.2 → 0.20.0
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/dist/bin/claim-push-bg.js +400 -0
- package/dist/bin/jpi-bounties.js +2 -2
- package/dist/bin/jpi-chat-read.js +2 -2
- package/dist/bin/jpi-chat.js +2 -2
- package/dist/bin/jpi-claim.js +273 -139
- package/dist/bin/jpi-contribute.js +2 -2
- package/dist/bin/jpi-devs.js +2 -2
- package/dist/bin/jpi-dispatch.js +408 -213
- package/dist/bin/jpi-inbox.js +2 -2
- package/dist/bin/jpi-intro.js +2 -2
- package/dist/bin/jpi-jobs.js +2 -2
- package/dist/bin/jpi-learn.js +2 -2
- package/dist/bin/jpi-login.js +2 -2
- package/dist/bin/jpi-profile.js +2 -2
- package/dist/bin/jpi-refresh.js +327 -7
- package/dist/bin/jpi-save.js +2 -2
- package/dist/bin/jpi-sync.js +2 -2
- package/dist/bin/jpi-trajectory.js +2 -2
- package/dist/src/chat-client.js +2 -2
- package/dist/src/chat-keystore.js +2 -2
- package/dist/src/github-auth.js +2 -2
- package/dist/src/intro.js +2 -2
- package/dist/src/profile.js +2 -2
- package/dist/src/trajectory.js +2 -2
- package/package.json +1 -1
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -950,6 +950,65 @@ var init_src = __esm({
|
|
|
950
950
|
}
|
|
951
951
|
});
|
|
952
952
|
|
|
953
|
+
// ../../node_modules/keytar/build/Release/keytar.node
|
|
954
|
+
var keytar_default;
|
|
955
|
+
var init_keytar = __esm({
|
|
956
|
+
"../../node_modules/keytar/build/Release/keytar.node"() {
|
|
957
|
+
keytar_default = "../keytar-KOAAH267.node";
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
962
|
+
var require_keytar = __commonJS({
|
|
963
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
964
|
+
"use strict";
|
|
965
|
+
init_keytar();
|
|
966
|
+
try {
|
|
967
|
+
module.exports = __require(keytar_default);
|
|
968
|
+
} catch {
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
// ../../node_modules/keytar/lib/keytar.js
|
|
974
|
+
var require_keytar2 = __commonJS({
|
|
975
|
+
"../../node_modules/keytar/lib/keytar.js"(exports, module) {
|
|
976
|
+
"use strict";
|
|
977
|
+
var keytar = require_keytar();
|
|
978
|
+
function checkRequired(val, name) {
|
|
979
|
+
if (!val || val.length <= 0) {
|
|
980
|
+
throw new Error(name + " is required.");
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
module.exports = {
|
|
984
|
+
getPassword: function(service, account) {
|
|
985
|
+
checkRequired(service, "Service");
|
|
986
|
+
checkRequired(account, "Account");
|
|
987
|
+
return keytar.getPassword(service, account);
|
|
988
|
+
},
|
|
989
|
+
setPassword: function(service, account, password) {
|
|
990
|
+
checkRequired(service, "Service");
|
|
991
|
+
checkRequired(account, "Account");
|
|
992
|
+
checkRequired(password, "Password");
|
|
993
|
+
return keytar.setPassword(service, account, password);
|
|
994
|
+
},
|
|
995
|
+
deletePassword: function(service, account) {
|
|
996
|
+
checkRequired(service, "Service");
|
|
997
|
+
checkRequired(account, "Account");
|
|
998
|
+
return keytar.deletePassword(service, account);
|
|
999
|
+
},
|
|
1000
|
+
findPassword: function(service) {
|
|
1001
|
+
checkRequired(service, "Service");
|
|
1002
|
+
return keytar.findPassword(service);
|
|
1003
|
+
},
|
|
1004
|
+
findCredentials: function(service) {
|
|
1005
|
+
checkRequired(service, "Service");
|
|
1006
|
+
return keytar.findCredentials(service);
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
|
|
953
1012
|
// src/claims.ts
|
|
954
1013
|
var claims_exports = {};
|
|
955
1014
|
__export(claims_exports, {
|
|
@@ -962,9 +1021,9 @@ __export(claims_exports, {
|
|
|
962
1021
|
toPushedClaim: () => toPushedClaim,
|
|
963
1022
|
updateClaim: () => updateClaim
|
|
964
1023
|
});
|
|
965
|
-
import { readFileSync as
|
|
966
|
-
import { join as
|
|
967
|
-
import { homedir } from "os";
|
|
1024
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, renameSync, existsSync as existsSync3 } from "fs";
|
|
1025
|
+
import { join as join4 } from "path";
|
|
1026
|
+
import { homedir as homedir3 } from "os";
|
|
968
1027
|
function toPushedClaim(claim) {
|
|
969
1028
|
return {
|
|
970
1029
|
kind: claim.kind,
|
|
@@ -984,8 +1043,8 @@ function normalizeClaim(c) {
|
|
|
984
1043
|
}
|
|
985
1044
|
function readClaims() {
|
|
986
1045
|
try {
|
|
987
|
-
if (!
|
|
988
|
-
const data = JSON.parse(
|
|
1046
|
+
if (!existsSync3(CLAIMS_FILE)) return [];
|
|
1047
|
+
const data = JSON.parse(readFileSync4(CLAIMS_FILE, "utf8"));
|
|
989
1048
|
const claims = Array.isArray(data?.claims) ? data.claims : [];
|
|
990
1049
|
return claims.map(normalizeClaim);
|
|
991
1050
|
} catch {
|
|
@@ -993,10 +1052,10 @@ function readClaims() {
|
|
|
993
1052
|
}
|
|
994
1053
|
}
|
|
995
1054
|
function writeClaims(claims) {
|
|
996
|
-
|
|
1055
|
+
mkdirSync3(TERMINALHIRE_DIR3, { recursive: true });
|
|
997
1056
|
const tmp = `${CLAIMS_FILE}.tmp`;
|
|
998
1057
|
const payload = { claims };
|
|
999
|
-
|
|
1058
|
+
writeFileSync3(tmp, JSON.stringify(payload, null, 2), "utf8");
|
|
1000
1059
|
renameSync(tmp, CLAIMS_FILE);
|
|
1001
1060
|
}
|
|
1002
1061
|
function findClaim(id) {
|
|
@@ -1053,12 +1112,12 @@ function acceptedPRRate(claims = readClaims()) {
|
|
|
1053
1112
|
const merged = claims.filter((c) => c.state === "merged").length;
|
|
1054
1113
|
return { merged, total, rate: total === 0 ? 0 : merged / total };
|
|
1055
1114
|
}
|
|
1056
|
-
var
|
|
1115
|
+
var TERMINALHIRE_DIR3, CLAIMS_FILE, TERMINAL_STATES;
|
|
1057
1116
|
var init_claims = __esm({
|
|
1058
1117
|
"src/claims.ts"() {
|
|
1059
1118
|
"use strict";
|
|
1060
|
-
|
|
1061
|
-
CLAIMS_FILE =
|
|
1119
|
+
TERMINALHIRE_DIR3 = join4(homedir3(), ".terminalhire");
|
|
1120
|
+
CLAIMS_FILE = join4(TERMINALHIRE_DIR3, "claims.json");
|
|
1062
1121
|
TERMINAL_STATES = /* @__PURE__ */ new Set(["merged", "abandoned"]);
|
|
1063
1122
|
}
|
|
1064
1123
|
});
|
|
@@ -1157,25 +1216,25 @@ __export(job_status_store_exports, {
|
|
|
1157
1216
|
statusFilePath: () => statusFilePath
|
|
1158
1217
|
});
|
|
1159
1218
|
import {
|
|
1160
|
-
readFileSync as
|
|
1161
|
-
writeFileSync as
|
|
1219
|
+
readFileSync as readFileSync5,
|
|
1220
|
+
writeFileSync as writeFileSync4,
|
|
1162
1221
|
renameSync as renameSync2,
|
|
1163
|
-
mkdirSync as
|
|
1164
|
-
existsSync as
|
|
1222
|
+
mkdirSync as mkdirSync4,
|
|
1223
|
+
existsSync as existsSync4,
|
|
1165
1224
|
copyFileSync,
|
|
1166
1225
|
openSync,
|
|
1167
1226
|
closeSync,
|
|
1168
1227
|
unlinkSync
|
|
1169
1228
|
} from "fs";
|
|
1170
|
-
import { join as
|
|
1171
|
-
import { homedir as
|
|
1229
|
+
import { join as join5, dirname } from "path";
|
|
1230
|
+
import { homedir as homedir4 } from "os";
|
|
1172
1231
|
function statusFilePath() {
|
|
1173
1232
|
return STATUS_FILE;
|
|
1174
1233
|
}
|
|
1175
1234
|
function atomicWriteJson(path, obj) {
|
|
1176
|
-
|
|
1235
|
+
mkdirSync4(dirname(path), { recursive: true });
|
|
1177
1236
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
1178
|
-
|
|
1237
|
+
writeFileSync4(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
1179
1238
|
renameSync2(tmp, path);
|
|
1180
1239
|
}
|
|
1181
1240
|
function sleepMs(ms) {
|
|
@@ -1192,7 +1251,7 @@ function withLock(fn) {
|
|
|
1192
1251
|
for (; ; ) {
|
|
1193
1252
|
let fd;
|
|
1194
1253
|
try {
|
|
1195
|
-
|
|
1254
|
+
mkdirSync4(dirname(LOCK_FILE), { recursive: true });
|
|
1196
1255
|
fd = openSync(LOCK_FILE, "wx");
|
|
1197
1256
|
} catch (err) {
|
|
1198
1257
|
if (err && err.code === "EEXIST") {
|
|
@@ -1223,10 +1282,10 @@ function withLock(fn) {
|
|
|
1223
1282
|
}
|
|
1224
1283
|
}
|
|
1225
1284
|
function readStatusMap() {
|
|
1226
|
-
if (!
|
|
1285
|
+
if (!existsSync4(STATUS_FILE)) return {};
|
|
1227
1286
|
let raw;
|
|
1228
1287
|
try {
|
|
1229
|
-
raw =
|
|
1288
|
+
raw = readFileSync5(STATUS_FILE, "utf8");
|
|
1230
1289
|
} catch {
|
|
1231
1290
|
return {};
|
|
1232
1291
|
}
|
|
@@ -1258,77 +1317,18 @@ function markClicked(id) {
|
|
|
1258
1317
|
return next[id];
|
|
1259
1318
|
});
|
|
1260
1319
|
}
|
|
1261
|
-
var
|
|
1320
|
+
var TERMINALHIRE_DIR4, STATUS_FILE, LOCK_FILE, BAK_FILE;
|
|
1262
1321
|
var init_job_status_store = __esm({
|
|
1263
1322
|
"bin/job-status-store.js"() {
|
|
1264
1323
|
"use strict";
|
|
1265
1324
|
init_src();
|
|
1266
|
-
|
|
1267
|
-
STATUS_FILE =
|
|
1325
|
+
TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
|
|
1326
|
+
STATUS_FILE = join5(TERMINALHIRE_DIR4, "job-status.json");
|
|
1268
1327
|
LOCK_FILE = `${STATUS_FILE}.lock`;
|
|
1269
1328
|
BAK_FILE = `${STATUS_FILE}.bak`;
|
|
1270
1329
|
}
|
|
1271
1330
|
});
|
|
1272
1331
|
|
|
1273
|
-
// ../../node_modules/keytar/build/Release/keytar.node
|
|
1274
|
-
var keytar_default;
|
|
1275
|
-
var init_keytar = __esm({
|
|
1276
|
-
"../../node_modules/keytar/build/Release/keytar.node"() {
|
|
1277
|
-
keytar_default = "../keytar-KOAAH267.node";
|
|
1278
|
-
}
|
|
1279
|
-
});
|
|
1280
|
-
|
|
1281
|
-
// node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node
|
|
1282
|
-
var require_keytar = __commonJS({
|
|
1283
|
-
"node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
1284
|
-
"use strict";
|
|
1285
|
-
init_keytar();
|
|
1286
|
-
try {
|
|
1287
|
-
module.exports = __require(keytar_default);
|
|
1288
|
-
} catch {
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
});
|
|
1292
|
-
|
|
1293
|
-
// ../../node_modules/keytar/lib/keytar.js
|
|
1294
|
-
var require_keytar2 = __commonJS({
|
|
1295
|
-
"../../node_modules/keytar/lib/keytar.js"(exports, module) {
|
|
1296
|
-
"use strict";
|
|
1297
|
-
var keytar = require_keytar();
|
|
1298
|
-
function checkRequired(val, name) {
|
|
1299
|
-
if (!val || val.length <= 0) {
|
|
1300
|
-
throw new Error(name + " is required.");
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
module.exports = {
|
|
1304
|
-
getPassword: function(service, account) {
|
|
1305
|
-
checkRequired(service, "Service");
|
|
1306
|
-
checkRequired(account, "Account");
|
|
1307
|
-
return keytar.getPassword(service, account);
|
|
1308
|
-
},
|
|
1309
|
-
setPassword: function(service, account, password) {
|
|
1310
|
-
checkRequired(service, "Service");
|
|
1311
|
-
checkRequired(account, "Account");
|
|
1312
|
-
checkRequired(password, "Password");
|
|
1313
|
-
return keytar.setPassword(service, account, password);
|
|
1314
|
-
},
|
|
1315
|
-
deletePassword: function(service, account) {
|
|
1316
|
-
checkRequired(service, "Service");
|
|
1317
|
-
checkRequired(account, "Account");
|
|
1318
|
-
return keytar.deletePassword(service, account);
|
|
1319
|
-
},
|
|
1320
|
-
findPassword: function(service) {
|
|
1321
|
-
checkRequired(service, "Service");
|
|
1322
|
-
return keytar.findPassword(service);
|
|
1323
|
-
},
|
|
1324
|
-
findCredentials: function(service) {
|
|
1325
|
-
checkRequired(service, "Service");
|
|
1326
|
-
return keytar.findCredentials(service);
|
|
1327
|
-
}
|
|
1328
|
-
};
|
|
1329
|
-
}
|
|
1330
|
-
});
|
|
1331
|
-
|
|
1332
1332
|
// src/profile.ts
|
|
1333
1333
|
var profile_exports = {};
|
|
1334
1334
|
__export(profile_exports, {
|
|
@@ -1344,41 +1344,41 @@ __export(profile_exports, {
|
|
|
1344
1344
|
writeProfile: () => writeProfile
|
|
1345
1345
|
});
|
|
1346
1346
|
import {
|
|
1347
|
-
createCipheriv,
|
|
1348
|
-
createDecipheriv,
|
|
1349
|
-
randomBytes as
|
|
1347
|
+
createCipheriv as createCipheriv2,
|
|
1348
|
+
createDecipheriv as createDecipheriv2,
|
|
1349
|
+
randomBytes as randomBytes3
|
|
1350
1350
|
} from "crypto";
|
|
1351
1351
|
import {
|
|
1352
|
-
readFileSync as
|
|
1353
|
-
writeFileSync as
|
|
1354
|
-
mkdirSync as
|
|
1355
|
-
existsSync as
|
|
1352
|
+
readFileSync as readFileSync6,
|
|
1353
|
+
writeFileSync as writeFileSync5,
|
|
1354
|
+
mkdirSync as mkdirSync5,
|
|
1355
|
+
existsSync as existsSync5
|
|
1356
1356
|
} from "fs";
|
|
1357
|
-
import { join as
|
|
1358
|
-
import { homedir as
|
|
1359
|
-
async function
|
|
1357
|
+
import { join as join6 } from "path";
|
|
1358
|
+
import { homedir as homedir5 } from "os";
|
|
1359
|
+
async function loadKey2() {
|
|
1360
1360
|
try {
|
|
1361
1361
|
const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
|
|
1362
1362
|
const stored = await kt.getPassword("terminalhire", "profile-key");
|
|
1363
1363
|
if (stored) {
|
|
1364
1364
|
return Buffer.from(stored, "hex");
|
|
1365
1365
|
}
|
|
1366
|
-
const key2 =
|
|
1366
|
+
const key2 = randomBytes3(KEY_BYTES2);
|
|
1367
1367
|
await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
|
|
1368
1368
|
return key2;
|
|
1369
1369
|
} catch {
|
|
1370
1370
|
}
|
|
1371
|
-
|
|
1372
|
-
if (
|
|
1373
|
-
return Buffer.from(
|
|
1371
|
+
mkdirSync5(TERMINALHIRE_DIR5, { recursive: true });
|
|
1372
|
+
if (existsSync5(KEY_FILE2)) {
|
|
1373
|
+
return Buffer.from(readFileSync6(KEY_FILE2, "utf8").trim(), "hex");
|
|
1374
1374
|
}
|
|
1375
|
-
const key =
|
|
1376
|
-
|
|
1375
|
+
const key = randomBytes3(KEY_BYTES2);
|
|
1376
|
+
writeFileSync5(KEY_FILE2, key.toString("hex"), { mode: 384, encoding: "utf8" });
|
|
1377
1377
|
return key;
|
|
1378
1378
|
}
|
|
1379
|
-
function
|
|
1380
|
-
const iv =
|
|
1381
|
-
const cipher =
|
|
1379
|
+
function encrypt2(plaintext, key) {
|
|
1380
|
+
const iv = randomBytes3(IV_BYTES2);
|
|
1381
|
+
const cipher = createCipheriv2(ALGO2, key, iv);
|
|
1382
1382
|
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
1383
1383
|
const tag = cipher.getAuthTag();
|
|
1384
1384
|
return {
|
|
@@ -1387,9 +1387,9 @@ function encrypt(plaintext, key) {
|
|
|
1387
1387
|
ciphertext: ct.toString("hex")
|
|
1388
1388
|
};
|
|
1389
1389
|
}
|
|
1390
|
-
function
|
|
1391
|
-
const decipher =
|
|
1392
|
-
|
|
1390
|
+
function decrypt2(blob, key) {
|
|
1391
|
+
const decipher = createDecipheriv2(
|
|
1392
|
+
ALGO2,
|
|
1393
1393
|
key,
|
|
1394
1394
|
Buffer.from(blob.iv, "hex")
|
|
1395
1395
|
);
|
|
@@ -1431,12 +1431,12 @@ function migrateTagWeights(profile) {
|
|
|
1431
1431
|
}
|
|
1432
1432
|
}
|
|
1433
1433
|
async function readProfile() {
|
|
1434
|
-
if (!
|
|
1434
|
+
if (!existsSync5(PROFILE_FILE)) return blankProfile();
|
|
1435
1435
|
try {
|
|
1436
|
-
const key = await
|
|
1437
|
-
const raw =
|
|
1436
|
+
const key = await loadKey2();
|
|
1437
|
+
const raw = readFileSync6(PROFILE_FILE, "utf8");
|
|
1438
1438
|
const blob = JSON.parse(raw);
|
|
1439
|
-
const plaintext =
|
|
1439
|
+
const plaintext = decrypt2(blob, key);
|
|
1440
1440
|
const parsed = JSON.parse(plaintext);
|
|
1441
1441
|
migrateTagWeights(parsed);
|
|
1442
1442
|
return parsed;
|
|
@@ -1445,12 +1445,12 @@ async function readProfile() {
|
|
|
1445
1445
|
}
|
|
1446
1446
|
}
|
|
1447
1447
|
async function writeProfile(profile) {
|
|
1448
|
-
|
|
1449
|
-
const key = await
|
|
1448
|
+
mkdirSync5(TERMINALHIRE_DIR5, { recursive: true });
|
|
1449
|
+
const key = await loadKey2();
|
|
1450
1450
|
profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1451
1451
|
profile.skillTags = deriveSkillTags(profile.tagWeights);
|
|
1452
|
-
const blob =
|
|
1453
|
-
|
|
1452
|
+
const blob = encrypt2(JSON.stringify(profile), key);
|
|
1453
|
+
writeFileSync5(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
|
|
1454
1454
|
}
|
|
1455
1455
|
function accumulateSession(profile, tags, isEmployerContext, inferredSeniority, seniorityIsAuthoritative = false) {
|
|
1456
1456
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1511,13 +1511,13 @@ async function removeSavedJob(id) {
|
|
|
1511
1511
|
return true;
|
|
1512
1512
|
}
|
|
1513
1513
|
async function deleteProfile() {
|
|
1514
|
-
const { rmSync:
|
|
1514
|
+
const { rmSync: rmSync4 } = await import("fs");
|
|
1515
1515
|
try {
|
|
1516
|
-
|
|
1516
|
+
rmSync4(PROFILE_FILE);
|
|
1517
1517
|
} catch {
|
|
1518
1518
|
}
|
|
1519
1519
|
try {
|
|
1520
|
-
|
|
1520
|
+
rmSync4(KEY_FILE2);
|
|
1521
1521
|
} catch {
|
|
1522
1522
|
}
|
|
1523
1523
|
}
|
|
@@ -1534,17 +1534,17 @@ function profileToFingerprint(profile) {
|
|
|
1534
1534
|
}
|
|
1535
1535
|
};
|
|
1536
1536
|
}
|
|
1537
|
-
var
|
|
1537
|
+
var TERMINALHIRE_DIR5, PROFILE_FILE, KEY_FILE2, ALGO2, KEY_BYTES2, IV_BYTES2, DECAY_HALF_LIFE_MS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
|
|
1538
1538
|
var init_profile = __esm({
|
|
1539
1539
|
"src/profile.ts"() {
|
|
1540
1540
|
"use strict";
|
|
1541
1541
|
init_src();
|
|
1542
|
-
|
|
1543
|
-
PROFILE_FILE =
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1542
|
+
TERMINALHIRE_DIR5 = join6(homedir5(), ".terminalhire");
|
|
1543
|
+
PROFILE_FILE = join6(TERMINALHIRE_DIR5, "profile.enc");
|
|
1544
|
+
KEY_FILE2 = join6(TERMINALHIRE_DIR5, "key");
|
|
1545
|
+
ALGO2 = "aes-256-gcm";
|
|
1546
|
+
KEY_BYTES2 = 32;
|
|
1547
|
+
IV_BYTES2 = 12;
|
|
1548
1548
|
DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1549
1549
|
LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
1550
1550
|
"typescript",
|
|
@@ -1571,9 +1571,9 @@ var init_profile = __esm({
|
|
|
1571
1571
|
|
|
1572
1572
|
// bin/jpi-claim.js
|
|
1573
1573
|
init_src();
|
|
1574
|
-
import { readFileSync as
|
|
1575
|
-
import { join as
|
|
1576
|
-
import { homedir as
|
|
1574
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync6, rmSync as rmSync3 } from "fs";
|
|
1575
|
+
import { join as join7 } from "path";
|
|
1576
|
+
import { homedir as homedir6, hostname as osHostname } from "os";
|
|
1577
1577
|
import { execFile } from "child_process";
|
|
1578
1578
|
import { promisify } from "util";
|
|
1579
1579
|
import { createInterface } from "readline";
|
|
@@ -1602,10 +1602,95 @@ function openInBrowser(url) {
|
|
|
1602
1602
|
}
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
|
+
// bin/claim-push-bg.js
|
|
1606
|
+
import { createHash as createHash3 } from "crypto";
|
|
1607
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, rmSync as rmSync2 } from "fs";
|
|
1608
|
+
import { join as join3 } from "path";
|
|
1609
|
+
import { homedir as homedir2 } from "os";
|
|
1610
|
+
|
|
1611
|
+
// src/github-auth.ts
|
|
1612
|
+
import {
|
|
1613
|
+
createCipheriv,
|
|
1614
|
+
createDecipheriv,
|
|
1615
|
+
randomBytes as randomBytes2
|
|
1616
|
+
} from "crypto";
|
|
1617
|
+
import {
|
|
1618
|
+
readFileSync as readFileSync2,
|
|
1619
|
+
writeFileSync,
|
|
1620
|
+
mkdirSync,
|
|
1621
|
+
existsSync,
|
|
1622
|
+
rmSync
|
|
1623
|
+
} from "fs";
|
|
1624
|
+
import { join as join2 } from "path";
|
|
1625
|
+
import { homedir } from "os";
|
|
1626
|
+
var TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
|
|
1627
|
+
var TOKEN_FILE = join2(TERMINALHIRE_DIR, "github-token.enc");
|
|
1628
|
+
var KEY_FILE = join2(TERMINALHIRE_DIR, "key");
|
|
1629
|
+
var ALGO = "aes-256-gcm";
|
|
1630
|
+
var KEY_BYTES = 32;
|
|
1631
|
+
var IV_BYTES = 12;
|
|
1632
|
+
async function loadKey() {
|
|
1633
|
+
try {
|
|
1634
|
+
const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
|
|
1635
|
+
const stored = await kt.getPassword("terminalhire", "profile-key");
|
|
1636
|
+
if (stored) return Buffer.from(stored, "hex");
|
|
1637
|
+
const key2 = randomBytes2(KEY_BYTES);
|
|
1638
|
+
await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
|
|
1639
|
+
return key2;
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
mkdirSync(TERMINALHIRE_DIR, { recursive: true });
|
|
1643
|
+
if (existsSync(KEY_FILE)) {
|
|
1644
|
+
return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
|
|
1645
|
+
}
|
|
1646
|
+
const key = randomBytes2(KEY_BYTES);
|
|
1647
|
+
writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
|
|
1648
|
+
return key;
|
|
1649
|
+
}
|
|
1650
|
+
function encrypt(plaintext, key) {
|
|
1651
|
+
const iv = randomBytes2(IV_BYTES);
|
|
1652
|
+
const cipher = createCipheriv(ALGO, key, iv);
|
|
1653
|
+
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
1654
|
+
const tag = cipher.getAuthTag();
|
|
1655
|
+
return { iv: iv.toString("hex"), tag: tag.toString("hex"), ciphertext: ct.toString("hex") };
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// bin/claim-push-bg.js
|
|
1659
|
+
var TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join3(homedir2(), ".terminalhire");
|
|
1660
|
+
var CLAIM_PUSH_AUTO_MARKER = join3(TERMINALHIRE_DIR2, "claim-push-auto.json");
|
|
1661
|
+
var CLAIM_PUSH_TOKEN_FILE = join3(TERMINALHIRE_DIR2, "claim-push-token.enc");
|
|
1662
|
+
var AUTO_CONSENT_VERSION = 2;
|
|
1663
|
+
var AUTO_PUSH_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
1664
|
+
async function writePushTokenEnc(rawToken) {
|
|
1665
|
+
mkdirSync2(TERMINALHIRE_DIR2, { recursive: true });
|
|
1666
|
+
const key = await loadKey();
|
|
1667
|
+
const blob = encrypt(rawToken, key);
|
|
1668
|
+
writeFileSync2(CLAIM_PUSH_TOKEN_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
|
|
1669
|
+
}
|
|
1670
|
+
function clearPushTokenEnc() {
|
|
1671
|
+
try {
|
|
1672
|
+
rmSync2(CLAIM_PUSH_TOKEN_FILE);
|
|
1673
|
+
} catch {
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
function writeAutoMarker(marker) {
|
|
1677
|
+
mkdirSync2(TERMINALHIRE_DIR2, { recursive: true });
|
|
1678
|
+
writeFileSync2(CLAIM_PUSH_AUTO_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
|
|
1679
|
+
}
|
|
1680
|
+
function clearAutoMarker() {
|
|
1681
|
+
try {
|
|
1682
|
+
rmSync2(CLAIM_PUSH_AUTO_MARKER);
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
function computeSnapshotHash(pushed) {
|
|
1687
|
+
return createHash3("sha256").update(JSON.stringify(pushed)).digest("hex");
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1605
1690
|
// bin/jpi-claim.js
|
|
1606
|
-
var
|
|
1607
|
-
var INDEX_CACHE_FILE =
|
|
1608
|
-
var CLAIM_PUSH_MARKER =
|
|
1691
|
+
var TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join7(homedir6(), ".terminalhire");
|
|
1692
|
+
var INDEX_CACHE_FILE = join7(TERMINALHIRE_DIR6, "index-cache.json");
|
|
1693
|
+
var CLAIM_PUSH_MARKER = join7(TERMINALHIRE_DIR6, "claim-push.json");
|
|
1609
1694
|
var API_URL = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
|
|
1610
1695
|
var CLAIM_SYNC_BASE = "https://terminalhire.com";
|
|
1611
1696
|
var CLAIM_CONSENT_VERSION = 1;
|
|
@@ -1677,8 +1762,8 @@ function parseRepoFromRemote(url) {
|
|
|
1677
1762
|
return m ? `${m[1]}/${m[2]}` : null;
|
|
1678
1763
|
}
|
|
1679
1764
|
function readClaimablePool() {
|
|
1680
|
-
if (!
|
|
1681
|
-
const entry = JSON.parse(
|
|
1765
|
+
if (!existsSync6(INDEX_CACHE_FILE)) return [];
|
|
1766
|
+
const entry = JSON.parse(readFileSync7(INDEX_CACHE_FILE, "utf8"));
|
|
1682
1767
|
const bounties = (entry?.index?.jobs ?? []).filter((j) => j.source === "bounty");
|
|
1683
1768
|
const contributions = (entry?.index?.contribute ?? []).filter((j) => j.source === "contribute");
|
|
1684
1769
|
return [...bounties, ...contributions];
|
|
@@ -2340,18 +2425,18 @@ function claimSleep(ms) {
|
|
|
2340
2425
|
}
|
|
2341
2426
|
function readClaimPushMarker() {
|
|
2342
2427
|
try {
|
|
2343
|
-
return
|
|
2428
|
+
return existsSync6(CLAIM_PUSH_MARKER) ? JSON.parse(readFileSync7(CLAIM_PUSH_MARKER, "utf8")) : null;
|
|
2344
2429
|
} catch {
|
|
2345
2430
|
return null;
|
|
2346
2431
|
}
|
|
2347
2432
|
}
|
|
2348
2433
|
function writeClaimPushMarker(marker) {
|
|
2349
|
-
|
|
2350
|
-
|
|
2434
|
+
mkdirSync6(TERMINALHIRE_DIR6, { recursive: true });
|
|
2435
|
+
writeFileSync6(CLAIM_PUSH_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
|
|
2351
2436
|
}
|
|
2352
2437
|
function clearClaimPushMarker() {
|
|
2353
2438
|
try {
|
|
2354
|
-
|
|
2439
|
+
rmSync3(CLAIM_PUSH_MARKER);
|
|
2355
2440
|
} catch {
|
|
2356
2441
|
}
|
|
2357
2442
|
}
|
|
@@ -2371,7 +2456,20 @@ function renderClaimConsent(pushed, login) {
|
|
|
2371
2456
|
console.log(" This is NOT required to use terminalhire.");
|
|
2372
2457
|
console.log("");
|
|
2373
2458
|
}
|
|
2374
|
-
|
|
2459
|
+
function renderAutoConsent() {
|
|
2460
|
+
console.log("");
|
|
2461
|
+
console.log(" keep your dashboard updated in the background (opt-in)");
|
|
2462
|
+
console.log("");
|
|
2463
|
+
console.log(" Terminal Hire will keep your dashboard updated in the background \u2014");
|
|
2464
|
+
console.log(" pushing the SAME score-free fields, at most once/day \u2014 until you run");
|
|
2465
|
+
console.log(" `terminalhire claim --push --revoke`.");
|
|
2466
|
+
console.log("");
|
|
2467
|
+
console.log(" This stores a push-only credential on this machine (encrypted). It can");
|
|
2468
|
+
console.log(" ONLY add/update your OWN dashboard rows \u2014 it can never read or delete.");
|
|
2469
|
+
console.log(" Nothing new is sent: the payload is identical to the manual push above.");
|
|
2470
|
+
console.log("");
|
|
2471
|
+
}
|
|
2472
|
+
async function cmdPush({ keepUpdated = false } = {}) {
|
|
2375
2473
|
const claimsMod = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
2376
2474
|
const { readProfile: readProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
2377
2475
|
const profile = await readProfile2();
|
|
@@ -2395,6 +2493,16 @@ async function cmdPush() {
|
|
|
2395
2493
|
process.exit(0);
|
|
2396
2494
|
}
|
|
2397
2495
|
const consentedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2496
|
+
let autoConsent = null;
|
|
2497
|
+
if (keepUpdated) {
|
|
2498
|
+
renderAutoConsent();
|
|
2499
|
+
const autoAnswer = await askYes(' Keep it updated in the background? Type "yes" to confirm: ');
|
|
2500
|
+
if (autoAnswer === "yes") {
|
|
2501
|
+
autoConsent = { version: AUTO_CONSENT_VERSION, consentedAt };
|
|
2502
|
+
} else {
|
|
2503
|
+
console.log("\n Background updates NOT enabled \u2014 doing a one-time push instead.");
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2398
2506
|
console.log("\n Starting browser verification...");
|
|
2399
2507
|
let begin;
|
|
2400
2508
|
try {
|
|
@@ -2467,7 +2575,9 @@ async function cmdPush() {
|
|
|
2467
2575
|
res = await fetch(`${CLAIM_SYNC_BASE}/api/claim-sync`, {
|
|
2468
2576
|
method: "POST",
|
|
2469
2577
|
headers: { "Content-Type": "application/json" },
|
|
2470
|
-
|
|
2578
|
+
// autoConsent is included ONLY when the dev opted into background updates —
|
|
2579
|
+
// the server mints the pushToken in the same push when it is present + valid.
|
|
2580
|
+
body: JSON.stringify({ consentToken, claims: pushed, proofToken, ...autoConsent ? { autoConsent } : {} }),
|
|
2471
2581
|
signal: AbortSignal.timeout(1e4)
|
|
2472
2582
|
});
|
|
2473
2583
|
} catch (err) {
|
|
@@ -2486,11 +2596,31 @@ async function cmdPush() {
|
|
|
2486
2596
|
process.exit(1);
|
|
2487
2597
|
}
|
|
2488
2598
|
let deleteToken = null;
|
|
2599
|
+
let pushToken = null;
|
|
2489
2600
|
try {
|
|
2490
|
-
|
|
2601
|
+
const body = await res.json();
|
|
2602
|
+
deleteToken = body?.deleteToken || null;
|
|
2603
|
+
pushToken = body?.pushToken || null;
|
|
2491
2604
|
} catch {
|
|
2492
2605
|
}
|
|
2493
2606
|
writeClaimPushMarker({ consentedAt, login, deleteToken });
|
|
2607
|
+
if (autoConsent && pushToken) {
|
|
2608
|
+
try {
|
|
2609
|
+
await writePushTokenEnc(pushToken);
|
|
2610
|
+
writeAutoMarker({
|
|
2611
|
+
autoConsentedAt: consentedAt,
|
|
2612
|
+
version: AUTO_CONSENT_VERSION,
|
|
2613
|
+
login,
|
|
2614
|
+
lastPushedAt: consentedAt,
|
|
2615
|
+
lastSnapshotHash: computeSnapshotHash(pushed)
|
|
2616
|
+
});
|
|
2617
|
+
console.log("\n \u2713 Background updates enabled \u2014 your dashboard will stay current");
|
|
2618
|
+
console.log(" (at most once/day). Stop any time: terminalhire claim --push --revoke");
|
|
2619
|
+
} catch {
|
|
2620
|
+
console.log("\n \u2713 Pushed, but could not enable background updates on this machine.");
|
|
2621
|
+
console.log(" Re-run `terminalhire claim --push --keep-updated` to retry.");
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2494
2624
|
console.log("\n \u2713 Your claims now show on your dashboard: https://terminalhire.com/dashboard");
|
|
2495
2625
|
console.log(" Delete them any time: terminalhire claim --push --revoke\n");
|
|
2496
2626
|
}
|
|
@@ -2532,7 +2662,10 @@ async function cmdRevoke() {
|
|
|
2532
2662
|
process.exit(1);
|
|
2533
2663
|
}
|
|
2534
2664
|
clearClaimPushMarker();
|
|
2665
|
+
clearPushTokenEnc();
|
|
2666
|
+
clearAutoMarker();
|
|
2535
2667
|
console.log("\n \u2713 Pushed claims deleted and local marker cleared.\n");
|
|
2668
|
+
console.log(" Background updates (if any) have been stopped.\n");
|
|
2536
2669
|
}
|
|
2537
2670
|
async function run() {
|
|
2538
2671
|
const verb = process.argv[2];
|
|
@@ -2541,7 +2674,7 @@ async function run() {
|
|
|
2541
2674
|
const json = Boolean(flags.json);
|
|
2542
2675
|
if (verb === "--push") {
|
|
2543
2676
|
if (flags.revoke) await cmdRevoke();
|
|
2544
|
-
else await cmdPush();
|
|
2677
|
+
else await cmdPush({ keepUpdated: Boolean(flags["keep-updated"]) });
|
|
2545
2678
|
return;
|
|
2546
2679
|
}
|
|
2547
2680
|
try {
|
|
@@ -2581,6 +2714,7 @@ async function run() {
|
|
|
2581
2714
|
}
|
|
2582
2715
|
export {
|
|
2583
2716
|
AI_DISCLOSURE_NOTE,
|
|
2717
|
+
CLAIM_CONSENT_VERSION,
|
|
2584
2718
|
buildSubmitBody,
|
|
2585
2719
|
cmdRecord,
|
|
2586
2720
|
findClaimableByShortRef,
|
|
@@ -1301,9 +1301,9 @@ var init_keytar = __esm({
|
|
|
1301
1301
|
}
|
|
1302
1302
|
});
|
|
1303
1303
|
|
|
1304
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
1304
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
1305
1305
|
var require_keytar = __commonJS({
|
|
1306
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
1306
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
1307
1307
|
"use strict";
|
|
1308
1308
|
init_keytar();
|
|
1309
1309
|
try {
|
package/dist/bin/jpi-devs.js
CHANGED
|
@@ -6759,9 +6759,9 @@ var init_keytar = __esm({
|
|
|
6759
6759
|
}
|
|
6760
6760
|
});
|
|
6761
6761
|
|
|
6762
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
6762
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
6763
6763
|
var require_keytar = __commonJS({
|
|
6764
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
6764
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
6765
6765
|
"use strict";
|
|
6766
6766
|
init_keytar();
|
|
6767
6767
|
try {
|