terminalhire 0.19.1 → 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
+ };
@@ -4407,7 +4407,9 @@ var init_config = __esm({
4407
4407
  contributeEnabled: false,
4408
4408
  contributePrompted: false,
4409
4409
  betaOptIn: false,
4410
- lastFullFeedbackAt: null
4410
+ lastFullFeedbackAt: null,
4411
+ lastPulseAskAt: null,
4412
+ pulseDisclosed: false
4411
4413
  };
4412
4414
  }
4413
4415
  });
@@ -4435,7 +4435,9 @@ var init_config = __esm({
4435
4435
  contributeEnabled: false,
4436
4436
  contributePrompted: false,
4437
4437
  betaOptIn: false,
4438
- lastFullFeedbackAt: null
4438
+ lastFullFeedbackAt: null,
4439
+ lastPulseAskAt: null,
4440
+ pulseDisclosed: false
4439
4441
  };
4440
4442
  }
4441
4443
  });