Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/thatstupidcomputer / scripts/discovery.ts |
| Date | Not specified in source |
| Repository | Janady13/thatstupidcomputer |
| Revision | 57555cde46910102dd25d6c1c1fb2c359e9655bd |
| Branch | main |
| Path or archive member | scripts/discovery.ts |
| Original SHA-256 | 013895449aaa03d7587c0e009e6654fdcbca9f4e63099ddb15f44d552e684a06 |
| Git blob SHA | b54d365ac6486e4728782aa767292f89b3ff1a86 |
| Reading-copy SHA-256 | 013895449aaa03d7587c0e009e6654fdcbca9f4e63099ddb15f44d552e684a06 |
Recovered source transcript
Download complete source text ↓75 lines · 7.3 KB
import {readFileSync} from 'node:fs';
import {fileURLToPath} from 'node:url';
import {parseEnv} from 'node:util';
import {createHash} from 'node:crypto';
import {BingWebmaster} from '../packages/discovery/providers/bing/webmaster.ts';
import {collectBing} from '../packages/discovery/providers/bing/collect.ts';
import {SITE_ORIGIN,ROUTES} from '../packages/site/content.ts';
import {object,webUrl} from '../packages/discovery/http.ts';
import {evidenceDirectory,saveEvidence} from '../packages/discovery/evidence.ts';
import {SearchConsole} from '../packages/discovery/providers/google/search-console.ts';
import {searchConsoleToken} from '../packages/discovery/providers/google/auth.ts';
import {PROVIDERS,type ProviderId} from '../packages/discovery/registry.ts';
import {answerCatalog} from '../packages/discovery/answers.ts';
import {observeAnswer} from '../packages/discovery/observe.ts';
async function main() {
const mode=process.argv[2];
if(mode==='--help'||!mode) {
console.log('discovery catalog | bing | google START_DATE END_DATE | answer PROVIDER MODEL\nPrivate reports only. Set TSC_DISCOVERY_ROOT when using the bundled CLI. Bing: TSC_DISCOVERY_NODE + TSC_DISCOVERY_NODE_SHA256. Credentials: TSC_DISCOVERY_ENV or provider environment key; Google Search Console: TSC_GSC_SERVICE_ACCOUNT. Each answer command makes one bounded, billable native API request. No consumer-UI results are inferred.');return;
}
if(!['catalog','bing','google','answer'].includes(mode))throw new Error('Unknown discovery command');
const directory=evidenceDirectory(process.env.TSC_DISCOVERY_ROOT??fileURLToPath(new URL('..',import.meta.url)));
const environment=process.env.TSC_DISCOVERY_ENV?parseEnv(readFileSync(process.env.TSC_DISCOVERY_ENV,'utf8')):{};
if(mode==='catalog') {
const startedAt=new Date().toISOString();
const file=saveEvidence(directory,'catalog',startedAt,{schema:'tsc-answer-catalog/v1',startedAt,providers:(Object.keys(PROVIDERS) as ProviderId[]).map(answerCatalog)});
console.log(JSON.stringify({file,providers:Object.keys(PROVIDERS)}));return;
}
if(mode==='answer') {
const provider=process.argv[3] as ProviderId,model=process.argv[4]??'';
if(!Object.hasOwn(PROVIDERS,provider))throw new Error('Unknown provider');
const keyNames:Record<ProviderId,string>={bing:'BING_KEY',google:'GEMINI_API_KEY',openai:'OPENAI_API_KEY',anthropic:'ANTHROPIC_API_KEY',perplexity:'PERPLEXITY_API_KEY',xai:'XAI_API_KEY',mistral:'MISTRAL_API_KEY',deepseek:'DEEPSEEK_API_KEY'};
const keyName=keyNames[provider];
const query='Search the web in English: What website repair and search-visibility services does That Stupid Computer offer? Cite the current source pages. If reliable information is unavailable, say so. Do not infer prices or guaranteed results.';
const report=await observeAnswer(provider,{model,query,key:process.env[keyName]??environment[keyName]??''});
const file=saveEvidence(directory,provider,report.startedAt,report);
console.log(JSON.stringify({file,provider,outcome:report.outcome,...('reason' in report?{reason:report.reason,httpStatus:report.httpStatus}:{citations:report.citations.map(c=>c.url),model:report.responseModel})}));
if(report.outcome==='unavailable')process.exitCode=2;return;
}
if(mode==='google') {
if(!process.env.TSC_GSC_SERVICE_ACCOUNT)throw new Error('Google service account path required');
const auth=await searchConsoleToken(JSON.parse(readFileSync(process.env.TSC_GSC_SERVICE_ACCOUNT,'utf8')));
if(auth.status!=='ok')throw new Error('Google authentication unavailable');
const property=`sc-domain:${new URL(SITE_ORIGIN).hostname}`,startedAt=new Date().toISOString();
const client=new SearchConsole({token:auth.data,property,origin:SITE_ORIGIN});
const ownership=await client.site();
if(ownership.status!=='ok'||!object(ownership.data)||!['siteOwner','siteFullUser','siteRestrictedUser'].includes(String(ownership.data.permissionLevel)))throw new Error('Search Console property access unavailable');
const [sitemaps,analytics]=await Promise.all([client.sitemaps(),client.analytics(process.argv[3]??'',process.argv[4]??'')]);
const inspections=[];
for(const page of ROUTES) inspections.push({url:SITE_ORIGIN+page.path,result:await client.inspect(SITE_ORIGIN+page.path)});
const report={schema:'tsc-search-console-observation/v1',startedAt,finishedAt:new Date().toISOString(),property,readOnly:true,ownership,sitemaps,analytics,inspections,
backlinks:{status:'native-report-required',reason:'No Links API resource in the official Search Console API reference.'},
generativeAi:{status:'native-report-required',reason:'Search AI participation and generative AI report readback require the native supported surface; do not infer them from web analytics.'}};
const file=saveEvidence(directory,'google',startedAt,report);
console.log(JSON.stringify({file,property,sitemaps:sitemaps.status,analytics:analytics.status,inspections:inspections.map(i=>({url:i.url,status:i.result.status})),nativeReportsRequired:['Links','Generative AI']}));
if([sitemaps,analytics,...inspections.map(i=>i.result)].some(x=>x.status!=='ok'))process.exitCode=2;return;
}
const nodePath=process.env.TSC_DISCOVERY_NODE;
if(!nodePath || !process.env.TSC_DISCOVERY_NODE_SHA256) throw new Error('Pinned canonical node path and SHA256 required');
const bytes=readFileSync(nodePath); const hash=createHash('sha256').update(bytes).digest('hex');
if(hash!==process.env.TSC_DISCOVERY_NODE_SHA256) throw new Error('Canonical node hash changed');
const node:unknown=JSON.parse(bytes.toString('utf8'));
if(!object(node)||!webUrl(node.canonicalHost)||!object(node.org)||typeof node.org['@id']!=='string') throw new Error('Canonical node contract missing');
const canonicalHost=String(node.canonicalHost);
if(new URL(canonicalHost).pathname!=='/'||new URL(node.org['@id']).origin!==new URL(canonicalHost).origin) throw new Error('Canonical node identity scope mismatch');
const sites=[SITE_ORIGIN+'/',canonicalHost];
const client=new BingWebmaster({key:process.env.BING_KEY??environment.BING_KEY??'',sites});
const report=await collectBing(client,[{url:sites[0],role:'website',targets:ROUTES.map(p=>SITE_ORIGIN+p.path)},{url:sites[1],role:'organization',targets:[sites[1]]}],node);
const stable=createHash('sha256').update(readFileSync(nodePath)).digest('hex')===hash;
const file=saveEvidence(directory,'bing',report.startedAt,{...report,canonicalNode:{sha256:hash,unchangedDuringRun:stable},limitations:['Canonical relationships are expected links, not independent endorsements.','Canonical-node facts were read, not modified or republished.','Bing empty detail lists do not prove zero inbound links.']});
console.log(JSON.stringify({file,canonicalNodeUnchanged:stable,properties:report.properties.map(p=>({url:p.property,status:p.status,expectedRelations:p.backlinks?.reconciliation.length,queriedTargets:p.backlinks?.targets.length,observedDetailRows:p.backlinks?.targets.reduce((n,t)=>n+t.sample.records.length,0),aiPerformance:p.aiPerformance.status}))}));
if(!stable || report.properties.some(p=>p.status!=='collected')) process.exitCode=2;
}
main().catch(()=>{console.error('Discovery collection failed; verify configured provider, credential access, canonical node pin and private output path. Credentials and provider error bodies are not logged.');process.exitCode=1;});