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.
@@ -0,0 +1,400 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") {
25
+ for (let key of __getOwnPropNames(from))
26
+ if (!__hasOwnProp.call(to, key) && key !== except)
27
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
28
+ }
29
+ return to;
30
+ };
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
32
+ // If the importer is in node compatibility mode or this is not an ESM
33
+ // file that has been converted to a CommonJS file using a Babel-
34
+ // compatible transform (i.e. "__esModule" has not been set), then set
35
+ // "default" to the CommonJS "module.exports" for node compatibility.
36
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
37
+ mod
38
+ ));
39
+
40
+ // ../../node_modules/keytar/build/Release/keytar.node
41
+ var keytar_default;
42
+ var init_keytar = __esm({
43
+ "../../node_modules/keytar/build/Release/keytar.node"() {
44
+ keytar_default = "../keytar-KOAAH267.node";
45
+ }
46
+ });
47
+
48
+ // node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
49
+ var require_keytar = __commonJS({
50
+ "node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
51
+ "use strict";
52
+ init_keytar();
53
+ try {
54
+ module.exports = __require(keytar_default);
55
+ } catch {
56
+ }
57
+ }
58
+ });
59
+
60
+ // ../../node_modules/keytar/lib/keytar.js
61
+ var require_keytar2 = __commonJS({
62
+ "../../node_modules/keytar/lib/keytar.js"(exports, module) {
63
+ "use strict";
64
+ var keytar = require_keytar();
65
+ function checkRequired(val, name) {
66
+ if (!val || val.length <= 0) {
67
+ throw new Error(name + " is required.");
68
+ }
69
+ }
70
+ module.exports = {
71
+ getPassword: function(service, account) {
72
+ checkRequired(service, "Service");
73
+ checkRequired(account, "Account");
74
+ return keytar.getPassword(service, account);
75
+ },
76
+ setPassword: function(service, account, password) {
77
+ checkRequired(service, "Service");
78
+ checkRequired(account, "Account");
79
+ checkRequired(password, "Password");
80
+ return keytar.setPassword(service, account, password);
81
+ },
82
+ deletePassword: function(service, account) {
83
+ checkRequired(service, "Service");
84
+ checkRequired(account, "Account");
85
+ return keytar.deletePassword(service, account);
86
+ },
87
+ findPassword: function(service) {
88
+ checkRequired(service, "Service");
89
+ return keytar.findPassword(service);
90
+ },
91
+ findCredentials: function(service) {
92
+ checkRequired(service, "Service");
93
+ return keytar.findCredentials(service);
94
+ }
95
+ };
96
+ }
97
+ });
98
+
99
+ // src/claims.ts
100
+ var claims_exports = {};
101
+ __export(claims_exports, {
102
+ acceptedPRRate: () => acceptedPRRate,
103
+ findClaim: () => findClaim,
104
+ listClaims: () => listClaims,
105
+ readClaims: () => readClaims,
106
+ recordClaim: () => recordClaim,
107
+ removeClaim: () => removeClaim,
108
+ toPushedClaim: () => toPushedClaim,
109
+ updateClaim: () => updateClaim
110
+ });
111
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, renameSync, existsSync as existsSync2 } from "fs";
112
+ import { join as join2 } from "path";
113
+ import { homedir as homedir2 } from "os";
114
+ function toPushedClaim(claim) {
115
+ return {
116
+ kind: claim.kind,
117
+ repoFullName: claim.repoFullName,
118
+ state: claim.state,
119
+ prUrl: claim.prUrl,
120
+ merged: claim.state === "merged",
121
+ claimedAt: claim.claimedAt,
122
+ updatedAt: claim.updatedAt
123
+ };
124
+ }
125
+ function nowISO() {
126
+ return (/* @__PURE__ */ new Date()).toISOString();
127
+ }
128
+ function normalizeClaim(c) {
129
+ return { ...c, kind: c.kind ?? "bounty", policy: c.policy ?? null };
130
+ }
131
+ function readClaims() {
132
+ try {
133
+ if (!existsSync2(CLAIMS_FILE)) return [];
134
+ const data = JSON.parse(readFileSync2(CLAIMS_FILE, "utf8"));
135
+ const claims = Array.isArray(data?.claims) ? data.claims : [];
136
+ return claims.map(normalizeClaim);
137
+ } catch {
138
+ return [];
139
+ }
140
+ }
141
+ function writeClaims(claims) {
142
+ mkdirSync2(TERMINALHIRE_DIR2, { recursive: true });
143
+ const tmp = `${CLAIMS_FILE}.tmp`;
144
+ const payload = { claims };
145
+ writeFileSync2(tmp, JSON.stringify(payload, null, 2), "utf8");
146
+ renameSync(tmp, CLAIMS_FILE);
147
+ }
148
+ function findClaim(id) {
149
+ return readClaims().find((c) => c.id === id) ?? null;
150
+ }
151
+ function listClaims(opts = {}) {
152
+ const claims = readClaims();
153
+ if (!opts.active) return claims;
154
+ return claims.filter((c) => !TERMINAL_STATES.has(c.state));
155
+ }
156
+ function recordClaim(rec) {
157
+ const claims = readClaims();
158
+ if (claims.some((c) => c.id === rec.id)) {
159
+ throw new Error(
160
+ `claim already exists for '${rec.id}' \u2014 run 'terminalhire claim status ${rec.id}' or 'terminalhire claim release ${rec.id}'`
161
+ );
162
+ }
163
+ const ts = nowISO();
164
+ const claim = {
165
+ ...rec,
166
+ // Defensive default (mirrors normalizeClaim's `kind ?? 'bounty'` pattern):
167
+ // a caller written before `policy` existed, or a plain-JS caller that skips
168
+ // it, still produces a valid record instead of `policy: undefined`.
169
+ policy: rec.policy ?? null,
170
+ state: "claimed",
171
+ worktreePath: null,
172
+ branch: null,
173
+ prUrl: null,
174
+ review: null,
175
+ claimedAt: ts,
176
+ updatedAt: ts
177
+ };
178
+ claims.push(claim);
179
+ writeClaims(claims);
180
+ return claim;
181
+ }
182
+ function updateClaim(id, patch) {
183
+ const claims = readClaims();
184
+ const idx = claims.findIndex((c) => c.id === id);
185
+ if (idx === -1) return null;
186
+ claims[idx] = { ...claims[idx], ...patch, updatedAt: nowISO() };
187
+ writeClaims(claims);
188
+ return claims[idx];
189
+ }
190
+ function removeClaim(id) {
191
+ const claims = readClaims();
192
+ const next = claims.filter((c) => c.id !== id);
193
+ if (next.length === claims.length) return false;
194
+ writeClaims(next);
195
+ return true;
196
+ }
197
+ function acceptedPRRate(claims = readClaims()) {
198
+ const total = claims.length;
199
+ const merged = claims.filter((c) => c.state === "merged").length;
200
+ return { merged, total, rate: total === 0 ? 0 : merged / total };
201
+ }
202
+ var TERMINALHIRE_DIR2, CLAIMS_FILE, TERMINAL_STATES;
203
+ var init_claims = __esm({
204
+ "src/claims.ts"() {
205
+ "use strict";
206
+ TERMINALHIRE_DIR2 = join2(homedir2(), ".terminalhire");
207
+ CLAIMS_FILE = join2(TERMINALHIRE_DIR2, "claims.json");
208
+ TERMINAL_STATES = /* @__PURE__ */ new Set(["merged", "abandoned"]);
209
+ }
210
+ });
211
+
212
+ // bin/claim-push-bg.js
213
+ import { createHash } from "crypto";
214
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, rmSync as rmSync2 } from "fs";
215
+ import { join as join3 } from "path";
216
+ import { homedir as homedir3 } from "os";
217
+
218
+ // src/github-auth.ts
219
+ import {
220
+ createCipheriv,
221
+ createDecipheriv,
222
+ randomBytes
223
+ } from "crypto";
224
+ import {
225
+ readFileSync,
226
+ writeFileSync,
227
+ mkdirSync,
228
+ existsSync,
229
+ rmSync
230
+ } from "fs";
231
+ import { join } from "path";
232
+ import { homedir } from "os";
233
+ var TERMINALHIRE_DIR = join(homedir(), ".terminalhire");
234
+ var TOKEN_FILE = join(TERMINALHIRE_DIR, "github-token.enc");
235
+ var KEY_FILE = join(TERMINALHIRE_DIR, "key");
236
+ var ALGO = "aes-256-gcm";
237
+ var KEY_BYTES = 32;
238
+ var IV_BYTES = 12;
239
+ async function loadKey() {
240
+ try {
241
+ const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
242
+ const stored = await kt.getPassword("terminalhire", "profile-key");
243
+ if (stored) return Buffer.from(stored, "hex");
244
+ const key2 = randomBytes(KEY_BYTES);
245
+ await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
246
+ return key2;
247
+ } catch {
248
+ }
249
+ mkdirSync(TERMINALHIRE_DIR, { recursive: true });
250
+ if (existsSync(KEY_FILE)) {
251
+ return Buffer.from(readFileSync(KEY_FILE, "utf8").trim(), "hex");
252
+ }
253
+ const key = randomBytes(KEY_BYTES);
254
+ writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
255
+ return key;
256
+ }
257
+ function encrypt(plaintext, key) {
258
+ const iv = randomBytes(IV_BYTES);
259
+ const cipher = createCipheriv(ALGO, key, iv);
260
+ const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
261
+ const tag = cipher.getAuthTag();
262
+ return { iv: iv.toString("hex"), tag: tag.toString("hex"), ciphertext: ct.toString("hex") };
263
+ }
264
+ function decrypt(blob, key) {
265
+ const decipher = createDecipheriv(ALGO, key, Buffer.from(blob.iv, "hex"));
266
+ decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
267
+ const plain = Buffer.concat([
268
+ decipher.update(Buffer.from(blob.ciphertext, "hex")),
269
+ decipher.final()
270
+ ]);
271
+ return plain.toString("utf8");
272
+ }
273
+
274
+ // bin/claim-push-bg.js
275
+ var TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join3(homedir3(), ".terminalhire");
276
+ var CLAIM_PUSH_AUTO_MARKER = join3(TERMINALHIRE_DIR3, "claim-push-auto.json");
277
+ var CLAIM_PUSH_TOKEN_FILE = join3(TERMINALHIRE_DIR3, "claim-push-token.enc");
278
+ var CLAIM_SYNC_BASE = "https://terminalhire.com";
279
+ var AUTO_CONSENT_VERSION = 2;
280
+ var AUTO_PUSH_THROTTLE_MS = 24 * 60 * 60 * 1e3;
281
+ var CLAIM_PUSH_FIELDS = ["kind", "repoFullName", "state", "prUrl", "merged", "claimedAt", "updatedAt"];
282
+ async function writePushTokenEnc(rawToken) {
283
+ mkdirSync3(TERMINALHIRE_DIR3, { recursive: true });
284
+ const key = await loadKey();
285
+ const blob = encrypt(rawToken, key);
286
+ writeFileSync3(CLAIM_PUSH_TOKEN_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
287
+ }
288
+ async function readPushTokenEnc() {
289
+ if (!existsSync3(CLAIM_PUSH_TOKEN_FILE)) return void 0;
290
+ try {
291
+ const key = await loadKey();
292
+ const blob = JSON.parse(readFileSync3(CLAIM_PUSH_TOKEN_FILE, "utf8"));
293
+ return decrypt(blob, key);
294
+ } catch {
295
+ return void 0;
296
+ }
297
+ }
298
+ function clearPushTokenEnc() {
299
+ try {
300
+ rmSync2(CLAIM_PUSH_TOKEN_FILE);
301
+ } catch {
302
+ }
303
+ }
304
+ function readAutoMarker() {
305
+ try {
306
+ return existsSync3(CLAIM_PUSH_AUTO_MARKER) ? JSON.parse(readFileSync3(CLAIM_PUSH_AUTO_MARKER, "utf8")) : null;
307
+ } catch {
308
+ return null;
309
+ }
310
+ }
311
+ function writeAutoMarker(marker) {
312
+ mkdirSync3(TERMINALHIRE_DIR3, { recursive: true });
313
+ writeFileSync3(CLAIM_PUSH_AUTO_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
314
+ }
315
+ function clearAutoMarker() {
316
+ try {
317
+ rmSync2(CLAIM_PUSH_AUTO_MARKER);
318
+ } catch {
319
+ }
320
+ }
321
+ function computeSnapshotHash(pushed) {
322
+ return createHash("sha256").update(JSON.stringify(pushed)).digest("hex");
323
+ }
324
+ function backgroundPushGate(params) {
325
+ const {
326
+ autoMarkerExists,
327
+ tokenFileExists,
328
+ lastPushedAt,
329
+ now,
330
+ throttleMs,
331
+ currentHash,
332
+ lastSnapshotHash
333
+ } = params;
334
+ if (!autoMarkerExists || !tokenFileExists) {
335
+ return { push: false, reason: "not-opted-in" };
336
+ }
337
+ const last = lastPushedAt ? Date.parse(lastPushedAt) : NaN;
338
+ if (!Number.isNaN(last) && now - last < throttleMs) {
339
+ return { push: false, reason: "throttled" };
340
+ }
341
+ if (lastSnapshotHash && lastSnapshotHash === currentHash) {
342
+ return { push: false, reason: "unchanged" };
343
+ }
344
+ return { push: true, reason: "ok" };
345
+ }
346
+ async function runBackgroundClaimPush({ now = Date.now() } = {}) {
347
+ try {
348
+ if (!existsSync3(CLAIM_PUSH_AUTO_MARKER) || !existsSync3(CLAIM_PUSH_TOKEN_FILE)) return;
349
+ const marker = readAutoMarker();
350
+ if (!marker || !marker.autoConsentedAt) return;
351
+ const { listClaims: listClaims2, toPushedClaim: toPushedClaim2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
352
+ const pushed = listClaims2().map((c) => toPushedClaim2(c));
353
+ const currentHash = computeSnapshotHash(pushed);
354
+ const gate = backgroundPushGate({
355
+ autoMarkerExists: true,
356
+ tokenFileExists: true,
357
+ lastPushedAt: marker.lastPushedAt ?? null,
358
+ now,
359
+ throttleMs: AUTO_PUSH_THROTTLE_MS,
360
+ currentHash,
361
+ lastSnapshotHash: marker.lastSnapshotHash ?? null
362
+ });
363
+ if (!gate.push) return;
364
+ const token = await readPushTokenEnc();
365
+ if (!token) return;
366
+ const consentToken = {
367
+ consentedAt: marker.autoConsentedAt,
368
+ version: AUTO_CONSENT_VERSION,
369
+ fields: CLAIM_PUSH_FIELDS
370
+ };
371
+ const res = await fetch(`${CLAIM_SYNC_BASE}/api/claim-sync`, {
372
+ method: "POST",
373
+ headers: { "Content-Type": "application/json" },
374
+ body: JSON.stringify({ consentToken, claims: pushed, pushToken: token }),
375
+ signal: AbortSignal.timeout(1e4)
376
+ });
377
+ if (!res.ok) return;
378
+ writeAutoMarker({
379
+ ...marker,
380
+ lastPushedAt: new Date(now).toISOString(),
381
+ lastSnapshotHash: currentHash
382
+ });
383
+ } catch {
384
+ }
385
+ }
386
+ export {
387
+ AUTO_CONSENT_VERSION,
388
+ AUTO_PUSH_THROTTLE_MS,
389
+ CLAIM_PUSH_AUTO_MARKER,
390
+ CLAIM_PUSH_TOKEN_FILE,
391
+ backgroundPushGate,
392
+ clearAutoMarker,
393
+ clearPushTokenEnc,
394
+ computeSnapshotHash,
395
+ readAutoMarker,
396
+ readPushTokenEnc,
397
+ runBackgroundClaimPush,
398
+ writeAutoMarker,
399
+ writePushTokenEnc
400
+ };
@@ -6759,9 +6759,9 @@ var init_keytar = __esm({
6759
6759
  }
6760
6760
  });
6761
6761
 
6762
- // node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node
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/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node"(exports, module) {
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 {
@@ -3950,9 +3950,9 @@ var init_keytar = __esm({
3950
3950
  }
3951
3951
  });
3952
3952
 
3953
- // node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node
3953
+ // node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
3954
3954
  var require_keytar = __commonJS({
3955
- "node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node"(exports, module) {
3955
+ "node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
3956
3956
  "use strict";
3957
3957
  init_keytar();
3958
3958
  try {
@@ -3978,9 +3978,9 @@ var init_keytar = __esm({
3978
3978
  }
3979
3979
  });
3980
3980
 
3981
- // node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node
3981
+ // node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
3982
3982
  var require_keytar = __commonJS({
3983
- "node-file:/Users/ericgang/job-placement-inline/.claude/worktrees/release-0192/node_modules/keytar/build/Release/keytar.node"(exports, module) {
3983
+ "node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
3984
3984
  "use strict";
3985
3985
  init_keytar();
3986
3986
  try {