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.
@@ -285,7 +285,7 @@ function finalize(build) {
285
285
  };
286
286
  }
287
287
  function reconstruct(files, opts = {}) {
288
- const join4 = opts.joinSidechains !== false;
288
+ const join5 = opts.joinSidechains !== false;
289
289
  const mains = [];
290
290
  const sidechains = [];
291
291
  for (const file of files) {
@@ -310,7 +310,7 @@ function reconstruct(files, opts = {}) {
310
310
  }
311
311
  const orphanedSidechainPaths = [];
312
312
  const joinedPaths = /* @__PURE__ */ new Set();
313
- if (join4) {
313
+ if (join5) {
314
314
  const sidechainsBySession = /* @__PURE__ */ new Map();
315
315
  for (const sc of sidechains) {
316
316
  const acc = sidechainsBySession.get(sc.sessionId) ?? [];
@@ -855,6 +855,45 @@ var init_episodes = __esm({
855
855
  }
856
856
  });
857
857
 
858
+ // src/web-session.ts
859
+ import {
860
+ chmodSync,
861
+ existsSync,
862
+ mkdirSync,
863
+ readFileSync,
864
+ rmSync,
865
+ writeFileSync
866
+ } from "fs";
867
+ import { homedir } from "os";
868
+ import { join } from "path";
869
+ function terminalhireDir() {
870
+ return join(homedir(), ".terminalhire");
871
+ }
872
+ function webSessionFilePath() {
873
+ return join(terminalhireDir(), "web-session");
874
+ }
875
+ function readWebSessionFile() {
876
+ try {
877
+ const path = webSessionFilePath();
878
+ if (!existsSync(path)) return null;
879
+ const v = readFileSync(path, "utf8").trim();
880
+ return v.length > 0 ? v : null;
881
+ } catch {
882
+ return null;
883
+ }
884
+ }
885
+ function readWebSessionCookie() {
886
+ const fromFile = readWebSessionFile();
887
+ if (fromFile) return fromFile;
888
+ const env = process.env["TERMINALHIRE_WEB_SESSION"];
889
+ return typeof env === "string" && env.length > 0 ? env : null;
890
+ }
891
+ var init_web_session = __esm({
892
+ "src/web-session.ts"() {
893
+ "use strict";
894
+ }
895
+ });
896
+
858
897
  // ../../packages/core/src/types.ts
859
898
  var init_types = __esm({
860
899
  "../../packages/core/src/types.ts"() {
@@ -1530,8 +1569,8 @@ var init_feeds = __esm({
1530
1569
  });
1531
1570
 
1532
1571
  // ../../packages/core/src/partners.ts
1533
- import { readFileSync } from "fs";
1534
- import { join } from "path";
1572
+ import { readFileSync as readFileSync2 } from "fs";
1573
+ import { join as join2 } from "path";
1535
1574
  import { fileURLToPath } from "url";
1536
1575
  var EXAMPLE_BUYER, BUYER_REGISTRY;
1537
1576
  var init_partners = __esm({
@@ -1629,13 +1668,13 @@ import {
1629
1668
  randomBytes as randomBytes2
1630
1669
  } from "crypto";
1631
1670
  import {
1632
- readFileSync as readFileSync2,
1633
- writeFileSync,
1634
- mkdirSync,
1635
- existsSync
1671
+ readFileSync as readFileSync3,
1672
+ writeFileSync as writeFileSync2,
1673
+ mkdirSync as mkdirSync2,
1674
+ existsSync as existsSync2
1636
1675
  } from "fs";
1637
- import { join as join2 } from "path";
1638
- import { homedir } from "os";
1676
+ import { join as join3 } from "path";
1677
+ import { homedir as homedir2 } from "os";
1639
1678
  async function loadKey() {
1640
1679
  try {
1641
1680
  const kt = await import("keytar");
@@ -1648,12 +1687,12 @@ async function loadKey() {
1648
1687
  return key2;
1649
1688
  } catch {
1650
1689
  }
1651
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
1652
- if (existsSync(KEY_FILE)) {
1653
- return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
1690
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
1691
+ if (existsSync2(KEY_FILE)) {
1692
+ return Buffer.from(readFileSync3(KEY_FILE, "utf8").trim(), "hex");
1654
1693
  }
1655
1694
  const key = randomBytes2(KEY_BYTES);
1656
- writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
1695
+ writeFileSync2(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
1657
1696
  return key;
1658
1697
  }
1659
1698
  function encrypt(plaintext, key) {
@@ -1711,10 +1750,10 @@ function migrateTagWeights(profile) {
1711
1750
  }
1712
1751
  }
1713
1752
  async function readProfile() {
1714
- if (!existsSync(PROFILE_FILE)) return blankProfile();
1753
+ if (!existsSync2(PROFILE_FILE)) return blankProfile();
1715
1754
  try {
1716
1755
  const key = await loadKey();
1717
- const raw = readFileSync2(PROFILE_FILE, "utf8");
1756
+ const raw = readFileSync3(PROFILE_FILE, "utf8");
1718
1757
  const blob = JSON.parse(raw);
1719
1758
  const plaintext = decrypt(blob, key);
1720
1759
  const parsed = JSON.parse(plaintext);
@@ -1725,12 +1764,12 @@ async function readProfile() {
1725
1764
  }
1726
1765
  }
1727
1766
  async function writeProfile(profile) {
1728
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
1767
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
1729
1768
  const key = await loadKey();
1730
1769
  profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1731
1770
  profile.skillTags = deriveSkillTags(profile.tagWeights);
1732
1771
  const blob = encrypt(JSON.stringify(profile), key);
1733
- writeFileSync(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
1772
+ writeFileSync2(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
1734
1773
  }
1735
1774
  function accumulateSession(profile, tags, isEmployerContext, inferredSeniority, seniorityIsAuthoritative = false) {
1736
1775
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1791,13 +1830,13 @@ async function removeSavedJob(id) {
1791
1830
  return true;
1792
1831
  }
1793
1832
  async function deleteProfile() {
1794
- const { rmSync } = await import("fs");
1833
+ const { rmSync: rmSync2 } = await import("fs");
1795
1834
  try {
1796
- rmSync(PROFILE_FILE);
1835
+ rmSync2(PROFILE_FILE);
1797
1836
  } catch {
1798
1837
  }
1799
1838
  try {
1800
- rmSync(KEY_FILE);
1839
+ rmSync2(KEY_FILE);
1801
1840
  } catch {
1802
1841
  }
1803
1842
  }
@@ -1819,9 +1858,9 @@ var init_profile = __esm({
1819
1858
  "src/profile.ts"() {
1820
1859
  "use strict";
1821
1860
  init_src();
1822
- TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
1823
- PROFILE_FILE = join2(TERMINALHIRE_DIR, "profile.enc");
1824
- KEY_FILE = join2(TERMINALHIRE_DIR, "key");
1861
+ TERMINALHIRE_DIR = join3(homedir2(), ".terminalhire");
1862
+ PROFILE_FILE = join3(TERMINALHIRE_DIR, "profile.enc");
1863
+ KEY_FILE = join3(TERMINALHIRE_DIR, "key");
1825
1864
  ALGO = "aes-256-gcm";
1826
1865
  KEY_BYTES = 32;
1827
1866
  IV_BYTES = 12;
@@ -1891,14 +1930,14 @@ __export(trajectory_exports, {
1891
1930
  runTrajectoryPush: () => runTrajectoryPush
1892
1931
  });
1893
1932
  import {
1894
- existsSync as existsSync2,
1895
- mkdirSync as mkdirSync2,
1896
- readFileSync as readFileSync3,
1933
+ existsSync as existsSync3,
1934
+ mkdirSync as mkdirSync3,
1935
+ readFileSync as readFileSync4,
1897
1936
  readdirSync,
1898
- writeFileSync as writeFileSync2
1937
+ writeFileSync as writeFileSync3
1899
1938
  } from "fs";
1900
- import { homedir as homedir2 } from "os";
1901
- import { join as join3 } from "path";
1939
+ import { homedir as homedir3 } from "os";
1940
+ import { join as join4 } from "path";
1902
1941
  function isRecord4(value) {
1903
1942
  return typeof value === "object" && value !== null && !Array.isArray(value);
1904
1943
  }
@@ -1930,7 +1969,7 @@ function findJsonlFiles(dir) {
1930
1969
  return out;
1931
1970
  }
1932
1971
  for (const entry of entries) {
1933
- const full = join3(dir, entry.name);
1972
+ const full = join4(dir, entry.name);
1934
1973
  if (entry.isDirectory()) {
1935
1974
  out.push(...findJsonlFiles(full));
1936
1975
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -1944,7 +1983,7 @@ function loadCorpus(paths) {
1944
1983
  for (const path of paths) {
1945
1984
  let text;
1946
1985
  try {
1947
- text = readFileSync3(path, "utf8");
1986
+ text = readFileSync4(path, "utf8");
1948
1987
  } catch {
1949
1988
  continue;
1950
1989
  }
@@ -2043,12 +2082,12 @@ function renderMarkdown(view) {
2043
2082
  return lines.join("\n");
2044
2083
  }
2045
2084
  function writeExportArtifacts(score, markdown) {
2046
- const dir = join3(homedir2(), ".terminalhire");
2047
- mkdirSync2(dir, { recursive: true });
2048
- const jsonPath = join3(dir, "trajectory-export.json");
2049
- const mdPath = join3(dir, "trajectory-export.md");
2050
- writeFileSync2(jsonPath, JSON.stringify(score, null, 2) + "\n", "utf8");
2051
- writeFileSync2(mdPath, markdown, "utf8");
2085
+ const dir = join4(homedir3(), ".terminalhire");
2086
+ mkdirSync3(dir, { recursive: true });
2087
+ const jsonPath = join4(dir, "trajectory-export.json");
2088
+ const mdPath = join4(dir, "trajectory-export.md");
2089
+ writeFileSync3(jsonPath, JSON.stringify(score, null, 2) + "\n", "utf8");
2090
+ writeFileSync3(mdPath, markdown, "utf8");
2052
2091
  return { jsonPath, mdPath };
2053
2092
  }
2054
2093
  function renderInward(allNodes, view, files) {
@@ -2067,8 +2106,8 @@ function renderInward(allNodes, view, files) {
2067
2106
  console.log("");
2068
2107
  }
2069
2108
  function buildTrajectory() {
2070
- const projectsDir = join3(homedir2(), ".claude", "projects");
2071
- if (!existsSync2(projectsDir)) return null;
2109
+ const projectsDir = join4(homedir3(), ".claude", "projects");
2110
+ if (!existsSync3(projectsDir)) return null;
2072
2111
  const paths = findJsonlFiles(projectsDir);
2073
2112
  if (paths.length === 0) return null;
2074
2113
  const files = loadCorpus(paths);
@@ -2155,10 +2194,9 @@ function defaultPushDeps() {
2155
2194
  void Promise.resolve().then(() => (init_open_url(), open_url_exports)).then((m) => m.openInBrowser(url)).catch(() => {
2156
2195
  });
2157
2196
  },
2158
- sessionCookie: () => {
2159
- const v = process.env["TERMINALHIRE_WEB_SESSION"];
2160
- return typeof v === "string" && v.length > 0 ? v : null;
2161
- },
2197
+ // Session source priority: persisted file (`terminalhire link`) FIRST, then the
2198
+ // legacy TERMINALHIRE_WEB_SESSION env, then none.
2199
+ sessionCookie: () => readWebSessionCookie(),
2162
2200
  log: (msg) => console.log(msg),
2163
2201
  errorLog: (msg) => console.error(msg),
2164
2202
  exit: (code) => process.exit(code)
@@ -2317,6 +2355,7 @@ var init_trajectory = __esm({
2317
2355
  "src/trajectory.ts"() {
2318
2356
  "use strict";
2319
2357
  init_episodes();
2358
+ init_web_session();
2320
2359
  CAP_LABELS = {
2321
2360
  "cap:ui-automation": "UI automation",
2322
2361
  "cap:deploys": "Deployment",
@@ -1,7 +1,7 @@
1
1
  // src/chat-client.ts
2
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
3
- import { homedir as homedir3 } from "os";
4
- import { join as join4 } from "path";
2
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
3
+ import { homedir as homedir4 } from "os";
4
+ import { join as join5 } from "path";
5
5
 
6
6
  // ../../packages/core/src/vocab/graph.data.ts
7
7
  var VOCAB_NODES = [
@@ -3455,16 +3455,50 @@ async function loadOrCreateIdentity() {
3455
3455
  return keypair;
3456
3456
  }
3457
3457
 
3458
+ // src/web-session.ts
3459
+ import {
3460
+ chmodSync,
3461
+ existsSync as existsSync3,
3462
+ mkdirSync as mkdirSync3,
3463
+ readFileSync as readFileSync4,
3464
+ rmSync as rmSync3,
3465
+ writeFileSync as writeFileSync3
3466
+ } from "fs";
3467
+ import { homedir as homedir3 } from "os";
3468
+ import { join as join4 } from "path";
3469
+ function terminalhireDir() {
3470
+ return join4(homedir3(), ".terminalhire");
3471
+ }
3472
+ function webSessionFilePath() {
3473
+ return join4(terminalhireDir(), "web-session");
3474
+ }
3475
+ function readWebSessionFile() {
3476
+ try {
3477
+ const path = webSessionFilePath();
3478
+ if (!existsSync3(path)) return null;
3479
+ const v = readFileSync4(path, "utf8").trim();
3480
+ return v.length > 0 ? v : null;
3481
+ } catch {
3482
+ return null;
3483
+ }
3484
+ }
3485
+ function readWebSessionCookie() {
3486
+ const fromFile = readWebSessionFile();
3487
+ if (fromFile) return fromFile;
3488
+ const env = process.env["TERMINALHIRE_WEB_SESSION"];
3489
+ return typeof env === "string" && env.length > 0 ? env : null;
3490
+ }
3491
+
3458
3492
  // src/chat-client.ts
3459
3493
  var CHAT_BASE = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
3460
3494
  var GH_SESSION_COOKIE = "__jpi_gh_session";
3461
- var TERMINALHIRE_DIR3 = join4(homedir3(), ".terminalhire");
3462
- var PEERS_FILE = join4(TERMINALHIRE_DIR3, "chat-peers.json");
3495
+ var TERMINALHIRE_DIR3 = join5(homedir4(), ".terminalhire");
3496
+ var PEERS_FILE = join5(TERMINALHIRE_DIR3, "chat-peers.json");
3463
3497
  var REQUEST_TIMEOUT_MS = 1e4;
3464
3498
  var ChatNotLinkedError = class extends Error {
3465
3499
  constructor() {
3466
3500
  super(
3467
- `No linked web session found on this machine. Sign in at ${CHAT_BASE}/dashboard, then re-run with a bridged session (TERMINALHIRE_WEB_SESSION).`
3501
+ "No linked web session found on this machine. Run `terminalhire link` to connect this terminal to your account, then re-run."
3468
3502
  );
3469
3503
  this.name = "ChatNotLinkedError";
3470
3504
  }
@@ -3472,7 +3506,7 @@ var ChatNotLinkedError = class extends Error {
3472
3506
  var ChatSessionExpiredError = class extends Error {
3473
3507
  constructor() {
3474
3508
  super(
3475
- `Your web session expired \u2014 re-link it by signing in again at ${CHAT_BASE}/dashboard and re-bridging TERMINALHIRE_WEB_SESSION, then re-run.`
3509
+ "Your linked web session expired. Run `terminalhire link` to reconnect this terminal, then re-run."
3476
3510
  );
3477
3511
  this.name = "ChatSessionExpiredError";
3478
3512
  }
@@ -3499,8 +3533,8 @@ var ChatRequestError = class extends Error {
3499
3533
  };
3500
3534
  function defaultReadPeerPins() {
3501
3535
  try {
3502
- if (!existsSync3(PEERS_FILE)) return {};
3503
- const parsed = JSON.parse(readFileSync4(PEERS_FILE, "utf8"));
3536
+ if (!existsSync4(PEERS_FILE)) return {};
3537
+ const parsed = JSON.parse(readFileSync5(PEERS_FILE, "utf8"));
3504
3538
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
3505
3539
  const out = {};
3506
3540
  for (const [login, key] of Object.entries(parsed)) {
@@ -3512,16 +3546,15 @@ function defaultReadPeerPins() {
3512
3546
  }
3513
3547
  }
3514
3548
  function defaultWritePeerPins(pins) {
3515
- mkdirSync3(TERMINALHIRE_DIR3, { recursive: true });
3516
- writeFileSync3(PEERS_FILE, JSON.stringify(pins, null, 2), { mode: 384, encoding: "utf8" });
3549
+ mkdirSync4(TERMINALHIRE_DIR3, { recursive: true });
3550
+ writeFileSync4(PEERS_FILE, JSON.stringify(pins, null, 2), { mode: 384, encoding: "utf8" });
3517
3551
  }
3518
3552
  function defaultChatClientDeps() {
3519
3553
  return {
3520
3554
  fetchImpl: (...args) => globalThis.fetch(...args),
3521
- sessionCookie: () => {
3522
- const v = process.env["TERMINALHIRE_WEB_SESSION"];
3523
- return typeof v === "string" && v.length > 0 ? v : null;
3524
- },
3555
+ // Session source priority: persisted file (`terminalhire link`) FIRST, then the
3556
+ // legacy TERMINALHIRE_WEB_SESSION env, then none.
3557
+ sessionCookie: () => readWebSessionCookie(),
3525
3558
  loadIdentity: () => loadOrCreateIdentity(),
3526
3559
  readPeerPins: defaultReadPeerPins,
3527
3560
  writePeerPins: defaultWritePeerPins
package/dist/src/intro.js CHANGED
@@ -793,13 +793,13 @@ import {
793
793
  randomBytes as randomBytes2
794
794
  } from "crypto";
795
795
  import {
796
- readFileSync as readFileSync2,
797
- writeFileSync,
798
- mkdirSync,
799
- existsSync
796
+ readFileSync as readFileSync3,
797
+ writeFileSync as writeFileSync2,
798
+ mkdirSync as mkdirSync2,
799
+ existsSync as existsSync2
800
800
  } from "fs";
801
- import { join as join2 } from "path";
802
- import { homedir } from "os";
801
+ import { join as join3 } from "path";
802
+ import { homedir as homedir2 } from "os";
803
803
  async function loadKey() {
804
804
  try {
805
805
  const kt = await import("keytar");
@@ -812,12 +812,12 @@ async function loadKey() {
812
812
  return key2;
813
813
  } catch {
814
814
  }
815
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
816
- if (existsSync(KEY_FILE)) {
817
- return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
815
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
816
+ if (existsSync2(KEY_FILE)) {
817
+ return Buffer.from(readFileSync3(KEY_FILE, "utf8").trim(), "hex");
818
818
  }
819
819
  const key = randomBytes2(KEY_BYTES);
820
- writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
820
+ writeFileSync2(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
821
821
  return key;
822
822
  }
823
823
  function encrypt(plaintext, key) {
@@ -875,10 +875,10 @@ function migrateTagWeights(profile) {
875
875
  }
876
876
  }
877
877
  async function readProfile() {
878
- if (!existsSync(PROFILE_FILE)) return blankProfile();
878
+ if (!existsSync2(PROFILE_FILE)) return blankProfile();
879
879
  try {
880
880
  const key = await loadKey();
881
- const raw = readFileSync2(PROFILE_FILE, "utf8");
881
+ const raw = readFileSync3(PROFILE_FILE, "utf8");
882
882
  const blob = JSON.parse(raw);
883
883
  const plaintext = decrypt(blob, key);
884
884
  const parsed = JSON.parse(plaintext);
@@ -889,12 +889,12 @@ async function readProfile() {
889
889
  }
890
890
  }
891
891
  async function writeProfile(profile) {
892
- mkdirSync(TERMINALHIRE_DIR, { recursive: true });
892
+ mkdirSync2(TERMINALHIRE_DIR, { recursive: true });
893
893
  const key = await loadKey();
894
894
  profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
895
895
  profile.skillTags = deriveSkillTags(profile.tagWeights);
896
896
  const blob = encrypt(JSON.stringify(profile), key);
897
- writeFileSync(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
897
+ writeFileSync2(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
898
898
  }
899
899
  function accumulateSession(profile, tags, isEmployerContext, inferredSeniority, seniorityIsAuthoritative = false) {
900
900
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -955,13 +955,13 @@ async function removeSavedJob(id) {
955
955
  return true;
956
956
  }
957
957
  async function deleteProfile() {
958
- const { rmSync } = await import("fs");
958
+ const { rmSync: rmSync2 } = await import("fs");
959
959
  try {
960
- rmSync(PROFILE_FILE);
960
+ rmSync2(PROFILE_FILE);
961
961
  } catch {
962
962
  }
963
963
  try {
964
- rmSync(KEY_FILE);
964
+ rmSync2(KEY_FILE);
965
965
  } catch {
966
966
  }
967
967
  }
@@ -983,9 +983,9 @@ var init_profile = __esm({
983
983
  "src/profile.ts"() {
984
984
  "use strict";
985
985
  init_src();
986
- TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
987
- PROFILE_FILE = join2(TERMINALHIRE_DIR, "profile.enc");
988
- KEY_FILE = join2(TERMINALHIRE_DIR, "key");
986
+ TERMINALHIRE_DIR = join3(homedir2(), ".terminalhire");
987
+ PROFILE_FILE = join3(TERMINALHIRE_DIR, "profile.enc");
988
+ KEY_FILE = join3(TERMINALHIRE_DIR, "key");
989
989
  ALGO = "aes-256-gcm";
990
990
  KEY_BYTES = 32;
991
991
  IV_BYTES = 12;
@@ -1048,6 +1048,42 @@ var init_open_url = __esm({
1048
1048
 
1049
1049
  // src/intro.ts
1050
1050
  init_src();
1051
+
1052
+ // src/web-session.ts
1053
+ import {
1054
+ chmodSync,
1055
+ existsSync,
1056
+ mkdirSync,
1057
+ readFileSync as readFileSync2,
1058
+ rmSync,
1059
+ writeFileSync
1060
+ } from "fs";
1061
+ import { homedir } from "os";
1062
+ import { join as join2 } from "path";
1063
+ function terminalhireDir() {
1064
+ return join2(homedir(), ".terminalhire");
1065
+ }
1066
+ function webSessionFilePath() {
1067
+ return join2(terminalhireDir(), "web-session");
1068
+ }
1069
+ function readWebSessionFile() {
1070
+ try {
1071
+ const path = webSessionFilePath();
1072
+ if (!existsSync(path)) return null;
1073
+ const v = readFileSync2(path, "utf8").trim();
1074
+ return v.length > 0 ? v : null;
1075
+ } catch {
1076
+ return null;
1077
+ }
1078
+ }
1079
+ function readWebSessionCookie() {
1080
+ const fromFile = readWebSessionFile();
1081
+ if (fromFile) return fromFile;
1082
+ const env = process.env["TERMINALHIRE_WEB_SESSION"];
1083
+ return typeof env === "string" && env.length > 0 ? env : null;
1084
+ }
1085
+
1086
+ // src/intro.ts
1051
1087
  var LINK_BASE = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
1052
1088
  var GH_SESSION_COOKIE = "__jpi_gh_session";
1053
1089
  function defaultIntroDeps() {
@@ -1085,10 +1121,9 @@ function defaultIntroDeps() {
1085
1121
  void Promise.resolve().then(() => (init_open_url(), open_url_exports)).then((m) => m.openInBrowser(url)).catch(() => {
1086
1122
  });
1087
1123
  },
1088
- sessionCookie: () => {
1089
- const v = process.env["TERMINALHIRE_WEB_SESSION"];
1090
- return typeof v === "string" && v.length > 0 ? v : null;
1091
- },
1124
+ // Session source priority: persisted file (`terminalhire link`) FIRST, then the
1125
+ // legacy TERMINALHIRE_WEB_SESSION env, then none.
1126
+ sessionCookie: () => readWebSessionCookie(),
1092
1127
  log: (msg) => console.log(msg),
1093
1128
  errorLog: (msg) => console.error(msg),
1094
1129
  exit: (code) => process.exit(code)
@@ -1160,10 +1195,7 @@ async function runIntroRequest(args, overrides) {
1160
1195
  const cookie = deps.sessionCookie();
1161
1196
  if (!cookie) {
1162
1197
  deps.log("\n No linked web session found on this machine.");
1163
- deps.log(" Sign in at your dashboard first, then re-run with a bridged session.");
1164
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1165
- `);
1166
- deps.openBrowser(`${LINK_BASE}/dashboard`);
1198
+ deps.log(" Run `terminalhire link` to connect this terminal to your account, then re-run.\n");
1167
1199
  deps.exit(0);
1168
1200
  return;
1169
1201
  }
@@ -1183,9 +1215,8 @@ async function runIntroRequest(args, overrides) {
1183
1215
  return;
1184
1216
  }
1185
1217
  if (res.status === 401) {
1186
- deps.log("\n Your web session expired \u2014 sign in again at your dashboard, then re-run.");
1187
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1188
- `);
1218
+ deps.log("\n Your linked web session expired.");
1219
+ deps.log(" Run `terminalhire link` to reconnect this terminal, then re-run.\n");
1189
1220
  deps.exit(1);
1190
1221
  return;
1191
1222
  }
@@ -1216,10 +1247,7 @@ async function runIntroDecision(args, overrides) {
1216
1247
  const cookie = deps.sessionCookie();
1217
1248
  if (!cookie) {
1218
1249
  deps.log("\n No linked web session found on this machine.");
1219
- deps.log(" Sign in at your dashboard first, then re-run.");
1220
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1221
- `);
1222
- deps.openBrowser(`${LINK_BASE}/dashboard`);
1250
+ deps.log(" Run `terminalhire link` to connect this terminal to your account, then re-run.\n");
1223
1251
  deps.exit(0);
1224
1252
  return;
1225
1253
  }
@@ -1262,9 +1290,8 @@ async function runIntroDecision(args, overrides) {
1262
1290
  return;
1263
1291
  }
1264
1292
  if (res.status === 401) {
1265
- deps.log("\n Your web session expired \u2014 sign in again at your dashboard, then re-run.");
1266
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1267
- `);
1293
+ deps.log("\n Your linked web session expired.");
1294
+ deps.log(" Run `terminalhire link` to reconnect this terminal, then re-run.\n");
1268
1295
  deps.exit(1);
1269
1296
  return;
1270
1297
  }
@@ -1303,10 +1330,7 @@ async function runIntroList(overrides) {
1303
1330
  const cookie = deps.sessionCookie();
1304
1331
  if (!cookie) {
1305
1332
  deps.log("\n No linked web session found on this machine.");
1306
- deps.log(" Sign in at your dashboard first, then re-run.");
1307
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1308
- `);
1309
- deps.openBrowser(`${LINK_BASE}/dashboard`);
1333
+ deps.log(" Run `terminalhire link` to connect this terminal to your account, then re-run.\n");
1310
1334
  deps.exit(0);
1311
1335
  return;
1312
1336
  }
@@ -1325,9 +1349,8 @@ async function runIntroList(overrides) {
1325
1349
  return;
1326
1350
  }
1327
1351
  if (res.status === 401) {
1328
- deps.log("\n Your web session expired \u2014 sign in again at your dashboard, then re-run.");
1329
- deps.log(` \u2192 ${LINK_BASE}/dashboard
1330
- `);
1352
+ deps.log("\n Your linked web session expired.");
1353
+ deps.log(" Run `terminalhire link` to reconnect this terminal, then re-run.\n");
1331
1354
  deps.exit(1);
1332
1355
  return;
1333
1356
  }