[object Object]

← back to Ticket System

snapshot before TK-11443 parser fix

ededd12be037067dcd8bd84d334e31c6f55e8a34 · 2026-09-10 17:39:27 -0700 · Steve Abrams

Files touched

Diff

commit ededd12be037067dcd8bd84d334e31c6f55e8a34
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 17:39:27 2026 -0700

    snapshot before TK-11443 parser fix
---
 .../tk11175-api-liveness-probe.mjs                 | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/data/claude-run-11175/evidence/TK-11175-20260910T143730Z/tk11175-api-liveness-probe.mjs b/data/claude-run-11175/evidence/TK-11175-20260910T143730Z/tk11175-api-liveness-probe.mjs
new file mode 100644
index 00000000..a168cd63
--- /dev/null
+++ b/data/claude-run-11175/evidence/TK-11175-20260910T143730Z/tk11175-api-liveness-probe.mjs
@@ -0,0 +1,41 @@
+// TK-11175 READ-ONLY liveness probe. GET-only. No writes, no spend, no mutation.
+// Answers: is Content API for Shopping v2.1 still serving on 2026-09-10, and is Merchant API v1 serving?
+import fs from 'fs'; import crypto from 'crypto';
+const HOME = process.env.HOME;
+const SA_PATH = HOME + '/Projects/secrets-manager/gmc-sa-146735262.json';
+const MERCHANT = '146735262';
+const b64 = b => Buffer.from(b).toString('base64').replace(/=/g,'').replace(/\+/g,'-').replace(/\//g,'_');
+async function mcToken(){
+  const SA = JSON.parse(fs.readFileSync(SA_PATH,'utf8'));
+  const now = Math.floor(Date.now()/1000);
+  const si = b64(JSON.stringify({alg:'RS256',typ:'JWT'}))+'.'+b64(JSON.stringify({iss:SA.client_email,scope:'https://www.googleapis.com/auth/content',aud:'https://oauth2.googleapis.com/token',iat:now,exp:now+3600}));
+  const s = crypto.createSign('RSA-SHA256'); s.update(si); const jwt = si+'.'+b64(s.sign(SA.private_key));
+  const r = await fetch('https://oauth2.googleapis.com/token',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer',assertion:jwt})});
+  const j = await r.json(); if(!j.access_token) throw new Error('token fail: '+JSON.stringify(j).slice(0,200)); return j.access_token;
+}
+const t = await mcToken(); const H = { Authorization: 'Bearer '+t };
+const probes = [
+  ['CONTENT_v2.1  productstatuses', `https://shoppingcontent.googleapis.com/content/v2.1/${MERCHANT}/productstatuses?maxResults=1`],
+  ['CONTENT_v2.1  products',        `https://shoppingcontent.googleapis.com/content/v2.1/${MERCHANT}/products?maxResults=1`],
+  ['MERCHANT_v1   products',        `https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/products?pageSize=1`],
+  ['MERCHANT_v1   accounts/issues', `https://merchantapi.googleapis.com/accounts/v1/accounts/${MERCHANT}/issues`],
+];
+const out = [];
+for (const [name,url] of probes){
+  try {
+    const r = await fetch(url,{headers:H});
+    const body = await r.text();
+    let shape = 'empty';
+    try { const j = JSON.parse(body);
+      if (j.error) shape = 'ERROR: '+(j.error.status||'')+' '+String(j.error.message||'').slice(0,110);
+      else if (j.resources) shape = `OK resources=${j.resources.length}`;
+      else if (j.productStatuses) shape = `OK productStatuses=${j.productStatuses.length}`;
+      else if (j.products) shape = `OK products=${j.products.length}`;
+      else if (j.accountIssues) shape = `OK accountIssues=${(j.accountIssues||[]).length}`;
+      else shape = 'OK keys=['+Object.keys(j).join(',')+']';
+    } catch { shape = 'non-JSON len='+body.length; }
+    out.push({name, http:r.status, shape});
+  } catch(e){ out.push({name, http:'NETERR', shape:String(e.message).slice(0,110)}); }
+}
+console.log('probe_at=' + new Date().toISOString());
+for (const o of out) console.log(String(o.http).padEnd(7), o.name.padEnd(32), o.shape);

← 79f4475a auto-data-snapshot: 2026-09-10T17:34:11 (1 data files) — dat  ·  back to Ticket System  ·  governance(TK-11370): make live-by-default Shopify writers d 00df756a →