terminalhire 0.9.0 → 0.9.2

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,253 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/open-url.js
12
+ var open_url_exports = {};
13
+ __export(open_url_exports, {
14
+ openInBrowser: () => openInBrowser
15
+ });
16
+ import { spawn } from "child_process";
17
+ function openInBrowser(url) {
18
+ let cmd;
19
+ let args;
20
+ if (process.platform === "darwin") {
21
+ cmd = "open";
22
+ args = [url];
23
+ } else if (process.platform === "win32") {
24
+ cmd = "cmd";
25
+ args = ["/c", "start", "", url];
26
+ } else {
27
+ cmd = "xdg-open";
28
+ args = [url];
29
+ }
30
+ try {
31
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
32
+ child.on("error", () => {
33
+ });
34
+ child.unref();
35
+ } catch {
36
+ }
37
+ }
38
+ var init_open_url = __esm({
39
+ "src/open-url.js"() {
40
+ "use strict";
41
+ }
42
+ });
43
+
44
+ // src/link.ts
45
+ import { createServer } from "http";
46
+ import { randomBytes } from "crypto";
47
+
48
+ // src/web-session.ts
49
+ import {
50
+ chmodSync,
51
+ existsSync,
52
+ mkdirSync,
53
+ readFileSync,
54
+ rmSync,
55
+ writeFileSync
56
+ } from "fs";
57
+ import { homedir } from "os";
58
+ import { join } from "path";
59
+ function terminalhireDir() {
60
+ return join(homedir(), ".terminalhire");
61
+ }
62
+ function webSessionFilePath() {
63
+ return join(terminalhireDir(), "web-session");
64
+ }
65
+ function readWebSessionFile() {
66
+ try {
67
+ const path = webSessionFilePath();
68
+ if (!existsSync(path)) return null;
69
+ const v = readFileSync(path, "utf8").trim();
70
+ return v.length > 0 ? v : null;
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ function writeWebSessionFile(token) {
76
+ mkdirSync(terminalhireDir(), { recursive: true });
77
+ const path = webSessionFilePath();
78
+ writeFileSync(path, token, { mode: 384, encoding: "utf8" });
79
+ try {
80
+ chmodSync(path, 384);
81
+ } catch {
82
+ }
83
+ }
84
+ function clearWebSessionFile() {
85
+ try {
86
+ rmSync(webSessionFilePath());
87
+ } catch {
88
+ }
89
+ }
90
+
91
+ // src/link.ts
92
+ var LINK_BASE = "https://www.terminalhire.com";
93
+ var GH_SESSION_COOKIE = "__jpi_gh_session";
94
+ var LINK_TIMEOUT_MS = 12e4;
95
+ function resolveLoopbackRequest(rawUrl, expectedNonce) {
96
+ let u;
97
+ try {
98
+ u = new URL(rawUrl, "http://127.0.0.1");
99
+ } catch {
100
+ return { ok: false, reason: "bad_url" };
101
+ }
102
+ const nonce = u.searchParams.get("nonce");
103
+ if (!nonce || nonce !== expectedNonce) return { ok: false, reason: "nonce_mismatch" };
104
+ const token = u.searchParams.get("token");
105
+ if (!token) return { ok: false, reason: "missing_token" };
106
+ return { ok: true, token };
107
+ }
108
+ var LINKED_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>terminalhire</title></head>
109
+ <body style="font-family:system-ui;padding:2rem;background:#0b0d10;color:#e6e6e6">
110
+ <script>history.replaceState({},'','/');</script>
111
+ <p>CLI linked \u2014 you can close this tab.</p>
112
+ <script>setTimeout(function(){window.close();},400);</script>
113
+ </body></html>`;
114
+ var FAILED_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>terminalhire</title></head>
115
+ <body style="font-family:system-ui;padding:2rem;background:#0b0d10;color:#e6e6e6">
116
+ <script>history.replaceState({},'','/');</script>
117
+ <p>Link failed \u2014 return to your terminal and run <code>terminalhire link</code> again.</p>
118
+ </body></html>`;
119
+ function defaultStartLoopback(expectedNonce, timeoutMs) {
120
+ return new Promise((resolveHandle) => {
121
+ let settle;
122
+ const result = new Promise((res) => {
123
+ settle = res;
124
+ });
125
+ let done = false;
126
+ const finish = (r) => {
127
+ if (done) return;
128
+ done = true;
129
+ clearTimeout(timer);
130
+ settle(r);
131
+ setImmediate(() => {
132
+ try {
133
+ server.close();
134
+ } catch {
135
+ }
136
+ });
137
+ };
138
+ const server = createServer((req, res) => {
139
+ const outcome = resolveLoopbackRequest(req.url ?? "", expectedNonce);
140
+ res.writeHead(outcome.ok ? 200 : 400, { "Content-Type": "text/html; charset=utf-8" });
141
+ res.end(outcome.ok ? LINKED_HTML : FAILED_HTML);
142
+ finish(outcome);
143
+ });
144
+ const timer = setTimeout(() => finish({ ok: false, reason: "timeout" }), timeoutMs);
145
+ if (typeof timer.unref === "function") timer.unref();
146
+ server.on("error", () => finish({ ok: false, reason: "listen_error" }));
147
+ server.listen(0, "127.0.0.1", () => {
148
+ const addr = server.address();
149
+ const port = typeof addr === "object" && addr ? addr.port : 0;
150
+ resolveHandle({
151
+ port,
152
+ result,
153
+ close: () => {
154
+ try {
155
+ server.close();
156
+ } catch {
157
+ }
158
+ }
159
+ });
160
+ });
161
+ });
162
+ }
163
+ function defaultLinkDeps() {
164
+ return {
165
+ startLoopback: defaultStartLoopback,
166
+ openBrowser: (url) => {
167
+ void Promise.resolve().then(() => (init_open_url(), open_url_exports)).then((m) => m.openInBrowser(url)).catch(() => {
168
+ });
169
+ },
170
+ generateNonce: () => randomBytes(16).toString("hex"),
171
+ persistToken: (token) => writeWebSessionFile(token),
172
+ log: (msg) => console.log(msg),
173
+ errorLog: (msg) => console.error(msg),
174
+ exit: (code) => process.exit(code)
175
+ };
176
+ }
177
+ async function runLink(overrides) {
178
+ const deps = { ...defaultLinkDeps(), ...overrides };
179
+ const nonce = deps.generateNonce();
180
+ const handle = await deps.startLoopback(nonce, LINK_TIMEOUT_MS);
181
+ const url = `${LINK_BASE}/api/auth/link?port=${handle.port}&nonce=${encodeURIComponent(nonce)}`;
182
+ deps.log("");
183
+ deps.log(" terminalhire \u2014 link this terminal to your account");
184
+ deps.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
185
+ deps.log(" Opening your browser to approve. If it does not open, paste this URL:");
186
+ deps.log(` \u2192 ${url}`);
187
+ deps.log(" Waiting for approval (this tab closes itself once you approve)\u2026");
188
+ deps.openBrowser(url);
189
+ let outcome;
190
+ try {
191
+ outcome = await handle.result;
192
+ } finally {
193
+ handle.close();
194
+ }
195
+ if (!outcome.ok || !outcome.token) {
196
+ if (outcome.reason === "timeout") {
197
+ deps.errorLog("\n Link timed out \u2014 run `terminalhire link` again.\n");
198
+ } else if (outcome.reason === "nonce_mismatch") {
199
+ deps.errorLog("\n Link rejected (nonce did not match) \u2014 run `terminalhire link` again.\n");
200
+ } else {
201
+ deps.errorLog("\n Link failed \u2014 run `terminalhire link` again.\n");
202
+ }
203
+ deps.exit(1);
204
+ return;
205
+ }
206
+ deps.persistToken(outcome.token);
207
+ deps.log("\n This terminal is now linked to your terminalhire account.");
208
+ deps.log(" Try `terminalhire intro <login>`, `terminalhire chat`, or `terminalhire trajectory --push`.");
209
+ deps.log(" Unlink any time with `terminalhire link --logout`.\n");
210
+ deps.exit(0);
211
+ }
212
+ function defaultLinkLogoutDeps() {
213
+ return {
214
+ fetchImpl: (...args) => globalThis.fetch(...args),
215
+ readSessionFile: () => readWebSessionFile(),
216
+ clearSessionFile: () => clearWebSessionFile(),
217
+ log: (msg) => console.log(msg),
218
+ errorLog: (msg) => console.error(msg),
219
+ exit: (code) => process.exit(code)
220
+ };
221
+ }
222
+ async function runLinkLogout(overrides) {
223
+ const deps = { ...defaultLinkLogoutDeps(), ...overrides };
224
+ const token = deps.readSessionFile();
225
+ if (!token) {
226
+ deps.log("\n No linked web session on this machine \u2014 nothing to unlink.\n");
227
+ deps.exit(0);
228
+ return;
229
+ }
230
+ let revoked = false;
231
+ try {
232
+ const res = await deps.fetchImpl(`${LINK_BASE}/api/auth/session`, {
233
+ method: "DELETE",
234
+ headers: { Cookie: `${GH_SESSION_COOKIE}=${token}` },
235
+ signal: AbortSignal.timeout(1e4)
236
+ });
237
+ revoked = res.ok;
238
+ } catch {
239
+ }
240
+ deps.clearSessionFile();
241
+ if (revoked) {
242
+ deps.log("\n Unlinked \u2014 the session was revoked server-side and removed from this machine.\n");
243
+ } else {
244
+ deps.log("\n Removed the local session from this machine.");
245
+ deps.log(" (Could not reach the server to revoke it \u2014 it expires on its own.)\n");
246
+ }
247
+ deps.exit(0);
248
+ }
249
+ export {
250
+ resolveLoopbackRequest,
251
+ runLink,
252
+ runLinkLogout
253
+ };
@@ -683,8 +683,8 @@ var init_feeds = __esm({
683
683
  });
684
684
 
685
685
  // ../../packages/core/src/partners.ts
686
- import { readFileSync } from "fs";
687
- import { join } from "path";
686
+ import { readFileSync as readFileSync2 } from "fs";
687
+ import { join as join2 } from "path";
688
688
  import { fileURLToPath } from "url";
689
689
  var EXAMPLE_BUYER, BUYER_REGISTRY;
690
690
  var init_partners = __esm({
@@ -782,13 +782,13 @@ import {
782
782
  randomBytes as randomBytes2
783
783
  } from "crypto";
784
784
  import {
785
- readFileSync as readFileSync2,
786
- writeFileSync,
787
- mkdirSync,
788
- existsSync
785
+ readFileSync as readFileSync3,
786
+ writeFileSync as writeFileSync2,
787
+ mkdirSync as mkdirSync2,
788
+ existsSync as existsSync2
789
789
  } from "fs";
790
- import { join as join2 } from "path";
791
- import { homedir } from "os";
790
+ import { join as join3 } from "path";
791
+ import { homedir as homedir2 } from "os";
792
792
  async function loadKey() {
793
793
  try {
794
794
  const kt = await import("keytar");
@@ -801,12 +801,12 @@ async function loadKey() {
801
801
  return key2;
802
802
  } catch {
803
803
  }
804
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
805
- if (existsSync(KEY_FILE)) {
806
- return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
804
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
805
+ if (existsSync2(KEY_FILE)) {
806
+ return Buffer.from(readFileSync3(KEY_FILE, "utf8").trim(), "hex");
807
807
  }
808
808
  const key = randomBytes2(KEY_BYTES);
809
- writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
809
+ writeFileSync2(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
810
810
  return key;
811
811
  }
812
812
  function encrypt(plaintext, key) {
@@ -864,10 +864,10 @@ function migrateTagWeights(profile) {
864
864
  }
865
865
  }
866
866
  async function readProfile() {
867
- if (!existsSync(PROFILE_FILE)) return blankProfile();
867
+ if (!existsSync2(PROFILE_FILE)) return blankProfile();
868
868
  try {
869
869
  const key = await loadKey();
870
- const raw = readFileSync2(PROFILE_FILE, "utf8");
870
+ const raw = readFileSync3(PROFILE_FILE, "utf8");
871
871
  const blob = JSON.parse(raw);
872
872
  const plaintext = decrypt(blob, key);
873
873
  const parsed = JSON.parse(plaintext);
@@ -878,12 +878,12 @@ async function readProfile() {
878
878
  }
879
879
  }
880
880
  async function writeProfile(profile) {
881
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
881
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
882
882
  const key = await loadKey();
883
883
  profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
884
884
  profile.skillTags = deriveSkillTags(profile.tagWeights);
885
885
  const blob = encrypt(JSON.stringify(profile), key);
886
- writeFileSync(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
886
+ writeFileSync2(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
887
887
  }
888
888
  function accumulateSession(profile, tags, isEmployerContext, inferredSeniority, seniorityIsAuthoritative = false) {
889
889
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -944,13 +944,13 @@ async function removeSavedJob(id) {
944
944
  return true;
945
945
  }
946
946
  async function deleteProfile() {
947
- const { rmSync } = await import("fs");
947
+ const { rmSync: rmSync2 } = await import("fs");
948
948
  try {
949
- rmSync(PROFILE_FILE);
949
+ rmSync2(PROFILE_FILE);
950
950
  } catch {
951
951
  }
952
952
  try {
953
- rmSync(KEY_FILE);
953
+ rmSync2(KEY_FILE);
954
954
  } catch {
955
955
  }
956
956
  }
@@ -972,9 +972,9 @@ var init_profile = __esm({
972
972
  "src/profile.ts"() {
973
973
  "use strict";
974
974
  init_src();
975
- TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
976
- PROFILE_FILE = join2(TERMINALHIRE_DIR, "profile.enc");
977
- KEY_FILE = join2(TERMINALHIRE_DIR, "key");
975
+ TERMINALHIRE_DIR = join3(homedir2(), ".terminalhire");
976
+ PROFILE_FILE = join3(TERMINALHIRE_DIR, "profile.enc");
977
+ KEY_FILE = join3(TERMINALHIRE_DIR, "key");
978
978
  ALGO = "aes-256-gcm";
979
979
  KEY_BYTES = 32;
980
980
  IV_BYTES = 12;
@@ -1037,14 +1037,14 @@ var init_open_url = __esm({
1037
1037
 
1038
1038
  // src/trajectory.ts
1039
1039
  import {
1040
- existsSync as existsSync2,
1041
- mkdirSync as mkdirSync2,
1042
- readFileSync as readFileSync3,
1040
+ existsSync as existsSync3,
1041
+ mkdirSync as mkdirSync3,
1042
+ readFileSync as readFileSync4,
1043
1043
  readdirSync,
1044
- writeFileSync as writeFileSync2
1044
+ writeFileSync as writeFileSync3
1045
1045
  } from "fs";
1046
- import { homedir as homedir2 } from "os";
1047
- import { join as join3 } from "path";
1046
+ import { homedir as homedir3 } from "os";
1047
+ import { join as join4 } from "path";
1048
1048
 
1049
1049
  // ../../packages/core/src/episodes/node-model.ts
1050
1050
  function isRecord(value) {
@@ -1306,7 +1306,7 @@ function finalize(build) {
1306
1306
  };
1307
1307
  }
1308
1308
  function reconstruct(files, opts = {}) {
1309
- const join4 = opts.joinSidechains !== false;
1309
+ const join5 = opts.joinSidechains !== false;
1310
1310
  const mains = [];
1311
1311
  const sidechains = [];
1312
1312
  for (const file of files) {
@@ -1331,7 +1331,7 @@ function reconstruct(files, opts = {}) {
1331
1331
  }
1332
1332
  const orphanedSidechainPaths = [];
1333
1333
  const joinedPaths = /* @__PURE__ */ new Set();
1334
- if (join4) {
1334
+ if (join5) {
1335
1335
  const sidechainsBySession = /* @__PURE__ */ new Map();
1336
1336
  for (const sc of sidechains) {
1337
1337
  const acc = sidechainsBySession.get(sc.sessionId) ?? [];
@@ -1776,6 +1776,40 @@ function deriveRecoveryDepth(episodes, nodesByUuid) {
1776
1776
  return inwardMetric({ maxConsecutiveErrors, meanRecoveryDepth, recoveryChains, spansConsidered, spansSkipped });
1777
1777
  }
1778
1778
 
1779
+ // src/web-session.ts
1780
+ import {
1781
+ chmodSync,
1782
+ existsSync,
1783
+ mkdirSync,
1784
+ readFileSync,
1785
+ rmSync,
1786
+ writeFileSync
1787
+ } from "fs";
1788
+ import { homedir } from "os";
1789
+ import { join } from "path";
1790
+ function terminalhireDir() {
1791
+ return join(homedir(), ".terminalhire");
1792
+ }
1793
+ function webSessionFilePath() {
1794
+ return join(terminalhireDir(), "web-session");
1795
+ }
1796
+ function readWebSessionFile() {
1797
+ try {
1798
+ const path = webSessionFilePath();
1799
+ if (!existsSync(path)) return null;
1800
+ const v = readFileSync(path, "utf8").trim();
1801
+ return v.length > 0 ? v : null;
1802
+ } catch {
1803
+ return null;
1804
+ }
1805
+ }
1806
+ function readWebSessionCookie() {
1807
+ const fromFile = readWebSessionFile();
1808
+ if (fromFile) return fromFile;
1809
+ const env = process.env["TERMINALHIRE_WEB_SESSION"];
1810
+ return typeof env === "string" && env.length > 0 ? env : null;
1811
+ }
1812
+
1779
1813
  // src/trajectory.ts
1780
1814
  function isRecord4(value) {
1781
1815
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1808,7 +1842,7 @@ function findJsonlFiles(dir) {
1808
1842
  return out;
1809
1843
  }
1810
1844
  for (const entry of entries) {
1811
- const full = join3(dir, entry.name);
1845
+ const full = join4(dir, entry.name);
1812
1846
  if (entry.isDirectory()) {
1813
1847
  out.push(...findJsonlFiles(full));
1814
1848
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -1822,7 +1856,7 @@ function loadCorpus(paths) {
1822
1856
  for (const path of paths) {
1823
1857
  let text;
1824
1858
  try {
1825
- text = readFileSync3(path, "utf8");
1859
+ text = readFileSync4(path, "utf8");
1826
1860
  } catch {
1827
1861
  continue;
1828
1862
  }
@@ -1932,12 +1966,12 @@ function renderMarkdown(view) {
1932
1966
  return lines.join("\n");
1933
1967
  }
1934
1968
  function writeExportArtifacts(score, markdown) {
1935
- const dir = join3(homedir2(), ".terminalhire");
1936
- mkdirSync2(dir, { recursive: true });
1937
- const jsonPath = join3(dir, "trajectory-export.json");
1938
- const mdPath = join3(dir, "trajectory-export.md");
1939
- writeFileSync2(jsonPath, JSON.stringify(score, null, 2) + "\n", "utf8");
1940
- writeFileSync2(mdPath, markdown, "utf8");
1969
+ const dir = join4(homedir3(), ".terminalhire");
1970
+ mkdirSync3(dir, { recursive: true });
1971
+ const jsonPath = join4(dir, "trajectory-export.json");
1972
+ const mdPath = join4(dir, "trajectory-export.md");
1973
+ writeFileSync3(jsonPath, JSON.stringify(score, null, 2) + "\n", "utf8");
1974
+ writeFileSync3(mdPath, markdown, "utf8");
1941
1975
  return { jsonPath, mdPath };
1942
1976
  }
1943
1977
  function renderInward(allNodes, view, files) {
@@ -1956,8 +1990,8 @@ function renderInward(allNodes, view, files) {
1956
1990
  console.log("");
1957
1991
  }
1958
1992
  function buildTrajectory() {
1959
- const projectsDir = join3(homedir2(), ".claude", "projects");
1960
- if (!existsSync2(projectsDir)) return null;
1993
+ const projectsDir = join4(homedir3(), ".claude", "projects");
1994
+ if (!existsSync3(projectsDir)) return null;
1961
1995
  const paths = findJsonlFiles(projectsDir);
1962
1996
  if (paths.length === 0) return null;
1963
1997
  const files = loadCorpus(paths);
@@ -2048,10 +2082,9 @@ function defaultPushDeps() {
2048
2082
  void Promise.resolve().then(() => (init_open_url(), open_url_exports)).then((m) => m.openInBrowser(url)).catch(() => {
2049
2083
  });
2050
2084
  },
2051
- sessionCookie: () => {
2052
- const v = process.env["TERMINALHIRE_WEB_SESSION"];
2053
- return typeof v === "string" && v.length > 0 ? v : null;
2054
- },
2085
+ // Session source priority: persisted file (`terminalhire link`) FIRST, then the
2086
+ // legacy TERMINALHIRE_WEB_SESSION env, then none.
2087
+ sessionCookie: () => readWebSessionCookie(),
2055
2088
  log: (msg) => console.log(msg),
2056
2089
  errorLog: (msg) => console.error(msg),
2057
2090
  exit: (code) => process.exit(code)
@@ -0,0 +1,55 @@
1
+ // src/web-session.ts
2
+ import {
3
+ chmodSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ rmSync,
8
+ writeFileSync
9
+ } from "fs";
10
+ import { homedir } from "os";
11
+ import { join } from "path";
12
+ function terminalhireDir() {
13
+ return join(homedir(), ".terminalhire");
14
+ }
15
+ function webSessionFilePath() {
16
+ return join(terminalhireDir(), "web-session");
17
+ }
18
+ function readWebSessionFile() {
19
+ try {
20
+ const path = webSessionFilePath();
21
+ if (!existsSync(path)) return null;
22
+ const v = readFileSync(path, "utf8").trim();
23
+ return v.length > 0 ? v : null;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ function readWebSessionCookie() {
29
+ const fromFile = readWebSessionFile();
30
+ if (fromFile) return fromFile;
31
+ const env = process.env["TERMINALHIRE_WEB_SESSION"];
32
+ return typeof env === "string" && env.length > 0 ? env : null;
33
+ }
34
+ function writeWebSessionFile(token) {
35
+ mkdirSync(terminalhireDir(), { recursive: true });
36
+ const path = webSessionFilePath();
37
+ writeFileSync(path, token, { mode: 384, encoding: "utf8" });
38
+ try {
39
+ chmodSync(path, 384);
40
+ } catch {
41
+ }
42
+ }
43
+ function clearWebSessionFile() {
44
+ try {
45
+ rmSync(webSessionFilePath());
46
+ } catch {
47
+ }
48
+ }
49
+ export {
50
+ clearWebSessionFile,
51
+ readWebSessionCookie,
52
+ readWebSessionFile,
53
+ webSessionFilePath,
54
+ writeWebSessionFile
55
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
5
5
  "repository": {
6
6
  "type": "git",