terminalhire 0.41.1 → 0.42.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.
@@ -11167,6 +11167,83 @@ var init_github_auth = __esm({
11167
11167
  }
11168
11168
  });
11169
11169
 
11170
+ // bin/founder-note-sync.js
11171
+ var founder_note_sync_exports = {};
11172
+ __export(founder_note_sync_exports, {
11173
+ acknowledgeFounderNotes: () => acknowledgeFounderNotes,
11174
+ computeFounderNotes: () => computeFounderNotes,
11175
+ fetchFounderNotes: () => fetchFounderNotes,
11176
+ formatNote: () => formatNote,
11177
+ noteKey: () => noteKey,
11178
+ syncFounderNotes: () => syncFounderNotes
11179
+ });
11180
+ async function fetchFounderNotes(pushToken, fetchImpl = fetch) {
11181
+ if (typeof pushToken !== "string" || pushToken.length === 0) return null;
11182
+ try {
11183
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE2}/api/claim/notes`, {
11184
+ method: "POST",
11185
+ headers: { "Content-Type": "application/json" },
11186
+ body: JSON.stringify({ pushToken }),
11187
+ signal: AbortSignal.timeout(15e3)
11188
+ });
11189
+ if (!res?.ok) return null;
11190
+ const body = await res.json();
11191
+ if (!body || !Array.isArray(body.notes)) return null;
11192
+ const notes = body.notes.filter(
11193
+ (n) => n && typeof n.claimId === "string" && n.claimId !== "" && typeof n.note === "string" && n.note !== "" && typeof n.at === "string" && (n.kind === "founder_note" || n.kind === "feedback")
11194
+ );
11195
+ return { notes, latestAt: typeof body.latestAt === "string" ? body.latestAt : null };
11196
+ } catch {
11197
+ return null;
11198
+ }
11199
+ }
11200
+ function noteKey(note) {
11201
+ return `${note.claimId}@${note.at}`;
11202
+ }
11203
+ function computeFounderNotes(notes, previous) {
11204
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
11205
+ const prior = previous && Array.isArray(previous.seen) ? new Set(previous.seen) : /* @__PURE__ */ new Set();
11206
+ const seen = keys.filter((k) => prior.has(k));
11207
+ return { count: keys.length - seen.length, seen };
11208
+ }
11209
+ function acknowledgeFounderNotes(notes) {
11210
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
11211
+ return { count: 0, seen: keys };
11212
+ }
11213
+ async function syncFounderNotes({
11214
+ readAutoMarker: readAutoMarker2,
11215
+ readPushTokenEnc: readPushTokenEnc2,
11216
+ readPrevious,
11217
+ fetchImpl = fetch,
11218
+ timeoutMs = 15e3
11219
+ } = {}) {
11220
+ try {
11221
+ const marker = readAutoMarker2();
11222
+ const token = await readPushTokenEnc2();
11223
+ if (!marker || !token) return null;
11224
+ const answer = await fetchFounderNotes(
11225
+ token,
11226
+ (url, init) => fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })
11227
+ );
11228
+ if (!answer) return null;
11229
+ return computeFounderNotes(answer.notes, readPrevious());
11230
+ } catch {
11231
+ return null;
11232
+ }
11233
+ }
11234
+ function formatNote(note) {
11235
+ const label = note.kind === "feedback" ? "changes requested" : "note";
11236
+ return ` ${note.at.slice(0, 16).replace("T", " ")} [${label}] ${note.claimId}
11237
+ ${note.note.split("\n").join("\n ")}`;
11238
+ }
11239
+ var CLAIM_SYNC_BASE2;
11240
+ var init_founder_note_sync = __esm({
11241
+ "bin/founder-note-sync.js"() {
11242
+ "use strict";
11243
+ CLAIM_SYNC_BASE2 = "https://terminalhire.com";
11244
+ }
11245
+ });
11246
+
11170
11247
  // ../../packages/core/src/policy-audit.ts
11171
11248
  function fence(nonce, body) {
11172
11249
  return `<<<UNTRUSTED-${nonce}
@@ -26322,6 +26399,7 @@ async function syncFounderVerdicts({
26322
26399
  }
26323
26400
 
26324
26401
  // bin/jpi-claim.js
26402
+ init_founder_note_sync();
26325
26403
  var TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR || join17(homedir11(), ".terminalhire");
26326
26404
  var INDEX_CACHE_FILE2 = join17(TERMINALHIRE_DIR11, "index-cache.json");
26327
26405
  var CLAIM_PUSH_MARKER = join17(TERMINALHIRE_DIR11, "claim-push.json");
@@ -26344,7 +26422,7 @@ function markClaimNudged(id) {
26344
26422
  }
26345
26423
  }
26346
26424
  var API_URL = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
26347
- var CLAIM_SYNC_BASE2 = "https://terminalhire.com";
26425
+ var CLAIM_SYNC_BASE3 = "https://terminalhire.com";
26348
26426
  var CLAIM_CONSENT_VERSION = 1;
26349
26427
  var CLAIM_POLL_INTERVAL_MS = 2e3;
26350
26428
  var CLAIM_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -27179,7 +27257,7 @@ async function mintRegistrationProof() {
27179
27257
  console.log(" GitHub identity has to be verified once in the browser.");
27180
27258
  let begin;
27181
27259
  try {
27182
- const r = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync/begin`, {
27260
+ const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
27183
27261
  method: "POST",
27184
27262
  headers: { "Content-Type": "application/json" },
27185
27263
  body: JSON.stringify({ hostname: osHostname() }),
@@ -27212,7 +27290,7 @@ async function mintRegistrationProof() {
27212
27290
  let statusRes;
27213
27291
  try {
27214
27292
  statusRes = await fetch(
27215
- `${CLAIM_SYNC_BASE2}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
27293
+ `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
27216
27294
  { signal: AbortSignal.timeout(1e4) }
27217
27295
  );
27218
27296
  } catch {
@@ -27339,7 +27417,7 @@ terminalhire claim: refusing to record \u2014 ${reason}
27339
27417
  };
27340
27418
  if (!auth) await acquireProofAuth();
27341
27419
  console.log("\n Registering this claim with terminalhire (founder posting)...");
27342
- const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE2}/api/claim/register`, {
27420
+ const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE3}/api/claim/register`, {
27343
27421
  method: "POST",
27344
27422
  headers: { "Content-Type": "application/json" },
27345
27423
  body: JSON.stringify({
@@ -27919,7 +27997,7 @@ async function resolveIssueOutcome(c, res, claims) {
27919
27997
  async function fetchFounderApprovals(pushToken, fetchImpl = fetch) {
27920
27998
  if (typeof pushToken !== "string" || pushToken.length === 0) return null;
27921
27999
  try {
27922
- const res = await fetchImpl(`${CLAIM_SYNC_BASE2}/api/claim/approvals`, {
28000
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE3}/api/claim/approvals`, {
27923
28001
  method: "POST",
27924
28002
  headers: { "Content-Type": "application/json" },
27925
28003
  body: JSON.stringify({ pushToken }),
@@ -28773,7 +28851,7 @@ async function cmdSlice(id, flags = {}) {
28773
28851
  const pushToken = await requireReadPushToken("fetching your granted slice");
28774
28852
  let res;
28775
28853
  try {
28776
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim/slice`, {
28854
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim/slice`, {
28777
28855
  method: "POST",
28778
28856
  headers: { "Content-Type": "application/json" },
28779
28857
  body: JSON.stringify({
@@ -28881,7 +28959,7 @@ async function cmdRuns(id, flags = {}) {
28881
28959
  const fetchOnce = async () => {
28882
28960
  let res;
28883
28961
  try {
28884
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/patch/runs`, {
28962
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch/runs`, {
28885
28963
  method: "POST",
28886
28964
  headers: { "Content-Type": "application/json" },
28887
28965
  body: JSON.stringify({
@@ -29029,7 +29107,7 @@ async function submitFounderPatch({ claims, claim, id, wt, flags }) {
29029
29107
  console.log("\n Submitting the patch through terminalhire...");
29030
29108
  let res;
29031
29109
  try {
29032
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/patch`, {
29110
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch`, {
29033
29111
  method: "POST",
29034
29112
  headers: { "Content-Type": "application/json" },
29035
29113
  body: JSON.stringify(submission),
@@ -29602,7 +29680,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29602
29680
  console.log("\n Starting browser verification...");
29603
29681
  let begin;
29604
29682
  try {
29605
- const r = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync/begin`, {
29683
+ const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
29606
29684
  method: "POST",
29607
29685
  headers: { "Content-Type": "application/json" },
29608
29686
  body: JSON.stringify({ hostname: osHostname() }),
@@ -29645,7 +29723,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29645
29723
  let statusRes;
29646
29724
  try {
29647
29725
  statusRes = await fetch(
29648
- `${CLAIM_SYNC_BASE2}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
29726
+ `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
29649
29727
  { signal: AbortSignal.timeout(1e4) }
29650
29728
  );
29651
29729
  } catch {
@@ -29676,7 +29754,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29676
29754
  console.log("\n Verified. Sharing your claims...");
29677
29755
  let res;
29678
29756
  try {
29679
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync`, {
29757
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
29680
29758
  method: "POST",
29681
29759
  headers: { "Content-Type": "application/json" },
29682
29760
  // autoConsent is included ONLY when the dev opted into background updates —
@@ -29803,7 +29881,7 @@ async function cmdRevoke() {
29803
29881
  console.log("\n Requesting deletion...");
29804
29882
  let res;
29805
29883
  try {
29806
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync`, {
29884
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
29807
29885
  method: "DELETE",
29808
29886
  headers: { "Content-Type": "application/json" },
29809
29887
  body: JSON.stringify({ login, deleteToken }),
@@ -29980,6 +30058,50 @@ async function cmdAudit(id, flags = {}) {
29980
30058
  }
29981
30059
  renderAudit(view);
29982
30060
  }
30061
+ async function cmdNotes(flags) {
30062
+ let pushToken = null;
30063
+ try {
30064
+ pushToken = await readPushTokenEnc();
30065
+ } catch {
30066
+ }
30067
+ if (!pushToken) {
30068
+ console.error("terminalhire claim notes: needs background push enrolment.");
30069
+ console.error(" Enrol once: terminalhire claim --push --keep-updated");
30070
+ process.exit(1);
30071
+ }
30072
+ const answer = await fetchFounderNotes(pushToken);
30073
+ if (answer === null) {
30074
+ console.error("terminalhire claim notes: could not reach terminalhire just now.");
30075
+ console.error(' This is NOT "no notes" \u2014 try again shortly.');
30076
+ process.exit(1);
30077
+ }
30078
+ if (flags.json) {
30079
+ console.log(JSON.stringify(answer, null, 2));
30080
+ await acknowledgeNotesQuietly(answer.notes);
30081
+ return;
30082
+ }
30083
+ if (answer.notes.length === 0) {
30084
+ console.log("\n No notes from founders on your claims.\n");
30085
+ await acknowledgeNotesQuietly(answer.notes);
30086
+ return;
30087
+ }
30088
+ console.log(
30089
+ `
30090
+ ${answer.notes.length} note${answer.notes.length === 1 ? "" : "s"} from founders:
30091
+ `
30092
+ );
30093
+ for (const note of answer.notes) console.log(formatNote(note));
30094
+ console.log("");
30095
+ await acknowledgeNotesQuietly(answer.notes);
30096
+ }
30097
+ async function acknowledgeNotesQuietly(notes) {
30098
+ try {
30099
+ const { acknowledgeFounderNotes: acknowledgeFounderNotes2 } = await Promise.resolve().then(() => (init_founder_note_sync(), founder_note_sync_exports));
30100
+ const { updateIndexCache: updateIndexCache2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
30101
+ updateIndexCache2({ founderNotes: acknowledgeFounderNotes2(notes) });
30102
+ } catch {
30103
+ }
30104
+ }
29983
30105
  async function run() {
29984
30106
  const verb = process.argv[2];
29985
30107
  const rawArgs = process.argv.slice(3).map((a) => a === "-y" ? "--yes" : a);
@@ -30042,9 +30164,12 @@ async function run() {
30042
30164
  case "audit":
30043
30165
  await cmdAudit(positional[0], flags);
30044
30166
  break;
30167
+ case "notes":
30168
+ await cmdNotes(flags);
30169
+ break;
30045
30170
  default:
30046
30171
  console.error(
30047
- `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | audit | release`
30172
+ `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | notes | audit | release`
30048
30173
  );
30049
30174
  process.exit(1);
30050
30175
  }
@@ -16769,6 +16769,83 @@ var init_founder_verdict_sync = __esm({
16769
16769
  }
16770
16770
  });
16771
16771
 
16772
+ // bin/founder-note-sync.js
16773
+ var founder_note_sync_exports = {};
16774
+ __export(founder_note_sync_exports, {
16775
+ acknowledgeFounderNotes: () => acknowledgeFounderNotes,
16776
+ computeFounderNotes: () => computeFounderNotes,
16777
+ fetchFounderNotes: () => fetchFounderNotes,
16778
+ formatNote: () => formatNote,
16779
+ noteKey: () => noteKey,
16780
+ syncFounderNotes: () => syncFounderNotes
16781
+ });
16782
+ async function fetchFounderNotes(pushToken, fetchImpl = fetch) {
16783
+ if (typeof pushToken !== "string" || pushToken.length === 0) return null;
16784
+ try {
16785
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE3}/api/claim/notes`, {
16786
+ method: "POST",
16787
+ headers: { "Content-Type": "application/json" },
16788
+ body: JSON.stringify({ pushToken }),
16789
+ signal: AbortSignal.timeout(15e3)
16790
+ });
16791
+ if (!res?.ok) return null;
16792
+ const body = await res.json();
16793
+ if (!body || !Array.isArray(body.notes)) return null;
16794
+ const notes = body.notes.filter(
16795
+ (n) => n && typeof n.claimId === "string" && n.claimId !== "" && typeof n.note === "string" && n.note !== "" && typeof n.at === "string" && (n.kind === "founder_note" || n.kind === "feedback")
16796
+ );
16797
+ return { notes, latestAt: typeof body.latestAt === "string" ? body.latestAt : null };
16798
+ } catch {
16799
+ return null;
16800
+ }
16801
+ }
16802
+ function noteKey(note) {
16803
+ return `${note.claimId}@${note.at}`;
16804
+ }
16805
+ function computeFounderNotes(notes, previous) {
16806
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
16807
+ const prior = previous && Array.isArray(previous.seen) ? new Set(previous.seen) : /* @__PURE__ */ new Set();
16808
+ const seen = keys.filter((k) => prior.has(k));
16809
+ return { count: keys.length - seen.length, seen };
16810
+ }
16811
+ function acknowledgeFounderNotes(notes) {
16812
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
16813
+ return { count: 0, seen: keys };
16814
+ }
16815
+ async function syncFounderNotes({
16816
+ readAutoMarker: readAutoMarker2,
16817
+ readPushTokenEnc: readPushTokenEnc2,
16818
+ readPrevious,
16819
+ fetchImpl = fetch,
16820
+ timeoutMs = 15e3
16821
+ } = {}) {
16822
+ try {
16823
+ const marker = readAutoMarker2();
16824
+ const token = await readPushTokenEnc2();
16825
+ if (!marker || !token) return null;
16826
+ const answer = await fetchFounderNotes(
16827
+ token,
16828
+ (url, init) => fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })
16829
+ );
16830
+ if (!answer) return null;
16831
+ return computeFounderNotes(answer.notes, readPrevious());
16832
+ } catch {
16833
+ return null;
16834
+ }
16835
+ }
16836
+ function formatNote(note) {
16837
+ const label = note.kind === "feedback" ? "changes requested" : "note";
16838
+ return ` ${note.at.slice(0, 16).replace("T", " ")} [${label}] ${note.claimId}
16839
+ ${note.note.split("\n").join("\n ")}`;
16840
+ }
16841
+ var CLAIM_SYNC_BASE3;
16842
+ var init_founder_note_sync = __esm({
16843
+ "bin/founder-note-sync.js"() {
16844
+ "use strict";
16845
+ CLAIM_SYNC_BASE3 = "https://terminalhire.com";
16846
+ }
16847
+ });
16848
+
16772
16849
  // ../../packages/core/src/policy-audit.ts
16773
16850
  function fence(nonce, body) {
16774
16851
  return `<<<UNTRUSTED-${nonce}
@@ -31776,7 +31853,7 @@ async function mintRegistrationProof() {
31776
31853
  console.log(" GitHub identity has to be verified once in the browser.");
31777
31854
  let begin;
31778
31855
  try {
31779
- const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
31856
+ const r = await fetch(`${CLAIM_SYNC_BASE4}/api/claim-sync/begin`, {
31780
31857
  method: "POST",
31781
31858
  headers: { "Content-Type": "application/json" },
31782
31859
  body: JSON.stringify({ hostname: osHostname() }),
@@ -31809,7 +31886,7 @@ async function mintRegistrationProof() {
31809
31886
  let statusRes;
31810
31887
  try {
31811
31888
  statusRes = await fetch(
31812
- `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
31889
+ `${CLAIM_SYNC_BASE4}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
31813
31890
  { signal: AbortSignal.timeout(1e4) }
31814
31891
  );
31815
31892
  } catch {
@@ -31918,7 +31995,7 @@ terminalhire claim: refusing to record \u2014 ${reason}
31918
31995
  };
31919
31996
  if (!auth) await acquireProofAuth();
31920
31997
  console.log("\n Registering this claim with terminalhire (founder posting)...");
31921
- const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE3}/api/claim/register`, {
31998
+ const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE4}/api/claim/register`, {
31922
31999
  method: "POST",
31923
32000
  headers: { "Content-Type": "application/json" },
31924
32001
  body: JSON.stringify({
@@ -32497,7 +32574,7 @@ async function resolveIssueOutcome(c, res, claims) {
32497
32574
  async function fetchFounderApprovals(pushToken, fetchImpl = fetch) {
32498
32575
  if (typeof pushToken !== "string" || pushToken.length === 0) return null;
32499
32576
  try {
32500
- const res = await fetchImpl(`${CLAIM_SYNC_BASE3}/api/claim/approvals`, {
32577
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE4}/api/claim/approvals`, {
32501
32578
  method: "POST",
32502
32579
  headers: { "Content-Type": "application/json" },
32503
32580
  body: JSON.stringify({ pushToken }),
@@ -33336,7 +33413,7 @@ async function cmdSlice(id, flags = {}) {
33336
33413
  const pushToken = await requireReadPushToken("fetching your granted slice");
33337
33414
  let res;
33338
33415
  try {
33339
- res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim/slice`, {
33416
+ res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim/slice`, {
33340
33417
  method: "POST",
33341
33418
  headers: { "Content-Type": "application/json" },
33342
33419
  body: JSON.stringify({
@@ -33444,7 +33521,7 @@ async function cmdRuns(id, flags = {}) {
33444
33521
  const fetchOnce = async () => {
33445
33522
  let res;
33446
33523
  try {
33447
- res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch/runs`, {
33524
+ res = await fetch(`${CLAIM_SYNC_BASE4}/api/patch/runs`, {
33448
33525
  method: "POST",
33449
33526
  headers: { "Content-Type": "application/json" },
33450
33527
  body: JSON.stringify({
@@ -33592,7 +33669,7 @@ async function submitFounderPatch({ claims, claim, id, wt, flags }) {
33592
33669
  console.log("\n Submitting the patch through terminalhire...");
33593
33670
  let res;
33594
33671
  try {
33595
- res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch`, {
33672
+ res = await fetch(`${CLAIM_SYNC_BASE4}/api/patch`, {
33596
33673
  method: "POST",
33597
33674
  headers: { "Content-Type": "application/json" },
33598
33675
  body: JSON.stringify(submission),
@@ -34165,7 +34242,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
34165
34242
  console.log("\n Starting browser verification...");
34166
34243
  let begin;
34167
34244
  try {
34168
- const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
34245
+ const r = await fetch(`${CLAIM_SYNC_BASE4}/api/claim-sync/begin`, {
34169
34246
  method: "POST",
34170
34247
  headers: { "Content-Type": "application/json" },
34171
34248
  body: JSON.stringify({ hostname: osHostname() }),
@@ -34208,7 +34285,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
34208
34285
  let statusRes;
34209
34286
  try {
34210
34287
  statusRes = await fetch(
34211
- `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
34288
+ `${CLAIM_SYNC_BASE4}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
34212
34289
  { signal: AbortSignal.timeout(1e4) }
34213
34290
  );
34214
34291
  } catch {
@@ -34239,7 +34316,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
34239
34316
  console.log("\n Verified. Sharing your claims...");
34240
34317
  let res;
34241
34318
  try {
34242
- res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
34319
+ res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim-sync`, {
34243
34320
  method: "POST",
34244
34321
  headers: { "Content-Type": "application/json" },
34245
34322
  // autoConsent is included ONLY when the dev opted into background updates —
@@ -34366,7 +34443,7 @@ async function cmdRevoke() {
34366
34443
  console.log("\n Requesting deletion...");
34367
34444
  let res;
34368
34445
  try {
34369
- res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
34446
+ res = await fetch(`${CLAIM_SYNC_BASE4}/api/claim-sync`, {
34370
34447
  method: "DELETE",
34371
34448
  headers: { "Content-Type": "application/json" },
34372
34449
  body: JSON.stringify({ login, deleteToken }),
@@ -34543,6 +34620,50 @@ async function cmdAudit(id, flags = {}) {
34543
34620
  }
34544
34621
  renderAudit(view);
34545
34622
  }
34623
+ async function cmdNotes(flags) {
34624
+ let pushToken = null;
34625
+ try {
34626
+ pushToken = await readPushTokenEnc();
34627
+ } catch {
34628
+ }
34629
+ if (!pushToken) {
34630
+ console.error("terminalhire claim notes: needs background push enrolment.");
34631
+ console.error(" Enrol once: terminalhire claim --push --keep-updated");
34632
+ process.exit(1);
34633
+ }
34634
+ const answer = await fetchFounderNotes(pushToken);
34635
+ if (answer === null) {
34636
+ console.error("terminalhire claim notes: could not reach terminalhire just now.");
34637
+ console.error(' This is NOT "no notes" \u2014 try again shortly.');
34638
+ process.exit(1);
34639
+ }
34640
+ if (flags.json) {
34641
+ console.log(JSON.stringify(answer, null, 2));
34642
+ await acknowledgeNotesQuietly(answer.notes);
34643
+ return;
34644
+ }
34645
+ if (answer.notes.length === 0) {
34646
+ console.log("\n No notes from founders on your claims.\n");
34647
+ await acknowledgeNotesQuietly(answer.notes);
34648
+ return;
34649
+ }
34650
+ console.log(
34651
+ `
34652
+ ${answer.notes.length} note${answer.notes.length === 1 ? "" : "s"} from founders:
34653
+ `
34654
+ );
34655
+ for (const note of answer.notes) console.log(formatNote(note));
34656
+ console.log("");
34657
+ await acknowledgeNotesQuietly(answer.notes);
34658
+ }
34659
+ async function acknowledgeNotesQuietly(notes) {
34660
+ try {
34661
+ const { acknowledgeFounderNotes: acknowledgeFounderNotes2 } = await Promise.resolve().then(() => (init_founder_note_sync(), founder_note_sync_exports));
34662
+ const { updateIndexCache: updateIndexCache2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
34663
+ updateIndexCache2({ founderNotes: acknowledgeFounderNotes2(notes) });
34664
+ } catch {
34665
+ }
34666
+ }
34546
34667
  async function run7() {
34547
34668
  const verb = process.argv[2];
34548
34669
  const rawArgs = process.argv.slice(3).map((a) => a === "-y" ? "--yes" : a);
@@ -34605,9 +34726,12 @@ async function run7() {
34605
34726
  case "audit":
34606
34727
  await cmdAudit(positional[0], flags);
34607
34728
  break;
34729
+ case "notes":
34730
+ await cmdNotes(flags);
34731
+ break;
34608
34732
  default:
34609
34733
  console.error(
34610
- `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | audit | release`
34734
+ `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | notes | audit | release`
34611
34735
  );
34612
34736
  process.exit(1);
34613
34737
  }
@@ -34616,7 +34740,7 @@ async function run7() {
34616
34740
  process.exit(1);
34617
34741
  }
34618
34742
  }
34619
- var TERMINALHIRE_DIR17, INDEX_CACHE_FILE5, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL6, CLAIM_SYNC_BASE3, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API3, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, REVISE_RECOVERY_STATES, PUSH_TOKEN_REFUSAL, SYNC_BACKGROUND_PUSH_ACTIVE_FIELD, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS, CLAIM_EVENT_LABEL, LINE_BREAKS, CONTROL_CHARS2;
34743
+ var TERMINALHIRE_DIR17, INDEX_CACHE_FILE5, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL6, CLAIM_SYNC_BASE4, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API3, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, REVISE_RECOVERY_STATES, PUSH_TOKEN_REFUSAL, SYNC_BACKGROUND_PUSH_ACTIVE_FIELD, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS, CLAIM_EVENT_LABEL, LINE_BREAKS, CONTROL_CHARS2;
34620
34744
  var init_jpi_claim = __esm({
34621
34745
  "bin/jpi-claim.js"() {
34622
34746
  "use strict";
@@ -34628,12 +34752,13 @@ var init_jpi_claim = __esm({
34628
34752
  init_state_dir();
34629
34753
  init_claim_push_bg();
34630
34754
  init_founder_verdict_sync();
34755
+ init_founder_note_sync();
34631
34756
  TERMINALHIRE_DIR17 = process.env.TERMINALHIRE_DIR || join30(homedir22(), ".terminalhire");
34632
34757
  INDEX_CACHE_FILE5 = join30(TERMINALHIRE_DIR17, "index-cache.json");
34633
34758
  CLAIM_PUSH_MARKER = join30(TERMINALHIRE_DIR17, "claim-push.json");
34634
34759
  REPO_CONTINUITY_NUDGE_MARKER = join30(TERMINALHIRE_DIR17, "repo-continuity-nudged.json");
34635
34760
  API_URL6 = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
34636
- CLAIM_SYNC_BASE3 = "https://terminalhire.com";
34761
+ CLAIM_SYNC_BASE4 = "https://terminalhire.com";
34637
34762
  CLAIM_CONSENT_VERSION = 1;
34638
34763
  CLAIM_POLL_INTERVAL_MS = 2e3;
34639
34764
  CLAIM_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -65526,7 +65651,7 @@ async function syncApprovedClaims({
65526
65651
  if (!approvalsSyncGate({ autoMarkerExists: !!marker, tokenFileExists: !!token }).sync) {
65527
65652
  return null;
65528
65653
  }
65529
- const res = await fetchImpl(`${CLAIM_SYNC_BASE4}/api/claim/approvals`, {
65654
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE5}/api/claim/approvals`, {
65530
65655
  method: "POST",
65531
65656
  headers: { "Content-Type": "application/json" },
65532
65657
  body: JSON.stringify({ pushToken: token }),
@@ -65541,11 +65666,11 @@ async function syncApprovedClaims({
65541
65666
  return null;
65542
65667
  }
65543
65668
  }
65544
- var CLAIM_SYNC_BASE4;
65669
+ var CLAIM_SYNC_BASE5;
65545
65670
  var init_approved_claims_sync = __esm({
65546
65671
  "bin/approved-claims-sync.js"() {
65547
65672
  "use strict";
65548
- CLAIM_SYNC_BASE4 = "https://terminalhire.com";
65673
+ CLAIM_SYNC_BASE5 = "https://terminalhire.com";
65549
65674
  }
65550
65675
  });
65551
65676
 
@@ -65906,6 +66031,18 @@ async function run28() {
65906
66031
  });
65907
66032
  } catch {
65908
66033
  }
66034
+ let founderNotes;
66035
+ try {
66036
+ const { syncFounderNotes: syncFounderNotes2 } = await Promise.resolve().then(() => (init_founder_note_sync(), founder_note_sync_exports));
66037
+ const { readAutoMarker: readAutoMarker2, readPushTokenEnc: readPushTokenEnc2 } = await Promise.resolve().then(() => (init_claim_push_bg(), claim_push_bg_exports));
66038
+ const { readCacheEntry: readCacheEntry2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
66039
+ founderNotes = await syncFounderNotes2({
66040
+ readAutoMarker: readAutoMarker2,
66041
+ readPushTokenEnc: readPushTokenEnc2,
66042
+ readPrevious: () => (readCacheEntry2() ?? {}).founderNotes
66043
+ });
66044
+ } catch {
66045
+ }
65909
66046
  const previousCache = readCacheEntry() ?? {};
65910
66047
  const effectiveFounderSurface = founderSurface ?? previousCache.founderSurface;
65911
66048
  let surfaceLead = "dev";
@@ -65927,6 +66064,7 @@ async function run28() {
65927
66064
  };
65928
66065
  if (founderPaid) cacheEntry.founderPaid = founderPaid;
65929
66066
  if (approvedClaims) cacheEntry.approvedClaims = approvedClaims;
66067
+ if (founderNotes) cacheEntry.founderNotes = founderNotes;
65930
66068
  if (founderSurface) cacheEntry.founderSurface = founderSurface;
65931
66069
  cacheEntry.indexETag = indexETag ?? "";
65932
66070
  if (pulseLatestPostedAt) {
@@ -11530,6 +11530,83 @@ var init_founder_verdict_sync = __esm({
11530
11530
  }
11531
11531
  });
11532
11532
 
11533
+ // bin/founder-note-sync.js
11534
+ var founder_note_sync_exports = {};
11535
+ __export(founder_note_sync_exports, {
11536
+ acknowledgeFounderNotes: () => acknowledgeFounderNotes,
11537
+ computeFounderNotes: () => computeFounderNotes,
11538
+ fetchFounderNotes: () => fetchFounderNotes,
11539
+ formatNote: () => formatNote,
11540
+ noteKey: () => noteKey,
11541
+ syncFounderNotes: () => syncFounderNotes
11542
+ });
11543
+ async function fetchFounderNotes(pushToken, fetchImpl = fetch) {
11544
+ if (typeof pushToken !== "string" || pushToken.length === 0) return null;
11545
+ try {
11546
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE2}/api/claim/notes`, {
11547
+ method: "POST",
11548
+ headers: { "Content-Type": "application/json" },
11549
+ body: JSON.stringify({ pushToken }),
11550
+ signal: AbortSignal.timeout(15e3)
11551
+ });
11552
+ if (!res?.ok) return null;
11553
+ const body = await res.json();
11554
+ if (!body || !Array.isArray(body.notes)) return null;
11555
+ const notes = body.notes.filter(
11556
+ (n) => n && typeof n.claimId === "string" && n.claimId !== "" && typeof n.note === "string" && n.note !== "" && typeof n.at === "string" && (n.kind === "founder_note" || n.kind === "feedback")
11557
+ );
11558
+ return { notes, latestAt: typeof body.latestAt === "string" ? body.latestAt : null };
11559
+ } catch {
11560
+ return null;
11561
+ }
11562
+ }
11563
+ function noteKey(note) {
11564
+ return `${note.claimId}@${note.at}`;
11565
+ }
11566
+ function computeFounderNotes(notes, previous) {
11567
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
11568
+ const prior = previous && Array.isArray(previous.seen) ? new Set(previous.seen) : /* @__PURE__ */ new Set();
11569
+ const seen = keys.filter((k) => prior.has(k));
11570
+ return { count: keys.length - seen.length, seen };
11571
+ }
11572
+ function acknowledgeFounderNotes(notes) {
11573
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
11574
+ return { count: 0, seen: keys };
11575
+ }
11576
+ async function syncFounderNotes({
11577
+ readAutoMarker: readAutoMarker2,
11578
+ readPushTokenEnc: readPushTokenEnc2,
11579
+ readPrevious,
11580
+ fetchImpl = fetch,
11581
+ timeoutMs = 15e3
11582
+ } = {}) {
11583
+ try {
11584
+ const marker = readAutoMarker2();
11585
+ const token = await readPushTokenEnc2();
11586
+ if (!marker || !token) return null;
11587
+ const answer = await fetchFounderNotes(
11588
+ token,
11589
+ (url, init) => fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })
11590
+ );
11591
+ if (!answer) return null;
11592
+ return computeFounderNotes(answer.notes, readPrevious());
11593
+ } catch {
11594
+ return null;
11595
+ }
11596
+ }
11597
+ function formatNote(note) {
11598
+ const label = note.kind === "feedback" ? "changes requested" : "note";
11599
+ return ` ${note.at.slice(0, 16).replace("T", " ")} [${label}] ${note.claimId}
11600
+ ${note.note.split("\n").join("\n ")}`;
11601
+ }
11602
+ var CLAIM_SYNC_BASE2;
11603
+ var init_founder_note_sync = __esm({
11604
+ "bin/founder-note-sync.js"() {
11605
+ "use strict";
11606
+ CLAIM_SYNC_BASE2 = "https://terminalhire.com";
11607
+ }
11608
+ });
11609
+
11533
11610
  // ../../packages/core/src/policy-audit.ts
11534
11611
  function fence(nonce, body) {
11535
11612
  return `<<<UNTRUSTED-${nonce}
@@ -27254,7 +27331,7 @@ async function mintRegistrationProof() {
27254
27331
  console.log(" GitHub identity has to be verified once in the browser.");
27255
27332
  let begin;
27256
27333
  try {
27257
- const r = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync/begin`, {
27334
+ const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
27258
27335
  method: "POST",
27259
27336
  headers: { "Content-Type": "application/json" },
27260
27337
  body: JSON.stringify({ hostname: osHostname() }),
@@ -27287,7 +27364,7 @@ async function mintRegistrationProof() {
27287
27364
  let statusRes;
27288
27365
  try {
27289
27366
  statusRes = await fetch(
27290
- `${CLAIM_SYNC_BASE2}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
27367
+ `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
27291
27368
  { signal: AbortSignal.timeout(1e4) }
27292
27369
  );
27293
27370
  } catch {
@@ -27396,7 +27473,7 @@ terminalhire claim: refusing to record \u2014 ${reason}
27396
27473
  };
27397
27474
  if (!auth) await acquireProofAuth();
27398
27475
  console.log("\n Registering this claim with terminalhire (founder posting)...");
27399
- const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE2}/api/claim/register`, {
27476
+ const sendRegistration = async (includeExpectation) => fetch(`${CLAIM_SYNC_BASE3}/api/claim/register`, {
27400
27477
  method: "POST",
27401
27478
  headers: { "Content-Type": "application/json" },
27402
27479
  body: JSON.stringify({
@@ -27975,7 +28052,7 @@ async function resolveIssueOutcome(c, res, claims) {
27975
28052
  async function fetchFounderApprovals(pushToken, fetchImpl = fetch) {
27976
28053
  if (typeof pushToken !== "string" || pushToken.length === 0) return null;
27977
28054
  try {
27978
- const res = await fetchImpl(`${CLAIM_SYNC_BASE2}/api/claim/approvals`, {
28055
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE3}/api/claim/approvals`, {
27979
28056
  method: "POST",
27980
28057
  headers: { "Content-Type": "application/json" },
27981
28058
  body: JSON.stringify({ pushToken }),
@@ -28814,7 +28891,7 @@ async function cmdSlice(id, flags = {}) {
28814
28891
  const pushToken = await requireReadPushToken("fetching your granted slice");
28815
28892
  let res;
28816
28893
  try {
28817
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim/slice`, {
28894
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim/slice`, {
28818
28895
  method: "POST",
28819
28896
  headers: { "Content-Type": "application/json" },
28820
28897
  body: JSON.stringify({
@@ -28922,7 +28999,7 @@ async function cmdRuns(id, flags = {}) {
28922
28999
  const fetchOnce = async () => {
28923
29000
  let res;
28924
29001
  try {
28925
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/patch/runs`, {
29002
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch/runs`, {
28926
29003
  method: "POST",
28927
29004
  headers: { "Content-Type": "application/json" },
28928
29005
  body: JSON.stringify({
@@ -29070,7 +29147,7 @@ async function submitFounderPatch({ claims, claim, id, wt, flags }) {
29070
29147
  console.log("\n Submitting the patch through terminalhire...");
29071
29148
  let res;
29072
29149
  try {
29073
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/patch`, {
29150
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/patch`, {
29074
29151
  method: "POST",
29075
29152
  headers: { "Content-Type": "application/json" },
29076
29153
  body: JSON.stringify(submission),
@@ -29643,7 +29720,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29643
29720
  console.log("\n Starting browser verification...");
29644
29721
  let begin;
29645
29722
  try {
29646
- const r = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync/begin`, {
29723
+ const r = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync/begin`, {
29647
29724
  method: "POST",
29648
29725
  headers: { "Content-Type": "application/json" },
29649
29726
  body: JSON.stringify({ hostname: osHostname() }),
@@ -29686,7 +29763,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29686
29763
  let statusRes;
29687
29764
  try {
29688
29765
  statusRes = await fetch(
29689
- `${CLAIM_SYNC_BASE2}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
29766
+ `${CLAIM_SYNC_BASE3}/api/claim-sync/status?challenge=${encodeURIComponent(challenge)}`,
29690
29767
  { signal: AbortSignal.timeout(1e4) }
29691
29768
  );
29692
29769
  } catch {
@@ -29717,7 +29794,7 @@ async function cmdPush({ keepUpdated = false } = {}) {
29717
29794
  console.log("\n Verified. Sharing your claims...");
29718
29795
  let res;
29719
29796
  try {
29720
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync`, {
29797
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
29721
29798
  method: "POST",
29722
29799
  headers: { "Content-Type": "application/json" },
29723
29800
  // autoConsent is included ONLY when the dev opted into background updates —
@@ -29844,7 +29921,7 @@ async function cmdRevoke() {
29844
29921
  console.log("\n Requesting deletion...");
29845
29922
  let res;
29846
29923
  try {
29847
- res = await fetch(`${CLAIM_SYNC_BASE2}/api/claim-sync`, {
29924
+ res = await fetch(`${CLAIM_SYNC_BASE3}/api/claim-sync`, {
29848
29925
  method: "DELETE",
29849
29926
  headers: { "Content-Type": "application/json" },
29850
29927
  body: JSON.stringify({ login, deleteToken }),
@@ -30021,6 +30098,50 @@ async function cmdAudit(id, flags = {}) {
30021
30098
  }
30022
30099
  renderAudit(view);
30023
30100
  }
30101
+ async function cmdNotes(flags) {
30102
+ let pushToken = null;
30103
+ try {
30104
+ pushToken = await readPushTokenEnc();
30105
+ } catch {
30106
+ }
30107
+ if (!pushToken) {
30108
+ console.error("terminalhire claim notes: needs background push enrolment.");
30109
+ console.error(" Enrol once: terminalhire claim --push --keep-updated");
30110
+ process.exit(1);
30111
+ }
30112
+ const answer = await fetchFounderNotes(pushToken);
30113
+ if (answer === null) {
30114
+ console.error("terminalhire claim notes: could not reach terminalhire just now.");
30115
+ console.error(' This is NOT "no notes" \u2014 try again shortly.');
30116
+ process.exit(1);
30117
+ }
30118
+ if (flags.json) {
30119
+ console.log(JSON.stringify(answer, null, 2));
30120
+ await acknowledgeNotesQuietly(answer.notes);
30121
+ return;
30122
+ }
30123
+ if (answer.notes.length === 0) {
30124
+ console.log("\n No notes from founders on your claims.\n");
30125
+ await acknowledgeNotesQuietly(answer.notes);
30126
+ return;
30127
+ }
30128
+ console.log(
30129
+ `
30130
+ ${answer.notes.length} note${answer.notes.length === 1 ? "" : "s"} from founders:
30131
+ `
30132
+ );
30133
+ for (const note of answer.notes) console.log(formatNote(note));
30134
+ console.log("");
30135
+ await acknowledgeNotesQuietly(answer.notes);
30136
+ }
30137
+ async function acknowledgeNotesQuietly(notes) {
30138
+ try {
30139
+ const { acknowledgeFounderNotes: acknowledgeFounderNotes2 } = await Promise.resolve().then(() => (init_founder_note_sync(), founder_note_sync_exports));
30140
+ const { updateIndexCache: updateIndexCache2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
30141
+ updateIndexCache2({ founderNotes: acknowledgeFounderNotes2(notes) });
30142
+ } catch {
30143
+ }
30144
+ }
30024
30145
  async function run() {
30025
30146
  const verb = process.argv[2];
30026
30147
  const rawArgs = process.argv.slice(3).map((a) => a === "-y" ? "--yes" : a);
@@ -30083,9 +30204,12 @@ async function run() {
30083
30204
  case "audit":
30084
30205
  await cmdAudit(positional[0], flags);
30085
30206
  break;
30207
+ case "notes":
30208
+ await cmdNotes(flags);
30209
+ break;
30086
30210
  default:
30087
30211
  console.error(
30088
- `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | audit | release`
30212
+ `terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | slice | list | status | update | submit | runs | notes | audit | release`
30089
30213
  );
30090
30214
  process.exit(1);
30091
30215
  }
@@ -30094,7 +30218,7 @@ async function run() {
30094
30218
  process.exit(1);
30095
30219
  }
30096
30220
  }
30097
- var TERMINALHIRE_DIR11, INDEX_CACHE_FILE2, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL, CLAIM_SYNC_BASE2, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API2, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, REVISE_RECOVERY_STATES, PUSH_TOKEN_REFUSAL, SYNC_BACKGROUND_PUSH_ACTIVE_FIELD, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS, CLAIM_EVENT_LABEL, LINE_BREAKS, CONTROL_CHARS2;
30221
+ var TERMINALHIRE_DIR11, INDEX_CACHE_FILE2, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL, CLAIM_SYNC_BASE3, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API2, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, REVISE_RECOVERY_STATES, PUSH_TOKEN_REFUSAL, SYNC_BACKGROUND_PUSH_ACTIVE_FIELD, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS, CLAIM_EVENT_LABEL, LINE_BREAKS, CONTROL_CHARS2;
30098
30222
  var init_jpi_claim = __esm({
30099
30223
  "bin/jpi-claim.js"() {
30100
30224
  "use strict";
@@ -30106,12 +30230,13 @@ var init_jpi_claim = __esm({
30106
30230
  init_state_dir();
30107
30231
  init_claim_push_bg();
30108
30232
  init_founder_verdict_sync();
30233
+ init_founder_note_sync();
30109
30234
  TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR || join18(homedir12(), ".terminalhire");
30110
30235
  INDEX_CACHE_FILE2 = join18(TERMINALHIRE_DIR11, "index-cache.json");
30111
30236
  CLAIM_PUSH_MARKER = join18(TERMINALHIRE_DIR11, "claim-push.json");
30112
30237
  REPO_CONTINUITY_NUDGE_MARKER = join18(TERMINALHIRE_DIR11, "repo-continuity-nudged.json");
30113
30238
  API_URL = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
30114
- CLAIM_SYNC_BASE2 = "https://terminalhire.com";
30239
+ CLAIM_SYNC_BASE3 = "https://terminalhire.com";
30115
30240
  CLAIM_CONSENT_VERSION = 1;
30116
30241
  CLAIM_POLL_INTERVAL_MS = 2e3;
30117
30242
  CLAIM_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
@@ -13072,6 +13072,83 @@ var init_approved_claims_badge = __esm({
13072
13072
  }
13073
13073
  });
13074
13074
 
13075
+ // bin/founder-note-sync.js
13076
+ var founder_note_sync_exports = {};
13077
+ __export(founder_note_sync_exports, {
13078
+ acknowledgeFounderNotes: () => acknowledgeFounderNotes,
13079
+ computeFounderNotes: () => computeFounderNotes,
13080
+ fetchFounderNotes: () => fetchFounderNotes,
13081
+ formatNote: () => formatNote,
13082
+ noteKey: () => noteKey,
13083
+ syncFounderNotes: () => syncFounderNotes
13084
+ });
13085
+ async function fetchFounderNotes(pushToken, fetchImpl = fetch) {
13086
+ if (typeof pushToken !== "string" || pushToken.length === 0) return null;
13087
+ try {
13088
+ const res = await fetchImpl(`${CLAIM_SYNC_BASE3}/api/claim/notes`, {
13089
+ method: "POST",
13090
+ headers: { "Content-Type": "application/json" },
13091
+ body: JSON.stringify({ pushToken }),
13092
+ signal: AbortSignal.timeout(15e3)
13093
+ });
13094
+ if (!res?.ok) return null;
13095
+ const body = await res.json();
13096
+ if (!body || !Array.isArray(body.notes)) return null;
13097
+ const notes = body.notes.filter(
13098
+ (n) => n && typeof n.claimId === "string" && n.claimId !== "" && typeof n.note === "string" && n.note !== "" && typeof n.at === "string" && (n.kind === "founder_note" || n.kind === "feedback")
13099
+ );
13100
+ return { notes, latestAt: typeof body.latestAt === "string" ? body.latestAt : null };
13101
+ } catch {
13102
+ return null;
13103
+ }
13104
+ }
13105
+ function noteKey(note) {
13106
+ return `${note.claimId}@${note.at}`;
13107
+ }
13108
+ function computeFounderNotes(notes, previous) {
13109
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
13110
+ const prior = previous && Array.isArray(previous.seen) ? new Set(previous.seen) : /* @__PURE__ */ new Set();
13111
+ const seen = keys.filter((k) => prior.has(k));
13112
+ return { count: keys.length - seen.length, seen };
13113
+ }
13114
+ function acknowledgeFounderNotes(notes) {
13115
+ const keys = Array.isArray(notes) ? notes.map(noteKey) : [];
13116
+ return { count: 0, seen: keys };
13117
+ }
13118
+ async function syncFounderNotes({
13119
+ readAutoMarker: readAutoMarker2,
13120
+ readPushTokenEnc: readPushTokenEnc2,
13121
+ readPrevious,
13122
+ fetchImpl = fetch,
13123
+ timeoutMs = 15e3
13124
+ } = {}) {
13125
+ try {
13126
+ const marker = readAutoMarker2();
13127
+ const token = await readPushTokenEnc2();
13128
+ if (!marker || !token) return null;
13129
+ const answer = await fetchFounderNotes(
13130
+ token,
13131
+ (url, init) => fetchImpl(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })
13132
+ );
13133
+ if (!answer) return null;
13134
+ return computeFounderNotes(answer.notes, readPrevious());
13135
+ } catch {
13136
+ return null;
13137
+ }
13138
+ }
13139
+ function formatNote(note) {
13140
+ const label = note.kind === "feedback" ? "changes requested" : "note";
13141
+ return ` ${note.at.slice(0, 16).replace("T", " ")} [${label}] ${note.claimId}
13142
+ ${note.note.split("\n").join("\n ")}`;
13143
+ }
13144
+ var CLAIM_SYNC_BASE3;
13145
+ var init_founder_note_sync = __esm({
13146
+ "bin/founder-note-sync.js"() {
13147
+ "use strict";
13148
+ CLAIM_SYNC_BASE3 = "https://terminalhire.com";
13149
+ }
13150
+ });
13151
+
13075
13152
  // bin/version-nudge.js
13076
13153
  var version_nudge_exports = {};
13077
13154
  __export(version_nudge_exports, {
@@ -13866,6 +13943,18 @@ async function run() {
13866
13943
  });
13867
13944
  } catch {
13868
13945
  }
13946
+ let founderNotes;
13947
+ try {
13948
+ const { syncFounderNotes: syncFounderNotes2 } = await Promise.resolve().then(() => (init_founder_note_sync(), founder_note_sync_exports));
13949
+ const { readAutoMarker: readAutoMarker2, readPushTokenEnc: readPushTokenEnc2 } = await Promise.resolve().then(() => (init_claim_push_bg(), claim_push_bg_exports));
13950
+ const { readCacheEntry: readCacheEntry2 } = await Promise.resolve().then(() => (init_cache_store(), cache_store_exports));
13951
+ founderNotes = await syncFounderNotes2({
13952
+ readAutoMarker: readAutoMarker2,
13953
+ readPushTokenEnc: readPushTokenEnc2,
13954
+ readPrevious: () => (readCacheEntry2() ?? {}).founderNotes
13955
+ });
13956
+ } catch {
13957
+ }
13869
13958
  const previousCache = readCacheEntry() ?? {};
13870
13959
  const effectiveFounderSurface = founderSurface ?? previousCache.founderSurface;
13871
13960
  let surfaceLead = "dev";
@@ -13887,6 +13976,7 @@ async function run() {
13887
13976
  };
13888
13977
  if (founderPaid) cacheEntry.founderPaid = founderPaid;
13889
13978
  if (approvedClaims) cacheEntry.approvedClaims = approvedClaims;
13979
+ if (founderNotes) cacheEntry.founderNotes = founderNotes;
13890
13980
  if (founderSurface) cacheEntry.founderSurface = founderSurface;
13891
13981
  cacheEntry.indexETag = indexETag ?? "";
13892
13982
  if (pulseLatestPostedAt) {
@@ -38,6 +38,10 @@ function approvedClaimsCount(entry) {
38
38
  const n = entry && entry.approvedClaims && entry.approvedClaims.count;
39
39
  return typeof n === "number" && n > 0 ? n : 0;
40
40
  }
41
+ function founderNoteCount(entry) {
42
+ const n = entry && entry.founderNotes && entry.founderNotes.count;
43
+ return typeof n === "number" && n > 0 ? n : 0;
44
+ }
41
45
  function founderNeedsYouCount(entry) {
42
46
  if (!entry || entry.surfaceLead !== "founder") return 0;
43
47
  const n = entry.founderSurface && entry.founderSurface.needsYouCount;
@@ -88,11 +92,15 @@ function render() {
88
92
  const incoming = incomingCount(entry);
89
93
  const paid = founderPaidCount(entry);
90
94
  const approved = approvedClaimsCount(entry);
95
+ const founderNotes = founderNoteCount(entry);
91
96
  const founderNeedsYou = founderNeedsYouCount(entry);
92
97
  const founderOpen = founderOpenCount(entry);
93
98
  const stale = sessionStale(entry) && unread === 0 && incoming === 0;
94
99
  const segments = [];
95
100
  if (approved > 0) segments.push(`\u2705 ${approved} approved \u2014 run: th claim start`);
101
+ if (founderNotes > 0) {
102
+ segments.push(`\u{1F4DD} ${founderNotes} from your founder \u2014 run: th claim notes`);
103
+ }
96
104
  if (founderNeedsYou > 0) {
97
105
  segments.push(
98
106
  `\u{1F9ED} ${founderNeedsYou} claim${founderNeedsYou === 1 ? "" : "s"} await your decision \u2014 run: th bounties`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.41.1",
3
+ "version": "0.42.0",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Your profile never leaves your machine.",
5
5
  "repository": {
6
6
  "type": "git",