@0x26/amp 0.6.1 → 0.6.3
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.
- package/dist/index.js +27 -14
- package/package.json +1 -1
- package/skill/SKILL.md +29 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Help}from"commander";import{homedir}from"node:os";import{dirname,join}from"node:path";import{chmodSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{homedir as homedir2}from"node:os";import{dirname as dirname2,join as join2}from"node:path";import{chmodSync as chmodSync2,existsSync as existsSync2,mkdirSync as mkdirSync2,readFileSync as readFileSync2,writeFileSync as writeFileSync2}from"node:fs";import{closeSync,openSync,rmSync,statSync,unlinkSync}from"node:fs";import{createHash,randomBytes}from"node:crypto";import{closeSync as closeSync2,readSync}from"node:fs";import{Command,CommanderError}from"commander";import{existsSync as existsSync7,rmSync as rmSync3}from"node:fs";import{createServer}from"node:http";import{spawn}from"node:child_process";import"node:fs";import"node:os";import"node:path";import{createHash as createHash2,randomUUID}from"node:crypto";import{mkdirSync as mkdirSync3,readFileSync as readFileSync3,renameSync,writeFileSync as writeFileSync3,rmSync as rmSync2}from"node:fs";import{dirname as dirname3,join as join3}from"node:path";import{homedir as homedir3}from"node:os";import{existsSync as existsSync5,mkdirSync as mkdirSync6,readFileSync as readFileSync6,writeFileSync as writeFileSync6}from"node:fs";import{homedir as homedir5}from"node:os";import{dirname as dirname6,join as join6,resolve}from"node:path";import{fileURLToPath}from"node:url";import{Option}from"commander";import{spawnSync}from"node:child_process";import{existsSync as existsSync4,mkdirSync as mkdirSync5,readFileSync as readFileSync5,renameSync as renameSync2,writeFileSync as writeFileSync5}from"node:fs";import{homedir as homedir4}from"node:os";import{dirname as dirname5,join as join5}from"node:path";import{execFileSync}from"node:child_process";import{existsSync as existsSync3,mkdirSync as mkdirSync4,readFileSync as readFileSync4,writeFileSync as writeFileSync4}from"node:fs";import{dirname as dirname4,join as join4}from"node:path";import{writeFileSync as writeFileSync7}from"node:fs";import{spawn as spawn2}from"node:child_process";import{randomUUID as randomUUID2}from"node:crypto";import{join as join8,resolve as resolve2}from"node:path";var __defProp=Object.defineProperty,__getOwnPropNames=Object.getOwnPropertyNames,__esm=(fn,res)=>function(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res},__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})};function colorEnabled(env=process.env,tty=!!process.stdout.isTTY){return tty&&env.TERM!=="dumb"&&env.NO_COLOR===void 0&&env.FORCE_COLOR!=="0"}function paint(text,role="accent",enabled=colorEnabled()){return enabled?`\x1B[${{accent:"38;5;75",muted:"2",strong:"1",error:"31"}[role]}m${text}\x1B[0m`:text}function wrap(text,width){let lines=[],line="";for(let word of clean(text).split(/\s+/)){line&&line.length+word.length+1>width&&(lines.push(line),line="");let rest=word;for(;rest.length>width;)line&&(lines.push(line),line=""),lines.push(rest.slice(0,width)),rest=rest.slice(width);line+=(line?" ":"")+rest}return(line||!lines.length)&&lines.push(line),lines}function brandedHelp(cmd,helper,style,options){let width=Math.max(32,Math.min(options.width,100)),inner=width-4,ink=(s,role="accent")=>paint(s,role,options.color),line=(s="")=>`${ink("\u2502","muted")} ${s} ${ink("\u2502","muted")}`,padded=(s,role)=>line((role?ink(s,role):s)+" ".repeat(Math.max(0,inner-s.length))),panel=(title,rows)=>{let label=` ${title} `,out2=[ink("\u256D\u2500","muted")+ink(label,"strong")+ink("\u2500".repeat(Math.max(0,width-label.length-3))+"\u256E","muted")],termWidth=Math.min(Math.max(0,...rows.map(([term])=>term.length)),Math.floor(inner*.4));for(let[term,description]of rows){if(!description){out2.push(...wrap(term,inner).map(s=>padded(s,"accent")));continue}inner<48||term.length>termWidth?(out2.push(...wrap(term,inner).map(s=>padded(s,"accent"))),out2.push(...wrap(description,inner-2).map(s=>padded(" "+s)))):wrap(description,inner-termWidth-2).forEach((s,i)=>{let left=i===0?term:"";out2.push(line(ink(left)+" ".repeat(termWidth-left.length+2)+s+" ".repeat(inner-termWidth-2-s.length)))})}return out2.push(ink("\u2570"+"\u2500".repeat(width-2)+"\u256F","muted")),out2.join(`
|
|
2
|
+
import{Help}from"commander";import{homedir}from"node:os";import{dirname,join}from"node:path";import{chmodSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{homedir as homedir2}from"node:os";import{dirname as dirname2,join as join2}from"node:path";import{chmodSync as chmodSync2,existsSync as existsSync2,mkdirSync as mkdirSync2,readFileSync as readFileSync2,writeFileSync as writeFileSync2}from"node:fs";import{closeSync,openSync,rmSync,statSync,unlinkSync}from"node:fs";import{createHash,randomBytes}from"node:crypto";import{closeSync as closeSync2,readSync}from"node:fs";import{Command,CommanderError}from"commander";import{existsSync as existsSync7,rmSync as rmSync3}from"node:fs";import{createServer}from"node:http";import{spawn}from"node:child_process";import"node:fs";import"node:os";import"node:path";import{createHash as createHash2,randomUUID}from"node:crypto";import{mkdirSync as mkdirSync3,readFileSync as readFileSync3,renameSync,writeFileSync as writeFileSync3,rmSync as rmSync2}from"node:fs";import{dirname as dirname3,join as join3}from"node:path";import{homedir as homedir3}from"node:os";import{existsSync as existsSync5,mkdirSync as mkdirSync6,readFileSync as readFileSync6,writeFileSync as writeFileSync6}from"node:fs";import{homedir as homedir5}from"node:os";import{dirname as dirname6,join as join6,resolve}from"node:path";import{fileURLToPath}from"node:url";import{Option}from"commander";import{spawnSync}from"node:child_process";import{existsSync as existsSync4,mkdirSync as mkdirSync5,readFileSync as readFileSync5,renameSync as renameSync2,writeFileSync as writeFileSync5}from"node:fs";import{homedir as homedir4}from"node:os";import{dirname as dirname5,join as join5}from"node:path";import{execFileSync}from"node:child_process";import{existsSync as existsSync3,mkdirSync as mkdirSync4,readFileSync as readFileSync4,writeFileSync as writeFileSync4}from"node:fs";import{dirname as dirname4,join as join4}from"node:path";import{writeFileSync as writeFileSync7}from"node:fs";import{spawn as spawn2}from"node:child_process";import{randomUUID as randomUUID2}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{join as join8,resolve as resolve2}from"node:path";var __defProp=Object.defineProperty,__getOwnPropNames=Object.getOwnPropertyNames,__esm=(fn,res)=>function(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res},__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})};function colorEnabled(env=process.env,tty=!!process.stdout.isTTY){return tty&&env.TERM!=="dumb"&&env.NO_COLOR===void 0&&env.FORCE_COLOR!=="0"}function paint(text,role="accent",enabled=colorEnabled()){return enabled?`\x1B[${{accent:"38;5;75",muted:"2",strong:"1",error:"31"}[role]}m${text}\x1B[0m`:text}function wrap(text,width){let lines=[],line="";for(let word of clean(text).split(/\s+/)){line&&line.length+word.length+1>width&&(lines.push(line),line="");let rest=word;for(;rest.length>width;)line&&(lines.push(line),line=""),lines.push(rest.slice(0,width)),rest=rest.slice(width);line+=(line?" ":"")+rest}return(line||!lines.length)&&lines.push(line),lines}function brandedHelp(cmd,helper,style,options){let width=Math.max(32,Math.min(options.width,100)),inner=width-4,ink=(s,role="accent")=>paint(s,role,options.color),line=(s="")=>`${ink("\u2502","muted")} ${s} ${ink("\u2502","muted")}`,padded=(s,role)=>line((role?ink(s,role):s)+" ".repeat(Math.max(0,inner-s.length))),panel=(title,rows)=>{let label=` ${title} `,out2=[ink("\u256D\u2500","muted")+ink(label,"strong")+ink("\u2500".repeat(Math.max(0,width-label.length-3))+"\u256E","muted")],termWidth=Math.min(Math.max(0,...rows.map(([term])=>term.length)),Math.floor(inner*.4));for(let[term,description]of rows){if(!description){out2.push(...wrap(term,inner).map(s=>padded(s,"accent")));continue}inner<48||term.length>termWidth?(out2.push(...wrap(term,inner).map(s=>padded(s,"accent"))),out2.push(...wrap(description,inner-2).map(s=>padded(" "+s)))):wrap(description,inner-termWidth-2).forEach((s,i)=>{let left=i===0?term:"";out2.push(line(ink(left)+" ".repeat(termWidth-left.length+2)+s+" ".repeat(inner-termWidth-2-s.length)))})}return out2.push(ink("\u2570"+"\u2500".repeat(width-2)+"\u256F","muted")),out2.join(`
|
|
3
3
|
`)},root=!cmd.parent,out=[""];if(root&&width>=55){let mark=[" \u2584\u2580\u2580\u2584 "," \u2580\u2584\u2584\u2580 "," \u2584\u2580 \u2580\u2584\u2580 "," \u2580\u2584\u2584\u2584\u2580 \u2580 "],text=["AMPERSAND",`${cmd.name()} / ${style.edition}`,cmd.version()?`v${cmd.version()}`:"",""];mark.forEach((s,i)=>out.push(" "+ink(s)+" "+ink(text[i],i===0?"strong":"muted")))}else out.push(ink(` & AMPERSAND / ${cmd.name()}`,"strong"));out.push("",...wrap(helper.commandDescription(cmd),width).map(s=>ink(s,"muted")),""),out.push(panel("Usage",[[helper.commandUsage(cmd),""]]));let commands=helper.visibleCommands(cmd);if(commands.length){let used=new Set;for(let group of root?style.groups??[]:[]){let members=commands.filter(c=>group.commands.includes(c.name()));members.length&&(members.forEach(c=>used.add(c.name())),out.push("",panel(group.title,members.map(c=>[helper.subcommandTerm(c),helper.subcommandDescription(c)]))))}let remaining=commands.filter(c=>!used.has(c.name()));remaining.length&&out.push("",panel(used.size?"More commands":"Commands",remaining.map(c=>[helper.subcommandTerm(c),helper.subcommandDescription(c)])))}let args=helper.visibleArguments(cmd);args.length&&out.push("",panel("Arguments",args.map(a=>[helper.argumentTerm(a),helper.argumentDescription(a)])));let opts=helper.visibleOptions(cmd);return opts.length&&out.push("",panel("Options",opts.map(o=>[helper.optionTerm(o),helper.optionDescription(o)]))),root?out.push("",panel("Start here",style.examples.map(e=>[e,""]))):out.push("",ink("Global options: "+(()=>{let root2=cmd;for(;root2.parent;)root2=root2.parent;return root2.name()})()+" --help","muted")),out.push(""),out.join(`
|
|
4
4
|
`)}function installTerminalHelp(program2,style){program2.configureHelp({formatHelp(cmd,helper){let args=process.argv.slice(2);return!process.stdout.isTTY||process.env.TERM==="dumb"||args.includes("--agent")||args.includes("--json")?Help.prototype.formatHelp.call(helper,cmd,helper):brandedHelp(cmd,helper,style,{width:process.stdout.columns||80,color:colorEnabled()})}})}function askProgress(write2,options={}){let now=options.now??Date.now,start=now(),frames=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],stage="Connecting",frame=0,stopped=!1,lastUpdate=start,draw=()=>{if(stopped)return;let elapsed=`${Math.floor((now()-start)/1e3)}s`,width=Math.max(12,(options.width??process.stderr.columns??80)-1),stale=Math.floor((now()-lastUpdate)/1e3),age=stale>=15?` \xB7 last update ${stale}s ago`:"",suffix=` \xB7 ${elapsed}${age.length+elapsed.length+13<=width?age:""}`,text=stage.slice(0,Math.max(1,width-suffix.length-2));write2(`\r\x1B[2K${paint(frames[frame++%frames.length],"accent",options.color)} ${text}${suffix}`)};draw();let timer=setInterval(draw,100);return timer.unref(),{update(message){stage=clean(message).trim().replace(/\s+/g," ")||"Working",lastUpdate=now()},note(message){stopped||(write2("\r\x1B[2K"+clean(message)+`
|
|
5
5
|
`),draw())},stop(){stopped||(stopped=!0,clearInterval(timer),write2("\r\x1B[2K"))}}}var clean,init_terminal=__esm({"src/terminal.ts"(){"use strict";clean=text=>text.replace(/[\x00-\x1f\x7f-\x9f]/g," ")}}),NPM_PACKAGE,init_brand=__esm({"src/brand.ts"(){"use strict";NPM_PACKAGE="@0x26/amp"}});function configPath(){return process.env.AMP_CONFIG_PATH??join(homedir(),".config","amp","config.json")}function readConfig(){let p=configPath();if(!existsSync(p))return{profiles:{}};try{let parsed=JSON.parse(readFileSync(p,"utf8"));return{current:parsed.current,profiles:parsed.profiles??{}}}catch{return{profiles:{}}}}function writeConfig(cfg){let p=configPath();mkdirSync(dirname(p),{recursive:!0,mode:448}),writeFileSync(p,`${JSON.stringify(cfg,null,2)}
|
|
@@ -9,12 +9,12 @@ import{Help}from"commander";import{homedir}from"node:os";import{dirname,join}fro
|
|
|
9
9
|
${payload.error.next_command?`Next: ${payload.error.next_command}
|
|
10
10
|
`:""}`),process.exitCode=1}function humanTable(rows){if(!rows.length)return"No results.";let columns=[...new Set(rows.flatMap(row=>Object.keys(row)))].slice(0,8),text=value=>String(typeof value=="object"&&value!==null?JSON.stringify(value):value??"").replace(/[\x00-\x1f\x7f-\x9f]/g," "),clean3=value=>text(value).slice(0,60),cells=[columns.map(clean3),...rows.slice(0,20).map(row=>columns.map(col=>clean3(row[col])))],widths=columns.map((_,i)=>Math.max(...cells.map(row=>row[i].length))),lines=cells.map(row=>row.map((v,i)=>v.padEnd(widths[i])).join(" ").trimEnd());return lines[0]=paint(lines[0],"accent"),lines.splice(1,0,paint(widths.map(n=>"\u2500".repeat(n)).join(" "),"muted")),lines.join(`
|
|
11
11
|
`)+(rows.length>20||rows.some(row=>Object.keys(row).length>8||Object.entries(row).some(([k,v])=>text(k).length>60||text(v).length>60))?`
|
|
12
|
-
Preview only; use --json or --out <file> for all returned data.`:"")}var current,experience,CliError,init_experience=__esm({"src/experience.ts"(){"use strict";init_terminal(),init_brand(),current={enabled:!1,agent:!1,json:!1,quiet:!1,noInput:!1},experience=()=>current,CliError=class extends Error{constructor(code,message,retryable=!1,nextCommand,details){super(message),this.code=code,this.retryable=retryable,this.nextCommand=nextCommand,this.details=details}}}});function bundle(){let store=process.env.AMP_MCP_URL??activeProfile()?.store_url;return{workosHost:"https://api.workos.com",clientId:"client_01KW0BEQP6KZ5ES1B39HEWA571",mcpUrl:store?normalizeStoreUrl(store)+"/mcp":"",defaultOrgId:""}}var init_env_customer=__esm({"src/env.customer.ts"(){init_config()}});function clientId(){return bundle().clientId}function workosBase(){return bundle().workosHost}function base64url(buf){return buf.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function generatePkce(){let verifier=base64url(randomBytes(32)),challenge=base64url(createHash("sha256").update(verifier).digest());return{verifier,challenge}}function generateState(){return base64url(randomBytes(32))}function buildAuthorizeUrl(opts){let u=new URL("/user_management/authorize",workosBase());return u.searchParams.set("client_id",opts.clientId),u.searchParams.set("redirect_uri",opts.redirectUri),u.searchParams.set("response_type","code"),u.searchParams.set("provider","authkit"),u.searchParams.set("code_challenge",opts.codeChallenge),u.searchParams.set("code_challenge_method","S256"),u.searchParams.set("state",opts.state),opts.organizationId&&u.searchParams.set("organization_id",opts.organizationId),u.toString()}async function exchangeCode(opts){return authenticate({client_id:opts.clientId,grant_type:"authorization_code",code:opts.code,code_verifier:opts.codeVerifier})}async function refreshTokens(opts){return authenticate({client_id:opts.clientId,grant_type:"refresh_token",refresh_token:opts.refreshToken})}async function authenticate(body){let res=await fetch(`${workosBase()}/user_management/authenticate`,{method:"POST",headers:{"content-type":"application/json"},redirect:"error",body:JSON.stringify(body)});if(!res.ok){let text=await res.text(),detail=/invalid_grant/.test(text)?"invalid_grant":"Authentication refused";throw new Error(`WorkOS ${body.grant_type} failed (${res.status}): ${detail}`)}return await res.json()}function decodeJwtPayload(jwt){let parts=jwt.split(".");if(parts.length<2)throw new Error("malformed JWT");let json=Buffer.from(parts[1],"base64url").toString("utf8");return JSON.parse(json)}function expiryFromClaims(claims){let ms=claims.exp?claims.exp*1e3:Date.now()+36e5;return new Date(ms).toISOString()}var CALLBACK_PORT,REDIRECT_URI,init_workos=__esm({"src/auth/workos.ts"(){"use strict";init_brand(),init_env_customer(),CALLBACK_PORT=9123,REDIRECT_URI=`http://127.0.0.1:${CALLBACK_PORT}/auth/callback`}});function machineConfigFromEnv(env=process.env){let clientId2=(env[CLIENT_ID_ENV]??"").trim(),clientSecret=(env[CLIENT_SECRET_ENV]??"").trim(),tokenUrl=(env[TOKEN_URL_ENV]??"").trim(),accessToken=(env[ACCESS_TOKEN_ENV]??"").trim(),triplePresent=[CLIENT_ID_ENV,CLIENT_SECRET_ENV,TOKEN_URL_ENV].some(key=>env[key]!==void 0);if(env[ACCESS_TOKEN_ENV]!==void 0){if(triplePresent)throw new Error(`${ACCESS_TOKEN_ENV} cannot be combined with ${CLIENT_ID_ENV}/${CLIENT_SECRET_ENV}/${TOKEN_URL_ENV} \u2014 pick ONE machine shape. A raw token is for a process that must never hold the client secret; if you have the secret, mint with the triple instead.`);if(accessToken==="")throw new Error(`${ACCESS_TOKEN_ENV} is set but empty or whitespace. Refusing to fall back to a stored human session.`);return{kind:"access_token",accessToken,clientId:subjectOf(accessToken)??"(raw token)"}}if(!triplePresent)return null;if(!clientId2||!clientSecret||!tokenUrl){let missing=[clientId2===""?CLIENT_ID_ENV:null,clientSecret===""?CLIENT_SECRET_ENV:null,tokenUrl===""?TOKEN_URL_ENV:null].filter(Boolean);throw new Error(`the machine lane needs all of ${CLIENT_ID_ENV}, ${CLIENT_SECRET_ENV}, ${TOKEN_URL_ENV} \u2014 missing ${missing.join(", ")}. Refusing rather than falling back to a stored human session, which would target whatever tenant that session holds.`)}return{kind:"client_credentials",clientId:clientId2,clientSecret,tokenUrl}}function subjectOf(jwt){let parts=jwt.split(".");if(parts.length!==3)return null;try{let payload=JSON.parse(Buffer.from(parts[1],"base64url").toString("utf8"));return typeof payload.sub=="string"?payload.sub:null}catch{return null}}async function machineToken(cfg,fetchImpl=fetch,nowMs=Date.now()){if(cfg.kind==="access_token")return cfg.accessToken;if(cached&&cached.expiresAtMs-6e4>nowMs)return cached.accessToken;let res=await fetchImpl(cfg.tokenUrl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:cfg.clientId,client_secret:cfg.clientSecret}).toString()}),text=await res.text();if(res.status<200||res.status>=300)throw new Error(`WorkOS M2M token request failed: ${res.status} ${text.slice(0,200)}`);let parsed;try{parsed=JSON.parse(text)}catch{throw new Error("WorkOS M2M token response was not JSON")}let accessToken=typeof parsed.access_token=="string"?parsed.access_token:"";if(!accessToken)throw new Error("WorkOS M2M token response had no access_token");let ttlS=typeof parsed.expires_in=="number"&&Number.isFinite(parsed.expires_in)&&parsed.expires_in>0?parsed.expires_in:300;return cached={accessToken,expiresAtMs:nowMs+ttlS*1e3},accessToken}function machinePath(path){return path.startsWith("/machine/")?path:`/machine${path}`}var CLIENT_ID_ENV,CLIENT_SECRET_ENV,TOKEN_URL_ENV,ACCESS_TOKEN_ENV,cached,init_machine=__esm({"src/machine.ts"(){"use strict";CLIENT_ID_ENV="AMP_M2M_CLIENT_ID",CLIENT_SECRET_ENV="AMP_M2M_CLIENT_SECRET",TOKEN_URL_ENV="AMP_WORKOS_TOKEN_URL",ACCESS_TOKEN_ENV="AMP_M2M_ACCESS_TOKEN",cached=null}});function requestCredentialConfigured(){return cached2!==void 0||failed||process.env.AMP_REQUEST_TOKEN_FD!==void 0||process.env.AMP_REQUEST_STORE_URL!==void 0}function refusal(){return new Error("Invalid or expired request credential configuration; refusing saved credentials and machine fallback.")}function requestCredential(){if(!requestCredentialConfigured())return null;if(failed)throw refusal();try{let fdText=process.env.AMP_REQUEST_TOKEN_FD,origin2=process.env.AMP_REQUEST_STORE_URL;if(CONFLICTING_ENV.some(key=>process.env[key]!==void 0)||process.argv.some(arg=>arg==="--profile"||arg.startsWith("--profile="))||!fdText||!/^[1-9][0-9]*(?![\s\S])/.test(fdText)||!origin2||origin2.trim()!==origin2)throw refusal();let fd=Number(fdText);if(!Number.isSafeInteger(fd)||fd<3||fd>2147483647)throw refusal();let store=new URL(origin2),loopback2=["localhost","127.0.0.1","[::1]"].includes(store.hostname);if(store.protocol!=="https:"&&!(store.protocol==="http:"&&loopback2)||store.username||store.password||store.search||store.hash||store.pathname!=="/"&&store.pathname!=="")throw refusal();let config=JSON.stringify([fdText,origin2]);if(cached2){if(cachedConfig!==config||cached2.expiresAt<=Date.now())throw refusal();return cached2}let bytes=Buffer.alloc(MAX_TOKEN_BYTES+1),used=0,bearer;try{for(;used<bytes.length;){let count=readSync(fd,bytes,used,bytes.length-used,null);if(count===0)break;used+=count}if(used===0||used>MAX_TOKEN_BYTES)throw refusal();bearer=bytes.subarray(0,used).toString("utf8")}finally{bytes.fill(0),closeSync2(fd)}if(!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+(?![\s\S])/.test(bearer))throw refusal();let claims=JSON.parse(Buffer.from(bearer.split(".")[1],"base64url").toString("utf8"));if(!claims||typeof claims!="object"||Array.isArray(claims))throw refusal();let payload=claims;if(typeof payload.sub!="string"||!payload.sub.startsWith("user_")||typeof payload.org_id!="string"||!payload.org_id.startsWith("org_")||typeof payload.exp!="number"||!Number.isFinite(payload.exp*1e3)||payload.exp*1e3<=Date.now()||payload.act!==void 0||payload.impersonator!==void 0||payload.nbf!==void 0&&(typeof payload.nbf!="number"||!Number.isFinite(payload.nbf)||payload.nbf*1e3>Date.now()))throw refusal();return cached2=Object.freeze({bearer,storeOrigin:store.origin,expiresAt:payload.exp*1e3,subject:payload.sub,orgId:payload.org_id}),cachedConfig=config,cached2}catch{throw failed=!0,refusal()}}function assertRequestReadRoute(method,route){if(!route.startsWith("/")||route.startsWith("//")||/[\\#\s]/.test(route))throw refusal();let pathname=route.split("?")[0];for(let part of pathname.split("/")){let decoded;try{decoded=decodeURIComponent(part)}catch{throw refusal()}if(decoded==="."||decoded===".."||/[%\\/\x00-\x1f\x7f]/.test(decoded))throw refusal()}let segment="[A-Za-z0-9_%.-]+",getPatterns=["^/v1/(users|artifacts|frame-ocr|capture/health)$",`^/v1/sql-jobs/${segment}$`,`^/v1/ontology/object-types(?:/${segment}(?:/dataset(?:/transactions)?)?)?$`,`^/v1/ontology/objects/${segment}(?:/${segment})?$`,`^/v1/ontology/(count|aggregate)/${segment}$`];if(!(method==="GET"?getPatterns.some(pattern=>new RegExp(pattern).test(pathname)):method==="POST"&&["/v1/query","/v1/sql-jobs","/v1/search"].includes(route)))throw new Error("Request credential permits only approved store read routes.")}var MAX_TOKEN_BYTES,CONFLICTING_ENV,cached2,cachedConfig,failed,init_request_credential=__esm({"src/request-credential.ts"(){"use strict";MAX_TOKEN_BYTES=16*1024,CONFLICTING_ENV=["AMP_M2M_ACCESS_TOKEN","AMP_M2M_CLIENT_ID","AMP_M2M_CLIENT_SECRET","AMP_WORKOS_TOKEN_URL","AMP_PROFILE","AMP_CONFIG_PATH","AMP_CREDENTIALS_PATH","AMP_MCP_URL","AMP_WORKOS_CLIENT_ID","AMP_WORKOS_BASE"],failed=!1}});function stillFresh(creds){let expMs=Date.parse(creds.expires_at);return!Number.isNaN(expMs)&&expMs-Date.now()>REFRESH_SKEW_MS}async function ensureFreshJwt(){if(requestCredentialConfigured())throw new Error("This command requires a saved human session; request credentials cannot be emitted or refreshed.");if(machineConfigFromEnv())throw new Error("This command requires a human session, but machine credentials are configured. Refusing to use stored human credentials. Unset AMP_M2M_ACCESS_TOKEN, AMP_M2M_CLIENT_ID, AMP_M2M_CLIENT_SECRET, and AMP_WORKOS_TOKEN_URL to use the human lane.");let creds=readRaw();if(!creds)throw new NotLoggedInError("Not logged in \u2014 run amp login");if(stillFresh(creds))return creds.jwt;if(!creds.refresh_token)throw new NotLoggedInError("Session expired and no refresh token \u2014 run amp login");return withFileLock(`${credentialsPath()}.lock`,async()=>{let cur=readRaw();if(!cur)throw new NotLoggedInError("Session removed \u2014 run amp login");if(stillFresh(cur))return cur.jwt;let refreshToken=cur.refresh_token;if(!refreshToken)throw new NotLoggedInError("Session expired and no refresh token \u2014 run amp login");let r;try{r=await refreshTokens({refreshToken,clientId:clientId()})}catch(err){if(err instanceof Error&&/invalid_grant/.test(err.message)){let rescued=readRaw();if(rescued&&stillFresh(rescued))return rescued.jwt;throw new NotLoggedInError("Session expired \u2014 run amp login")}throw err}let claims=decodeJwtPayload(r.access_token),next={jwt:r.access_token,expires_at:expiryFromClaims(claims),refresh_token:r.refresh_token??refreshToken,email:cur.email,org_id:cur.org_id,org_name:cur.org_name};return write(next),next.jwt})}var REFRESH_SKEW_MS,NotLoggedInError,init_session=__esm({"src/session.ts"(){"use strict";init_brand(),init_credentials(),init_workos(),init_lock(),init_machine(),init_request_credential(),REFRESH_SKEW_MS=6e4,NotLoggedInError=class extends Error{}}}),http_exports={};__export(http_exports,{DoorRefusalError:()=>DoorRefusalError,doorRequest:()=>doorRequest,reportError:()=>reportError,storeOrigin:()=>storeOrigin});function storeOrigin(){let request=requestCredential();if(request)return request.storeOrigin;let url=bundle().mcpUrl.replace(/\/mcp\/?$/,"");if(!url)throw new Error("Select a tenant with amp login --store <url>.");return url}async function doorRequest(method,path,body){let request=requestCredential(),machine=request?null:machineConfigFromEnv(),headers={},url;if(request)assertRequestReadRoute(method,path),headers.authorization=`Bearer ${request.bearer}`,url=`${request.storeOrigin}${path}`;else if(machine)headers.authorization=`Bearer ${await machineToken(machine)}`,url=`${storeOrigin()}${machinePath(path)}`;else{let jwt=await ensureFreshJwt(),creds=readRaw();headers.authorization=`Bearer ${jwt}`,creds?.org_id&&(headers["x-org-id"]=creds.org_id),url=`${storeOrigin()}${path}`}body!==void 0&&(headers["content-type"]="application/json");let res;try{res=await fetch(url,{method,headers,body:body===void 0?void 0:JSON.stringify(body),redirect:"error"})}catch{throw request?new Error("Request credential store read failed; no credential fallback attempted."):new CliError("CONNECTION_FAILED","Cannot reach your workspace. Check its URL and your connection.",!0)}if(!res.ok)throw await res.body?.cancel().catch(()=>{}),httpError(res.status);if(request&&!res.ok)throw await res.body?.cancel().catch(()=>{}),new DoorRefusalError(res.status,"Request credential store read refused.");let text;try{text=await res.text()}catch(err){throw request?new Error("Request credential store response could not be read."):err}if(!res.ok)throw new DoorRefusalError(res.status,"Request refused. Check your session and permissions.");return text}function reportError(err){if(experience().enabled){renderError(err instanceof NotLoggedInError?new CliError("AUTH_REQUIRED","Sign in to your workspace.",!1,"amp login"):err);return}err instanceof DoorRefusalError?(process.stderr.write(`${err.message}:
|
|
12
|
+
Preview only; use --json or --out <file> for all returned data.`:"")}var current,experience,CliError,init_experience=__esm({"src/experience.ts"(){"use strict";init_terminal(),init_brand(),current={enabled:!1,agent:!1,json:!1,quiet:!1,noInput:!1},experience=()=>current,CliError=class extends Error{constructor(code,message,retryable=!1,nextCommand,details){super(message),this.code=code,this.retryable=retryable,this.nextCommand=nextCommand,this.details=details}}}});function bundle(){let store=process.env.AMP_MCP_URL??activeProfile()?.store_url;return{workosHost:"https://api.workos.com",clientId:"client_01KW0BEQP6KZ5ES1B39HEWA571",mcpUrl:store?normalizeStoreUrl(store)+"/mcp":"",defaultOrgId:""}}var init_env_customer=__esm({"src/env.customer.ts"(){init_config()}});function clientId(){return bundle().clientId}function workosBase(){return bundle().workosHost}function base64url(buf){return buf.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function generatePkce(){let verifier=base64url(randomBytes(32)),challenge=base64url(createHash("sha256").update(verifier).digest());return{verifier,challenge}}function generateState(){return base64url(randomBytes(32))}function buildAuthorizeUrl(opts){let u=new URL("/user_management/authorize",workosBase());return u.searchParams.set("client_id",opts.clientId),u.searchParams.set("redirect_uri",opts.redirectUri),u.searchParams.set("response_type","code"),u.searchParams.set("provider","authkit"),u.searchParams.set("code_challenge",opts.codeChallenge),u.searchParams.set("code_challenge_method","S256"),u.searchParams.set("state",opts.state),opts.organizationId&&u.searchParams.set("organization_id",opts.organizationId),u.toString()}async function exchangeCode(opts){return authenticate({client_id:opts.clientId,grant_type:"authorization_code",code:opts.code,code_verifier:opts.codeVerifier})}async function refreshTokens(opts){return authenticate({client_id:opts.clientId,grant_type:"refresh_token",refresh_token:opts.refreshToken,...opts.organizationId?{organization_id:opts.organizationId}:{}})}async function authenticate(body){let res=await fetch(`${workosBase()}/user_management/authenticate`,{method:"POST",headers:{"content-type":"application/json"},redirect:"error",body:JSON.stringify(body)});if(!res.ok){let text=await res.text(),detail=/invalid_grant/.test(text)?"invalid_grant":"Authentication refused";throw new Error(`WorkOS ${body.grant_type} failed (${res.status}): ${detail}`)}return await res.json()}function decodeJwtPayload(jwt){let parts=jwt.split(".");if(parts.length<2)throw new Error("malformed JWT");let json=Buffer.from(parts[1],"base64url").toString("utf8");return JSON.parse(json)}function expiryFromClaims(claims){let ms=claims.exp?claims.exp*1e3:Date.now()+36e5;return new Date(ms).toISOString()}var CALLBACK_PORT,REDIRECT_URI,init_workos=__esm({"src/auth/workos.ts"(){"use strict";init_brand(),init_env_customer(),CALLBACK_PORT=9123,REDIRECT_URI=`http://127.0.0.1:${CALLBACK_PORT}/auth/callback`}});function machineConfigFromEnv(env=process.env){let clientId2=(env[CLIENT_ID_ENV]??"").trim(),clientSecret=(env[CLIENT_SECRET_ENV]??"").trim(),tokenUrl=(env[TOKEN_URL_ENV]??"").trim(),accessToken=(env[ACCESS_TOKEN_ENV]??"").trim(),triplePresent=[CLIENT_ID_ENV,CLIENT_SECRET_ENV,TOKEN_URL_ENV].some(key=>env[key]!==void 0);if(env[ACCESS_TOKEN_ENV]!==void 0){if(triplePresent)throw new Error(`${ACCESS_TOKEN_ENV} cannot be combined with ${CLIENT_ID_ENV}/${CLIENT_SECRET_ENV}/${TOKEN_URL_ENV} \u2014 pick ONE machine shape. A raw token is for a process that must never hold the client secret; if you have the secret, mint with the triple instead.`);if(accessToken==="")throw new Error(`${ACCESS_TOKEN_ENV} is set but empty or whitespace. Refusing to fall back to a stored human session.`);return{kind:"access_token",accessToken,clientId:subjectOf(accessToken)??"(raw token)"}}if(!triplePresent)return null;if(!clientId2||!clientSecret||!tokenUrl){let missing=[clientId2===""?CLIENT_ID_ENV:null,clientSecret===""?CLIENT_SECRET_ENV:null,tokenUrl===""?TOKEN_URL_ENV:null].filter(Boolean);throw new Error(`the machine lane needs all of ${CLIENT_ID_ENV}, ${CLIENT_SECRET_ENV}, ${TOKEN_URL_ENV} \u2014 missing ${missing.join(", ")}. Refusing rather than falling back to a stored human session, which would target whatever tenant that session holds.`)}return{kind:"client_credentials",clientId:clientId2,clientSecret,tokenUrl}}function subjectOf(jwt){let parts=jwt.split(".");if(parts.length!==3)return null;try{let payload=JSON.parse(Buffer.from(parts[1],"base64url").toString("utf8"));return typeof payload.sub=="string"?payload.sub:null}catch{return null}}async function machineToken(cfg,fetchImpl=fetch,nowMs=Date.now()){if(cfg.kind==="access_token")return cfg.accessToken;if(cached&&cached.expiresAtMs-6e4>nowMs)return cached.accessToken;let res=await fetchImpl(cfg.tokenUrl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:cfg.clientId,client_secret:cfg.clientSecret}).toString()}),text=await res.text();if(res.status<200||res.status>=300)throw new Error(`WorkOS M2M token request failed: ${res.status} ${text.slice(0,200)}`);let parsed;try{parsed=JSON.parse(text)}catch{throw new Error("WorkOS M2M token response was not JSON")}let accessToken=typeof parsed.access_token=="string"?parsed.access_token:"";if(!accessToken)throw new Error("WorkOS M2M token response had no access_token");let ttlS=typeof parsed.expires_in=="number"&&Number.isFinite(parsed.expires_in)&&parsed.expires_in>0?parsed.expires_in:300;return cached={accessToken,expiresAtMs:nowMs+ttlS*1e3},accessToken}function machinePath(path){return path.startsWith("/machine/")?path:`/machine${path}`}var CLIENT_ID_ENV,CLIENT_SECRET_ENV,TOKEN_URL_ENV,ACCESS_TOKEN_ENV,cached,init_machine=__esm({"src/machine.ts"(){"use strict";CLIENT_ID_ENV="AMP_M2M_CLIENT_ID",CLIENT_SECRET_ENV="AMP_M2M_CLIENT_SECRET",TOKEN_URL_ENV="AMP_WORKOS_TOKEN_URL",ACCESS_TOKEN_ENV="AMP_M2M_ACCESS_TOKEN",cached=null}});function requestCredentialConfigured(){return cached2!==void 0||failed||process.env.AMP_REQUEST_TOKEN_FD!==void 0||process.env.AMP_REQUEST_STORE_URL!==void 0||process.env.AMP_REQUEST_BRANCH_ID!==void 0}function refusal(){return new Error("Invalid or expired request credential configuration; refusing saved credentials and machine fallback.")}function requestCredential(){if(!requestCredentialConfigured())return null;if(failed)throw refusal();try{let fdText=process.env.AMP_REQUEST_TOKEN_FD,origin2=process.env.AMP_REQUEST_STORE_URL,declaredLane=process.env.AMP_REQUEST_LANE;if(declaredLane!==void 0&&declaredLane!=="human"&&declaredLane!=="machine")throw refusal();let branchId=process.env.AMP_REQUEST_BRANCH_ID??"main";if(!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}(?![\s\S])/.test(branchId)||CONFLICTING_ENV.some(key=>process.env[key]!==void 0)||process.argv.some(arg=>arg==="--profile"||arg.startsWith("--profile="))||!fdText||!/^[1-9][0-9]*(?![\s\S])/.test(fdText)||!origin2||origin2.trim()!==origin2)throw refusal();let fd=Number(fdText);if(!Number.isSafeInteger(fd)||fd<3||fd>2147483647)throw refusal();let store=new URL(origin2),loopback2=["localhost","127.0.0.1","[::1]"].includes(store.hostname);if(store.protocol!=="https:"&&!(store.protocol==="http:"&&loopback2)||store.username||store.password||store.search||store.hash||store.pathname!=="/"&&store.pathname!=="")throw refusal();let config=JSON.stringify([fdText,origin2,declaredLane??null,process.env.AMP_REQUEST_BRANCH_ID]);if(cached2){if(cachedConfig!==config||cached2.expiresAt<=Date.now())throw refusal();return cached2}let bytes=Buffer.alloc(MAX_TOKEN_BYTES+1),used=0,bearer;try{for(;used<bytes.length;){let count=readSync(fd,bytes,used,bytes.length-used,null);if(count===0)break;used+=count}if(used===0||used>MAX_TOKEN_BYTES)throw refusal();bearer=bytes.subarray(0,used).toString("utf8")}finally{bytes.fill(0),closeSync2(fd)}if(!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+(?![\s\S])/.test(bearer))throw refusal();let claims=JSON.parse(Buffer.from(bearer.split(".")[1],"base64url").toString("utf8"));if(!claims||typeof claims!="object"||Array.isArray(claims))throw refusal();let payload=claims,lane=declaredLane??(typeof payload.sub=="string"&&payload.sub.startsWith("client_")?"machine":"human"),clientId2=typeof payload.client_id=="string"&&payload.client_id?payload.client_id:payload.sub,subject=lane==="machine"?`m2m:${typeof clientId2=="string"?clientId2:""}`:payload.sub;if(typeof subject!="string"||!(lane==="machine"?/^m2m:(?!user_)[A-Za-z0-9_-]{1,128}(?![\s\S])/:/^user_[A-Za-z0-9_-]{1,128}(?![\s\S])/).test(subject)||lane==="machine"&&typeof payload.sub=="string"&&payload.sub.startsWith("user_")||typeof payload.org_id!="string"||!payload.org_id.startsWith("org_")||typeof payload.exp!="number"||!Number.isFinite(payload.exp*1e3)||payload.exp*1e3<=Date.now()||payload.act!==void 0||payload.impersonator!==void 0||payload.nbf!==void 0&&(typeof payload.nbf!="number"||!Number.isFinite(payload.nbf)||payload.nbf*1e3>Date.now()))throw refusal();return cached2=Object.freeze({bearer,lane,storeOrigin:store.origin,branchId,expiresAt:payload.exp*1e3,subject,orgId:payload.org_id}),cachedConfig=config,cached2}catch{throw failed=!0,refusal()}}function assertRequestReadRoute(method,route){if(!route.startsWith("/")||route.startsWith("//")||/[\\#\s]/.test(route))throw refusal();let pathname=route.split("?")[0];for(let part of pathname.split("/")){let decoded;try{decoded=decodeURIComponent(part)}catch{throw refusal()}if(decoded==="."||decoded===".."||/[%\\/\x00-\x1f\x7f]/.test(decoded))throw refusal()}let segment="[A-Za-z0-9_%.-]+",getPatterns=["^/v1/(users|artifacts|frame-ocr|capture/health)$",`^/v1/sql-jobs/${segment}$`,`^/v1/ontology/object-types(?:/${segment}(?:/dataset(?:/transactions)?)?)?$`,`^/v1/ontology/objects/${segment}(?:/${segment})?$`,`^/v1/ontology/(count|aggregate)/${segment}$`];if(!(method==="GET"?getPatterns.some(pattern=>new RegExp(pattern).test(pathname)):method==="POST"&&["/v1/query","/v1/sql-jobs","/v1/search"].includes(route)))throw new Error("Request credential permits only approved store read routes.")}var MAX_TOKEN_BYTES,CONFLICTING_ENV,cached2,cachedConfig,failed,init_request_credential=__esm({"src/request-credential.ts"(){"use strict";MAX_TOKEN_BYTES=16*1024,CONFLICTING_ENV=["AMP_M2M_ACCESS_TOKEN","AMP_M2M_CLIENT_ID","AMP_M2M_CLIENT_SECRET","AMP_WORKOS_TOKEN_URL","AMP_PROFILE","AMP_CONFIG_PATH","AMP_CREDENTIALS_PATH","AMP_MCP_URL","AMP_WORKOS_CLIENT_ID","AMP_WORKOS_BASE"],failed=!1}});function stillFresh(creds){try{if(creds.org_id&&decodeJwtPayload(creds.jwt).org_id!==creds.org_id)return!1}catch{return!1}let expMs=Date.parse(creds.expires_at);return!Number.isNaN(expMs)&&expMs-Date.now()>REFRESH_SKEW_MS}async function ensureFreshJwt(){if(requestCredentialConfigured())throw new Error("This command requires a saved human session; request credentials cannot be emitted or refreshed.");if(machineConfigFromEnv())throw new Error("This command requires a human session, but machine credentials are configured. Refusing to use stored human credentials. Unset AMP_M2M_ACCESS_TOKEN, AMP_M2M_CLIENT_ID, AMP_M2M_CLIENT_SECRET, and AMP_WORKOS_TOKEN_URL to use the human lane.");let creds=readRaw();if(!creds)throw new NotLoggedInError("Not logged in \u2014 run amp login");if(stillFresh(creds))return creds.jwt;if(!creds.refresh_token)throw new NotLoggedInError("Session expired and no refresh token \u2014 run amp login");return withFileLock(`${credentialsPath()}.lock`,async()=>{let cur=readRaw();if(!cur)throw new NotLoggedInError("Session removed \u2014 run amp login");if(stillFresh(cur))return cur.jwt;let refreshToken=cur.refresh_token;if(!refreshToken)throw new NotLoggedInError("Session expired and no refresh token \u2014 run amp login");let r;try{r=await refreshTokens({refreshToken,clientId:clientId(),organizationId:cur.org_id||void 0})}catch(err){if(err instanceof Error&&/invalid_grant/.test(err.message)){let rescued=readRaw();if(rescued&&stillFresh(rescued))return rescued.jwt;throw new NotLoggedInError("Session expired \u2014 run amp login")}throw err}let claims=decodeJwtPayload(r.access_token),next={jwt:r.access_token,expires_at:expiryFromClaims(claims),refresh_token:r.refresh_token??refreshToken,email:cur.email,org_id:cur.org_id,org_name:cur.org_name};if(write(next),cur.org_id&&claims.org_id!==cur.org_id)throw new NotLoggedInError(`Session organization mismatch \u2014 run amp login --org ${cur.org_id}`);return next.jwt})}var REFRESH_SKEW_MS,NotLoggedInError,init_session=__esm({"src/session.ts"(){"use strict";init_brand(),init_credentials(),init_workos(),init_lock(),init_machine(),init_request_credential(),REFRESH_SKEW_MS=6e4,NotLoggedInError=class extends Error{}}}),http_exports={};__export(http_exports,{DoorRefusalError:()=>DoorRefusalError,doorRequest:()=>doorRequest,reportError:()=>reportError,storeOrigin:()=>storeOrigin});function storeOrigin(){let request=requestCredential();if(request)return request.storeOrigin;let url=bundle().mcpUrl.replace(/\/mcp\/?$/,"");if(!url)throw new Error("Select a tenant with amp login --store <url>.");return url}async function doorRequest(method,path,body){let request=requestCredential(),machine=request?null:machineConfigFromEnv(),headers={},url;if(request)assertRequestReadRoute(method,path),headers.authorization=`Bearer ${request.bearer}`,headers["x-branch-id"]=request.branchId,url=`${request.storeOrigin}${request.lane==="machine"?machinePath(path):path}`;else if(machine)headers.authorization=`Bearer ${await machineToken(machine)}`,url=`${storeOrigin()}${machinePath(path)}`;else{let jwt=await ensureFreshJwt(),creds=readRaw();headers.authorization=`Bearer ${jwt}`,creds?.org_id&&(headers["x-org-id"]=creds.org_id),url=`${storeOrigin()}${path}`}body!==void 0&&(headers["content-type"]="application/json");let res;try{res=await fetch(url,{method,headers,body:body===void 0?void 0:JSON.stringify(body),redirect:"error"})}catch{throw request?new Error("Request credential store read failed; no credential fallback attempted."):new CliError("CONNECTION_FAILED","Cannot reach your workspace. Check its URL and your connection.",!0)}if(!res.ok)throw await res.body?.cancel().catch(()=>{}),httpError(res.status);if(request&&!res.ok)throw await res.body?.cancel().catch(()=>{}),new DoorRefusalError(res.status,"Request credential store read refused.");let text;try{text=await res.text()}catch(err){throw request?new Error("Request credential store response could not be read."):err}if(!res.ok)throw new DoorRefusalError(res.status,"Request refused. Check your session and permissions.");return text}function reportError(err){if(experience().enabled){renderError(err instanceof NotLoggedInError?new CliError("AUTH_REQUIRED","Sign in to your workspace.",!1,"amp login"):err);return}err instanceof DoorRefusalError?(process.stderr.write(`${err.message}:
|
|
13
13
|
`),process.stderr.write(err.body.endsWith(`
|
|
14
14
|
`)?err.body:`${err.body}
|
|
15
15
|
`)):err instanceof NotLoggedInError?process.stderr.write(`${err.message}
|
|
16
16
|
`):process.stderr.write(`${err instanceof Error?err.message:String(err)}
|
|
17
|
-
`),process.exitCode=1}var DoorRefusalError,init_http=__esm({"src/http.ts"(){"use strict";init_experience(),init_brand(),init_credentials(),init_machine(),init_env_customer(),init_session(),init_request_credential(),DoorRefusalError=class extends Error{constructor(status,body){super(`door refused (HTTP ${status})`),this.status=status,this.body=body}}}});init_terminal();var CLI_VERSION="0.6.
|
|
17
|
+
`),process.exitCode=1}var DoorRefusalError,init_http=__esm({"src/http.ts"(){"use strict";init_experience(),init_brand(),init_credentials(),init_machine(),init_env_customer(),init_session(),init_request_credential(),DoorRefusalError=class extends Error{constructor(status,body){super(`door refused (HTTP ${status})`),this.status=status,this.body=body}}}});init_terminal();var CLI_VERSION="0.6.3";init_brand();init_config();init_credentials();init_lock();init_experience();init_brand();init_brand();init_workos();init_credentials();init_env_customer();init_config();init_brand();var MAX_BYTES=65536;function skillUrl(service){let url=new URL(service);if(url.protocol!=="https:"||url.username||url.password||url.search||url.hash||url.pathname!=="/"||!/^(?:[a-z0-9.-]+|\[[a-f0-9:]+\])$/i.test(url.hostname)||/[\s\\]/.test(service))throw new Error("Use an HTTPS Ask service origin without a path or credentials.");return`${url.origin}/skill.md`}var skillVersion=text=>createHash2("sha256").update(text).digest("hex");function validSkill(text){return typeof text=="string"&&Buffer.byteLength(text)<=MAX_BYTES&&/^---\r?\nname: amp-cli\r?\n/.test(text)&&/^description: .+/m.test(text)&&!text.includes("\0")}async function fetchSkill(service,bundled,options={}){let url=skillUrl(service),file=join3(options.cacheDir??join3(homedir3(),".cache","amp","skills"),`${skillVersion(url)}.json`),cached3;try{let raw=readFileSync3(file);if(raw.length<=MAX_BYTES*2){let value=JSON.parse(raw.toString());value.url===url&&validSkill(value.text)&&(value.etag===void 0||typeof value.etag=="string"&&value.etag.length<=200&&!/[\r\n]/.test(value.etag))&&(cached3=value)}}catch{}let result=(text,source,stale,warning)=>({text,source,stale,url,version:skillVersion(text),...warning?{warning}:{}});try{let response=await(options.fetch??fetch)(url,{redirect:"error",credentials:"omit",signal:AbortSignal.timeout(options.timeoutMs??5e3),headers:{Accept:"text/markdown",...cached3?.etag?{"If-None-Match":cached3.etag}:{}}});if(response.status===304&&cached3)return result(cached3.text,"cache",!1);if(response.status!==200||response.redirected||!response.headers.get("content-type")?.startsWith("text/markdown"))throw await response.body?.cancel(),new Error("Invalid skill response");if(Number(response.headers.get("content-length"))>MAX_BYTES)throw await response.body?.cancel(),new Error("Skill too large");let reader=response.body?.getReader();if(!reader)throw new Error("Missing skill body");let chunks=[],bytes=0;try{for(;;){let{done,value}=await reader.read();if(done)break;if(bytes+=value.byteLength,bytes>MAX_BYTES)throw new Error("Skill too large");chunks.push(value)}}finally{await reader.cancel().catch(()=>{})}let text=new TextDecoder("utf-8",{fatal:!0}).decode(Buffer.concat(chunks));if(!validSkill(text))throw new Error("Invalid skill");let etag=response.headers.get("etag"),next={url,text,...etag&&etag.length<=200&&!/[\r\n]/.test(etag)?{etag}:{}},temporary=`${file}.${randomUUID()}.tmp`;try{mkdirSync3(dirname3(file),{recursive:!0,mode:448}),writeFileSync3(temporary,JSON.stringify(next),{mode:384}),renameSync(temporary,file)}catch{return result(text,"remote",!1,"Skill fetched; local cache could not be saved.")}finally{try{rmSync2(temporary,{force:!0})}catch{}}return result(text,"remote",!1)}catch{return result(cached3?.text??bundled(),cached3?"cache":"bundled",!0,"Published skill unavailable; using fallback instructions. Retry in a new chat or on explicit refresh.")}}init_experience();init_brand();init_brand();init_config();init_lock();init_brand();init_brand();init_credentials();var PACKAGE_NAME=NPM_PACKAGE,TTL_MS=1440*60*1e3;function isNewer(current2,candidate){let parse=v=>v.replace(/^v/,"").split("-")[0].split(".").map(n=>Number.parseInt(n,10)||0),a=parse(current2),b=parse(candidate);for(let i=0;i<Math.max(a.length,b.length);i+=1){let x=a[i]??0,y=b[i]??0;if(y>x)return!0;if(y<x)return!1}return!1}function fetchLatest(runner=(cmd,args)=>execFileSync(cmd,args,{encoding:"utf8",timeout:4e3,stdio:["ignore","pipe","ignore"]})){try{let out=runner("npm",["view",PACKAGE_NAME,"version","--json"]).trim(),parsed=JSON.parse(out);return typeof parsed=="string"?parsed:Array.isArray(parsed)&&typeof parsed[parsed.length-1]=="string"?parsed[parsed.length-1]:null}catch{return null}}var HOOK_MARKER="amp --agent skill sync",HOOK_COMMAND=`command -v amp >/dev/null 2>&1 && ${HOOK_MARKER} || true`;function npmInstall(spec){return spawnSync("npm",["install","-g",spec],{stdio:"ignore",timeout:9e4,shell:process.platform==="win32"}).status===0}function ensureLatestCli(opts={}){let env=opts.env??process.env,current2=CLI_VERSION;if(env.AMP_NO_UPDATE_CHECK==="1")return{status:"skipped",current:current2};let latest=(opts.latest??fetchLatest)();return latest?isNewer(current2,latest)?{status:(opts.install??npmInstall)(`${NPM_PACKAGE}@${latest}`)?"updated":"failed",current:current2,latest}:{status:"current",current:current2,latest}:{status:"unknown",current:current2}}function claudeSettingsPath(){return join5(homedir4(),".claude","settings.json")}function installSessionHook(settingsPath=claudeSettingsPath()){if(!existsSync4(dirname5(settingsPath)))return"skipped";let settings={};if(existsSync4(settingsPath))try{let parsed=JSON.parse(readFileSync5(settingsPath,"utf8"));if(!parsed||typeof parsed!="object"||Array.isArray(parsed))return"skipped";settings=parsed}catch{return"skipped"}let hooks=settings.hooks??{};if(!hooks||typeof hooks!="object"||Array.isArray(hooks))return"skipped";let sessionStart=hooks.SessionStart??[];if(!Array.isArray(sessionStart))return"skipped";if(sessionStart.some(group=>Array.isArray(group?.hooks)&&group.hooks.some(h=>typeof h?.command=="string"&&h.command.includes(HOOK_MARKER))))return"present";sessionStart.push({matcher:"startup|clear",hooks:[{type:"command",command:HOOK_COMMAND,timeout:120}]}),settings.hooks={...hooks,SessionStart:sessionStart};let tmp=`${settingsPath}.${process.pid}.tmp`;return writeFileSync5(tmp,`${JSON.stringify(settings,null,2)}
|
|
18
18
|
`),renameSync2(tmp,settingsPath),"installed"}function reexecSync(target){let r=spawnSync("amp",["--agent","skill","sync","--skip-update",...target===defaultInstallPath()?[]:["--dir",dirname5(target)]],{encoding:"utf8",timeout:3e4,shell:process.platform==="win32"});if(r.status!==0)return null;try{return JSON.parse(r.stdout).data}catch{return null}}async function syncSkill(opts={}){let target=opts.target??defaultInstallPath(),lock=join5(dirname5(configPath()),"skill-sync.lock");mkdirSync5(dirname5(lock),{recursive:!0,mode:448});let cli=opts.skipUpdate?{status:"skipped",current:CLI_VERSION}:await withFileLock(lock,async()=>(opts.update??ensureLatestCli)());if(cli.status==="updated"){let child=(opts.reexec??reexecSync)(target);if(child)return{...child,cli}}return withFileLock(lock,async()=>{let result={cli,skill:{path:target,outcome:installSkill(target)}},service=existsSync4(target)?skillService(readFileSync5(target,"utf8")):void 0;if(service){let published=await fetchSkill(service,bundledSkill,{fetch:opts.fetch,cacheDir:opts.cacheDir});result.published={url:published.url,source:published.source,stale:published.stale,version:published.version,...published.warning?{warning:published.warning}:{}}}return result})}var STAMP_PREFIX="<!-- amp-managed skill \xB7 amp ";function bundledSkillPath(){return resolve(dirname6(fileURLToPath(import.meta.url)),"..","skill","SKILL.md")}function bundledSkill(){let p=bundledSkillPath();if(!existsSync5(p))throw new Error("Bundled skill missing. Reinstall the CLI package.");return readFileSync6(p,"utf8")}function defaultInstallPath(){return join6(homedir5(),".claude","skills","amp-cli","SKILL.md")}function stamped(text){return`${STAMP_PREFIX}${CLI_VERSION} \u2014 bundled customer skill; remove this stamp to preserve manual edits -->
|
|
19
19
|
${text}`}function isAmpManaged(text){return text.startsWith(STAMP_PREFIX)}function installSkill(target=defaultInstallPath(),service){let current2=existsSync5(target)?readFileSync6(target,"utf8"):void 0;if(current2!==void 0&&!isAmpManaged(current2))return"kept-hand-edited";let publisher=service??(current2===void 0?void 0:skillService(current2)),next=stamped(publisher?bootstrapSkill(publisher):bundledSkill());return current2!==void 0?(current2===next||writeFileSync6(target,next),"refreshed"):(mkdirSync6(dirname6(target),{recursive:!0}),writeFileSync6(target,next),"installed")}function skillService(text){return text.match(/^<!-- amp-skill-service: (https:\/\/[^\s]+) -->$/m)?.[1]}function bootstrapSkill(service){let url=skillUrl(service),origin2=new URL(url).origin;return`---
|
|
20
20
|
name: amp-cli
|
|
@@ -142,24 +142,31 @@ Workspace: ${profile2?.name??"default"} \u2192 ${profile2?.store_url??bundle().m
|
|
|
142
142
|
`),toJob();throw err}return res.truncated&&opts.limit===void 0?(progress(`Retrieving the complete query result\u2026
|
|
143
143
|
`),toJob()):(res.truncated&&opts.limit!==void 0&&progress(`(result cut at your --limit ${opts.limit}; raise it or use --job for more)
|
|
144
144
|
`),warnIfPartial(res,opts.limit!==void 0),{rows:res.rows??[],rowCount:res.rowCount,truncated:res.truncated})}async function sqlRun(statement,opts){try{readContext();for(let[flag,value,max]of[["limit",opts.limit,1e4],["timeout-ms",opts.timeoutMs,2147483647],["budget-ms",opts.budgetMs,28e4]])if(value!==void 0&&(!/^\d+$/.test(value)||Number(value)<1||Number(value)>max))throw new CliError("INVALID_ARGUMENT",`--${flag} must be a whole number between 1 and ${max}.`);let res=await runSqlAuto(statement,{limit:opts.limit?Number(opts.limit):void 0,timeoutMs:opts.timeoutMs?Number(opts.timeoutMs):void 0,budgetMs:opts.budgetMs?Number(opts.budgetMs):void 0,job:opts.job});emit({rowCount:res.rowCount??res.rows.length,truncated:res.truncated??!1,rows:res.rows},opts,void 0,{row_count:res.rowCount??res.rows.length,truncated:!!res.truncated||(res.rowCount??res.rows.length)>=1e5,...res.truncated||(res.rowCount??res.rows.length)>=1e5?{next_action:"Narrow the query by time range or filters; raise an explicit --limit or use --job where appropriate."}:{}})}catch(err){reportError(err)}}async function sqlJobFetch(jobId,opts){try{readContext();let res=await drainSqlJob(jobId);warnIfPartial(res),emit({rowCount:res.rowCount??res.rows.length,truncated:res.truncated??!1,rows:res.rows},opts,void 0,{row_count:res.rowCount??res.rows.length,truncated:!!res.truncated||(res.rowCount??res.rows.length)>=1e5,...res.truncated||(res.rowCount??res.rows.length)>=1e5?{next_action:"Narrow the query by time range or filters; raise an explicit --limit or use --job where appropriate."}:{}})}catch(err){reportError(err)}}function registerSql(program2){program2.command("sql").description("Run a read-only query against your workspace").argument("[statement]","one SELECT/WITH statement").option("--limit <n>","maximum rows for a quick query (default 1000, max 10000)").option("--timeout-ms <n>","time limit for a quick query in milliseconds").option("--budget-ms <n>","time limit for a background query in milliseconds (default 240000, max 280000)").option("--job","run in the background and wait for results").option("--json","compact JSON to stdout").option("--out <file>","write the full result to a file").action(async(statement,opts)=>{if(!statement){reportError(new CliError("INVALID_ARGUMENT","Provide a SELECT or WITH statement, or resume a query with sql job <id>.",!1,"amp sql --help"));return}await sqlRun(statement,opts)}).command("job <sql-job-id>").description("Resume a query and retrieve its results").option("--json","compact JSON to stdout").option("--out <file>","write the full result to a file").action(async(jobId,opts)=>{await sqlJobFetch(jobId,opts)})}init_terminal();var cleanText=text=>text.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g,"").replace(/\t/g," "),readableText=text=>cleanText(text).replace(/\*\*([^*\n]+)\*\*/g,"$1").replace(/`([^`\n]+)`/g,"$1");function safeLink(url){try{let parsed=new URL(url);return!/[\s\x00-\x1f\x7f-\x9f]/.test(url)&&!parsed.username&&!parsed.password&&(parsed.protocol==="https:"||parsed.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(parsed.hostname))}catch{return!1}}function hyperlink(label,url){return safeLink(url)?`\x1B]8;;${url}\x1B\\${label}\x1B]8;;\x1B\\`:label}function linkedCitations(text,sources){let readable=readableText(text);return readable.replace(/\[(\d+)\]/g,(label,n,offset)=>{if(/(?:^|[\s("'`])\/[^\s()"'`]+$/.test(readable.slice(0,offset)))return label;let url=sources[Number(n)-1]?.url;return url?hyperlink(label,url):label})}function viewRows(answer2,state){let rows=readableText(answer2.text).split(`
|
|
145
|
-
`).map(text=>({text}));return rows.push({text:""},{text:`${state.sources?"\u25BE":"\u25B8"} Sources (${answer2.sources.length})`,action:"sources"}),state.sources&&answer2.sources.forEach((source,index)=>{rows.push({text:` [${index+1}] ${readableText(source.description).replace(/\n/g," ")}`,action:index}),
|
|
145
|
+
`).map(text=>({text}));return rows.push({text:""},{text:`${state.sources?"\u25BE":"\u25B8"} Sources (${answer2.sources.length})`,action:"sources"}),state.sources&&answer2.sources.forEach((source,index)=>{if(rows.push({text:` [${index+1}] ${readableText(source.description).replace(/\n/g," ")}`,action:index}),source.excerpt!==void 0)for(let text of cleanText(source.excerpt).split(`
|
|
146
|
+
`))rows.push({text:` ${text}`});rows.push({text:source.url&&safeLink(source.url)?` Open evidence: ${source.url}`:" Preview unavailable",action:index})}),rows.push({text:`${state.limitations?"\u25BE":"\u25B8"} Limitations (${answer2.limitations.length})`,action:"limitations"}),state.limitations&&answer2.limitations.forEach(text=>rows.push({text:` \u2022 ${readableText(text).replace(/\n/g," ")}`})),rows}function activateRow(state,action){return action==="sources"||action==="limitations"?{...state,[action]:!state[action]}:state}function openLink(url){if(!safeLink(url))return Promise.resolve(!1);let[command,args]=process.platform==="darwin"?["open",[url]]:process.platform==="win32"?["rundll32.exe",["url.dll,FileProtocolHandler",url]]:["xdg-open",[url]];return new Promise(resolve3=>{let child=spawn2(command,args,{shell:!1,stdio:"ignore"}),timer=setTimeout(()=>{child.kill(),resolve3(!1)},1e4);timer.unref(),child.once("close",()=>clearTimeout(timer)),child.once("error",()=>resolve3(!1)),child.once("exit",code=>resolve3(code===0))})}async function showAnswerView(answer2,signal){let input=process.stdin,output=process.stdout;if(!input.isTTY||!output.isTTY||process.env.TERM==="dumb"||signal?.aborted)return;let wasRaw=input.isRaw,wasPaused=input.readableFlowing!==!0,state={sources:!1,limitations:!1,offset:0},active=!0,selected=0,visible=[],pending="",notice="",opening=!1,ink=process.env.NO_COLOR===void 0&&process.env.FORCE_COLOR!=="0",finish,completed=new Promise(resolve3=>{finish=resolve3});function draw(){if(!active)return;let width=Math.max(1,(output.columns||80)-2),height=Math.max(1,(output.rows||24)-4),rows=viewRows(answer2,state).flatMap(row=>{let chars=Array.from(row.text),parts=[];do parts.push({...row,text:chars.splice(0,width).join("")});while(chars.length);return parts});selected=Math.max(0,Math.min(selected,rows.length-1)),selected<state.offset&&(state.offset=selected),selected>=state.offset+height&&(state.offset=selected-height+1),visible=rows.slice(state.offset,state.offset+height);let title=paint("& Answer","accent",ink);output.write("\x1B[H\x1B[2J"+title+`\r
|
|
146
147
|
\r
|
|
147
148
|
`);for(let[i,row]of visible.entries()){let text=linkedCitations(row.text,answer2.sources);output.write((state.offset+i===selected?paint("\u203A ","accent",ink):" ")+text+`\r
|
|
148
|
-
`)}let footer=notice||"\u2191/\u2193 scroll \xB7 Enter/click toggle or open \xB7 s sources \xB7 l limitations \xB7 q close";output.write(`\x1B[${Math.max(1,output.rows||24)};1H`+Array.from(footer).slice(0,width).join(""))}async function activate(row){if(row){if(typeof row.action=="number"){let url=answer2.sources[row.action]?.url;if(url&&!opening){opening=!0;try{notice=await openLink(url)?"Opened evidence in browser":"Could not open browser; copy the URL from Sources"}finally{opening=!1}}}else state=activateRow(state,row.action);draw()}}function data(chunk){for(pending+=chunk.toString("utf8");pending;){if(pending.startsWith("\x1B[<")){let mouse=/^\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(pending);if(!mouse){pending.length>64&&(pending="");return}if(pending=pending.slice(mouse[0].length),mouse[4]==="M"&&Number(mouse[1])===0){let row=Number(mouse[3])-3;if(row>=0&&row<visible.length){selected=state.offset+row;let target=visible[row],action=target.action;if(action===void 0)for(let match of target.text.matchAll(/\[(\d+)\]/g)){let column=Number(mouse[2])-3,start=Array.from(target.text.slice(0,match.index)).length;column>=start&&column<start+match[0].length&&(action=Number(match[1])-1)}activate({...target,action})}}continue}if(pending==="\x1B"||pending==="\x1B[")return;if(pending.startsWith("\x1B[A"))selected--,pending=pending.slice(3);else if(pending.startsWith("\x1B[B"))selected++,pending=pending.slice(3);else{let key=pending[0];if(pending=pending.slice(1),key==="q"||key===""||key===""){finish();return}key==="s"&&(state=activateRow(state,"sources")),key==="l"&&(state=activateRow(state,"limitations")),(key==="\r"||key===" ")&&activate(visible[selected-state.offset])}draw()}}try{input.setRawMode(!0),input.resume(),output.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1006h"),input.on("data",data),input.once("end",finish),input.once("error",finish),process.once("SIGTERM",finish),output.on("resize",draw),signal?.addEventListener("abort",finish,{once:!0}),draw(),await completed}finally{active=!1,input.off("data",data),input.off("end",finish),input.off("error",finish),process.off("SIGTERM",finish),output.off("resize",draw),signal?.removeEventListener("abort",finish),input.setRawMode(wasRaw),wasPaused&&input.pause(),output.write("\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[?1049l")}}init_terminal();init_experience();init_brand();init_config();init_machine();init_request_credential();init_session();
|
|
149
|
+
`)}let footer=notice||"\u2191/\u2193 scroll \xB7 Enter/click toggle or open \xB7 s sources \xB7 l limitations \xB7 q close";output.write(`\x1B[${Math.max(1,output.rows||24)};1H`+Array.from(footer).slice(0,width).join(""))}async function activate(row){if(row){if(typeof row.action=="number"){let url=answer2.sources[row.action]?.url;if(url&&!opening){opening=!0;try{notice=await openLink(url)?"Opened evidence in browser":"Could not open browser; copy the URL from Sources"}finally{opening=!1}}}else state=activateRow(state,row.action);draw()}}function data(chunk){for(pending+=chunk.toString("utf8");pending;){if(pending.startsWith("\x1B[<")){let mouse=/^\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(pending);if(!mouse){pending.length>64&&(pending="");return}if(pending=pending.slice(mouse[0].length),mouse[4]==="M"&&Number(mouse[1])===0){let row=Number(mouse[3])-3;if(row>=0&&row<visible.length){selected=state.offset+row;let target=visible[row],action=target.action;if(action===void 0)for(let match of target.text.matchAll(/\[(\d+)\]/g)){let column=Number(mouse[2])-3,start=Array.from(target.text.slice(0,match.index)).length;column>=start&&column<start+match[0].length&&(action=Number(match[1])-1)}activate({...target,action})}}continue}if(pending==="\x1B"||pending==="\x1B[")return;if(pending.startsWith("\x1B[A"))selected--,pending=pending.slice(3);else if(pending.startsWith("\x1B[B"))selected++,pending=pending.slice(3);else{let key=pending[0];if(pending=pending.slice(1),key==="q"||key===""||key===""){finish();return}key==="s"&&(state=activateRow(state,"sources")),key==="l"&&(state=activateRow(state,"limitations")),(key==="\r"||key===" ")&&activate(visible[selected-state.offset])}draw()}}try{input.setRawMode(!0),input.resume(),output.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1006h"),input.on("data",data),input.once("end",finish),input.once("error",finish),process.once("SIGTERM",finish),output.on("resize",draw),signal?.addEventListener("abort",finish,{once:!0}),draw(),await completed}finally{active=!1,input.off("data",data),input.off("end",finish),input.off("error",finish),process.off("SIGTERM",finish),output.off("resize",draw),signal?.removeEventListener("abort",finish),input.setRawMode(wasRaw),wasPaused&&input.pause(),output.write("\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[?1049l")}}init_terminal();init_experience();init_brand();init_config();init_machine();init_request_credential();init_session();function diagramBlock(answer2){let diagram=answer2.diagram;return!object(diagram)||diagram.format!=="mermaid"||typeof diagram.source!="string"||!diagram.source.trim()?"":`
|
|
150
|
+
|
|
151
|
+
\`\`\`mermaid
|
|
152
|
+
${diagram.source.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g,"").trim()}
|
|
153
|
+
\`\`\``}var defaults={profile:()=>process.env.AMP_MCP_URL?void 0:activeProfile()?.name,timezone:()=>Intl.DateTimeFormat().resolvedOptions().timeZone,fetch:(...args)=>fetch(...args),token:ensureFreshJwt,store:()=>{let selected=process.env.AMP_MCP_URL??activeProfile()?.store_url;if(!selected)throw new CliError("ASK_FAILED","Ask requires a selected tenant profile. Run amp login --store <url> --name <profile>.");return selected.replace(/\/mcp\/?$/,"")},stdout:text=>process.stdout.write(text),stderr:text=>process.stderr.write(text),uuid:randomUUID2},object=value=>!!value&&typeof value=="object"&&!Array.isArray(value),validId=(value,prefix)=>typeof value=="string"&&new RegExp(`^${prefix}_[A-Za-z0-9_-]{1,128}$`).test(value)&&!/[\r\n]/.test(value),terminal=new Set(["completed","failed","cancelled","interrupted"]),statuses=new Set(["idle","active","cancelling","interrupted"]),invalid=()=>new Error("Invalid Ask response."),StreamDisconnected=class extends CliError{constructor(){super("ASK_FAILED","Ask event stream disconnected.",!0)}};function origin(input,allowLocal){if(typeof input!="string"||input.trim()!==input||/[\s\\]/.test(input))throw new CliError("ASK_FAILED","Invalid Ask endpoint.");let url;try{url=new URL(input)}catch{throw new CliError("ASK_FAILED","Invalid Ask endpoint.")}if(url.username||url.password||url.search||url.hash||url.pathname!=="/"||url.protocol!=="https:"&&!(allowLocal&&url.protocol==="http:"&&loopback(url)))throw new CliError("ASK_FAILED","Ask endpoints require HTTPS origins; HTTP is allowed only for an explicitly selected local store.");return url}function loopback(url){return["localhost","127.0.0.1","[::1]"].includes(url.hostname)}function tokenOrg(token){try{if(!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+(?![\s\S])/.test(token))throw invalid();let claims=JSON.parse(Buffer.from(token.split(".")[1],"base64url").toString("utf8"));if(!object(claims)||typeof claims.sub!="string"||!claims.sub.startsWith("user_")||typeof claims.org_id!="string"||!/^org_[A-Za-z0-9]+(?![\s\S])/.test(claims.org_id)||typeof claims.exp!="number"||!Number.isFinite(claims.exp)||claims.exp*1e3<=Date.now()||claims.act!==void 0||claims.impersonator!==void 0||claims.nbf!==void 0&&(typeof claims.nbf!="number"||!Number.isFinite(claims.nbf)||claims.nbf*1e3>Date.now()))throw invalid();return claims.org_id}catch{throw new CliError("ASK_FAILED","Ask requires a valid, unexpired human session. Run amp login.")}}function answer(value){if(!object(value)||typeof value.text!="string"||!Array.isArray(value.evidence)||!Array.isArray(value.limitations)||!value.limitations.every(v=>typeof v=="string")||value.diagram!==void 0&&(!object(value.diagram)||typeof value.diagram.format!="string"||typeof value.diagram.source!="string"))throw invalid();return value}function evidenceDestination(url,type,id){let encoded=encodeURIComponent(id);if(type==="knowledge_file"&&url.pathname==="/knowledge"){let keys=[...url.searchParams.keys()];return keys.length===1&&keys[0]==="version"&&url.searchParams.get("version")===id}if(type==="observation"&&url.pathname===`/people/evidence/observation/${encoded}`){let keys=[...url.searchParams.keys()];if(!keys.length)return!0;if(keys.length!==1||keys[0]!=="tz"||!url.searchParams.get("tz"))return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:url.searchParams.get("tz")}),!0}catch{return!1}}if(type==="observation"&&/^\/people\/[^/]+\/day\/\d{4}-\d{2}-\d{2}$/.test(url.pathname)){let keys=[...url.searchParams.keys()];if(keys.length!==1)return!1;let value=url.searchParams.get(keys[0]);if(keys[0]==="at"){let ms=Date.parse(value);return!Number.isNaN(ms)&&new Date(ms).toISOString()===value}return(keys[0]==="minute"||keys[0]==="stream")&&value===id}return url.search?!1:type==="process"?url.pathname===`/processes/${encoded}`:type==="workflow_run"?url.pathname===`/runs/${encoded}`:type==="process_instance"&&/^\/processes\/[^/]+\/instances\/[^/]+$/.test(url.pathname)&&url.pathname.endsWith(`/instances/${encoded}`)}function evidenceUrl(ref){if(!(!object(ref)||typeof ref.object_type!="string"||typeof ref.object_id!="string"||!object(ref.preview)||ref.preview.status!=="available"||typeof ref.preview.url!="string"))try{let url=new URL(ref.preview.url);if(!/[\s\x00-\x1f\x7f-\x9f]/.test(ref.preview.url)&&!url.username&&!url.password&&!url.hash&&(url.protocol==="https:"||url.protocol==="http:"&&loopback(url))&&evidenceDestination(url,ref.object_type,ref.object_id))return url.toString()}catch{}}function formatAskAnswer(value){let clean3=text=>text.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g,""),lines=[clean3(value.text)+diagramBlock(value)];return value.evidence.length&&(lines.push("","Sources"),value.evidence.forEach((ref,index)=>{if(!object(ref)||typeof ref.object_type!="string"||typeof ref.object_id!="string"||typeof ref.description!="string")return;lines.push(`[${index+1}] ${clean3(ref.description)} (${clean3(ref.object_type)}: ${clean3(ref.object_id)})`),ref.source==="memory"&&object(ref.history)&&typeof ref.history.excerpt=="string"&&lines.push(` ${clean3(ref.history.excerpt)}`);let url=evidenceUrl(ref);url?lines.push(` Open evidence: ${url}`):lines.push(" Preview unavailable")})),value.limitations.length&&lines.push("","Limitations",...value.limitations.map(item=>`- ${clean3(item)}`)),`${lines.join(`
|
|
149
154
|
`)}
|
|
150
155
|
`}function snapshot(value,conversation){if(!object(value)||!object(value.conversation)||value.conversation.id!==conversation||typeof value.status!="string"||!statuses.has(value.status)||value.active_request_id!==null&&!validId(value.active_request_id,"req")||!object(value.transcript)||value.transcript.conversation_id!==conversation||value.transcript.version!==1||!Array.isArray(value.transcript.entries))throw invalid();for(let entry of value.transcript.entries){if(!object(entry)||!validId(entry.request_id,"req")||entry.outcome!==void 0&&(typeof entry.outcome!="string"||!terminal.has(entry.outcome)))throw invalid();entry.outcome==="completed"&&answer(entry.answer)}return value}async function readChunk(reader,signal,timeout=6e4){let timer,abort;try{return await Promise.race([reader.read(),new Promise((_,reject)=>{abort=()=>reject(new Error("Ask connection interrupted.")),timer=setTimeout(()=>reject(new Error("Ask connection timed out.")),timeout),signal.addEventListener("abort",abort,{once:!0}),signal.aborted&&abort()})])}finally{clearTimeout(timer),abort&&signal.removeEventListener("abort",abort)}}async function jsonBody(response,signal,limit){if(!response.body)throw invalid();let reader=response.body.getReader(),chunks=[],size=0,deadline=Date.now()+3e4;try{for(;;){let{done,value}=await readChunk(reader,signal,Math.max(1,deadline-Date.now()));if(done)break;if(size+=value.byteLength,size>limit)throw new CliError("ASK_FAILED","Ask response exceeds the byte limit.");chunks.push(value)}try{return JSON.parse(Buffer.concat(chunks).toString("utf8"))}catch{throw invalid()}}finally{reader.cancel().catch(()=>{}),reader.releaseLock()}}async function events(response,conversation,request,io,signal){if(!response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream")||!response.body)throw invalid();let reader=response.body.getReader(),decoder=new TextDecoder("utf-8",{fatal:!0}),pending="",frameBytes=0,data=[],eventType="",sequence=-1;try{for(;;){let{done,value}=await readChunk(reader,signal).catch(()=>{throw new StreamDisconnected});if(done)throw new StreamDisconnected;for(let offset=0;offset<value.length;offset+=4096){pending+=decoder.decode(value.subarray(offset,offset+4096),{stream:!0});let newline;for(;(newline=pending.indexOf(`
|
|
151
156
|
`))>=0;){let line=pending.slice(0,newline);if(pending=pending.slice(newline+1),frameBytes+=Buffer.byteLength(line)+1,frameBytes>65536)throw new CliError("ASK_FAILED","Ask event exceeds the byte limit.");if(line.endsWith("\r")&&(line=line.slice(0,-1)),line===""){if(data.length){let event;try{event=JSON.parse(data.join(`
|
|
152
|
-
`))}catch{throw invalid()}if(!object(event)||event.conversation_id!==conversation||event.request_id!==request||typeof event.sequence!="number"||!Number.isSafeInteger(event.sequence)||event.sequence<=sequence||event.type!==eventType)throw invalid();switch(sequence=event.sequence,event.type){case"progress":if(typeof event.message!="string")throw invalid();let message=event.message.replace(/[\x00-\x1f\x7f-\x9f]/g," ").slice(0,1e3);io.
|
|
153
|
-
`)
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
`))}catch{throw invalid()}if(!object(event)||event.conversation_id!==conversation||event.request_id!==request||typeof event.sequence!="number"||!Number.isSafeInteger(event.sequence)||event.sequence<=sequence||event.type!==eventType)throw invalid();switch(sequence=event.sequence,event.type){case"progress":{if(typeof event.message!="string")throw invalid();let message=event.message.replace(/[\x00-\x1f\x7f-\x9f]/g," ").slice(0,1e3);event.kind==="note"?io.note?io.note(message):io.stderr(` \xB7 ${message}
|
|
158
|
+
`):io.progress?io.progress(message):io.stderr(`${message}
|
|
159
|
+
`);break}case"answer_delta":if(typeof event.text!="string")throw invalid();break;case"status":if(typeof event.status!="string"||!statuses.has(event.status))throw invalid();if(event.status==="active"&&io.progress?.("Running \u2014 waiting for progress"),event.status==="cancelling"&&io.progress?.("Stopping work"),event.status==="idle"||event.status==="interrupted")return;break;case"completed":answer(event.answer);return;case"failed":return object(event.error)&&["unsupported_answer","evidence_unavailable"].includes(String(event.error.code))?String(event.error.code):void 0;case"cancelled":return;default:throw invalid()}}frameBytes=0,data=[],eventType=""}else if(line.startsWith("data:"))data.push(line.slice(5).replace(/^ /,""));else if(line.startsWith("event:"))eventType=line.slice(6).replace(/^ /,"");else if(!line.startsWith(":")&&!line.startsWith("id:")&&!line.startsWith("retry:"))throw invalid()}if(frameBytes+Buffer.byteLength(pending)>65536)throw new CliError("ASK_FAILED","Ask event exceeds the byte limit.")}}}finally{reader.cancel().catch(()=>{}),reader.releaseLock()}}async function ask(question,options,injected={}){if(requestCredentialConfigured()||machineConfigFromEnv())throw new CliError("ASK_FAILED","Ask requires a saved human session; machine and request credentials cannot invoke Ask.");if(options.transcript&&(question!==void 0||!options.conversation||options.watch||options.cancel||options.idempotencyKey))throw new CliError("ASK_FAILED","Use --transcript with --conversation and no question, watch or cancel.");if(options.watch&&options.cancel||(options.watch||options.cancel)&&question!==void 0||(options.watch||options.cancel)&&!options.conversation||options.idempotencyKey!==void 0&&(options.watch||options.cancel)||options.conversation!==void 0&&!validId(options.conversation,"conv"))throw new CliError("ASK_FAILED","Use a question, --watch, or --cancel. Watch/cancel require --conversation conv_<id>.");if(!options.transcript&&!options.watch&&!options.cancel&&(typeof question!="string"||!question.trim()||Buffer.byteLength(question)>32768))throw new CliError("ASK_FAILED","Ask requires a nonempty question of at most 32768 UTF-8 bytes.");if(options.idempotencyKey!==void 0&&(!/^[\x21-\x7e]{1,128}$/.test(options.idempotencyKey)||/[\r\n]/.test(options.idempotencyKey)))throw new CliError("ASK_FAILED","Invalid idempotency key.");let io={...defaults,...injected};experience().quiet&&(io.stderr=()=>{});let structured=(data,meta={})=>`${JSON.stringify(experience().agent?envelope(data,meta):data)}
|
|
160
|
+
`,machineOutput=options.transcript||options.json||experience().json||experience().agent,interactive=!machineOutput&&!experience().quiet&&process.env.TERM!=="dumb"&&(io.interactive??(!!process.stderr.isTTY&&!!process.stdout.isTTY)),status=interactive?askProgress(io.stderr,{color:colorEnabled(process.env,!!process.stderr.isTTY)}):void 0,lastStage="";io.progress=message=>{status?status.update(message):message!==lastStage&&(io.stderr(`${message}
|
|
161
|
+
`),lastStage=message)},io.note=message=>{status?status.note(` \xB7 ${message}`):io.stderr(` \xB7 ${message}
|
|
162
|
+
`)};let submissionKey,aborter=new AbortController,onAbort=()=>aborter.abort();io.signal?.addEventListener("abort",onAbort,{once:!0}),io.signal?.aborted&&aborter.abort();let conversation=options.conversation;try{let firstToken=await io.token(),org=tokenOrg(firstToken),store=origin(io.store(),!0),profile=io.profile?.()??null,request=async(url,method="GET",body,key,stream=!1)=>{let token=await io.token();if(tokenOrg(token)!==org)throw new CliError("ASK_FAILED","Human session changed tenant; refusing Ask request.");let timeout=setTimeout(()=>aborter.abort(),15e3),response;try{for(let attempt=0;response=await io.fetch(url,{method,redirect:"error",signal:aborter.signal,headers:{Authorization:`Bearer ${token}`,Accept:stream?"text/event-stream":"application/json",...body===void 0?{}:{"Content-Type":"application/json"},...key?{"Idempotency-Key":key}:{}},...body===void 0?{}:{body:JSON.stringify(body)}}),!(method!=="GET"||![429,502,503,504].includes(response.status)||attempt>=3);attempt++){let retryAfter=response.headers.get("retry-after"),hint=retryAfter===null?0:/^\d+(?:\.\d+)?$/.test(retryAfter)?Number(retryAfter)*1e3:Math.max(0,Date.parse(retryAfter)-Date.now()),pause=Math.max(250*2**attempt,Number.isFinite(hint)?hint:0);if(pause>3e3)break;await response.body?.cancel().catch(()=>{}),io.progress?.(response.status===429?"Ask read throttled; retrying":"Ask read temporarily unavailable; retrying"),await delay(pause,void 0,{signal:aborter.signal})}}catch{throw new CliError("ASK_FAILED","Ask connection unavailable or interrupted.")}finally{clearTimeout(timeout)}if(response.redirected||response.status>=300&&response.status<400)throw response.body?.cancel().catch(()=>{}),new CliError("ASK_FAILED","Ask redirects are forbidden.");if(!response.ok)throw response.body?.cancel().catch(()=>{}),response.status!==409?httpError(response.status):response.status===409?new CliError("ASK_FAILED","Ask conflict (409): conversation_busy or idempotency_conflict; watch the existing conversation or retry the original input/key."):response.status===401?new CliError("ASK_FAILED","Ask authentication expired or revoked. Run amp login, then reattach."):new CliError("ASK_FAILED",`Ask request refused (HTTP ${response.status}).`);return response},config=await jsonBody(await request(`${store.origin}/v1/ask/config`),aborter.signal,65536);if(!object(config)||config.org_id!==org||config.protocol_version!==1)throw new CliError("ASK_FAILED","Ask discovery does not match this session's tenant/protocol.");let endpoint=origin(config.endpoint,loopback(store)).origin,route=()=>`${endpoint}/v1/conversations/${conversation}`,requestId;if(options.transcript){let saved=snapshot(await jsonBody(await request(route()),aborter.signal,4259840),conversation);status?.stop(),io.stdout(structured(saved));return}if(options.cancel){let response=await request(`${route()}/cancel`,"POST",{});if(response.status!==202)throw invalid();let cancelled=await jsonBody(response,aborter.signal,65536);if(!object(cancelled)||cancelled.request_id!==null&&!validId(cancelled.request_id,"req"))throw invalid();status?.stop(),machineOutput?io.stdout(structured({conversation_id:conversation,...cancelled})):io.stderr(`Cancellation requested for ${conversation}; work may still be stopping.
|
|
156
163
|
Check: amp ask --watch --conversation ${conversation}
|
|
157
164
|
`);return}if(!options.watch){let key=options.idempotencyKey??io.uuid();submissionKey=key,interactive||io.stderr(`Idempotency key: ${key}
|
|
158
165
|
`);let requestTimezone;try{let detected=options.timezone??io.timezone?.();if(detected){if(detected.length>100||!/^[A-Za-z_]+(?:\/[A-Za-z0-9_+.-]+)*(?![\s\S])/.test(detected))throw new Error;requestTimezone=new Intl.DateTimeFormat("en-US",{timeZone:detected}).resolvedOptions().timeZone}else if(options.timezone!==void 0)throw new Error}catch{if(options.timezone!==void 0)throw new CliError("ASK_FAILED","Use a valid IANA timezone, such as America/Los_Angeles.")}let response=await request(`${endpoint}/v1/ask`,"POST",{question,...requestTimezone?{timezone:requestTimezone}:{},...conversation?{conversation_id:conversation}:{}},key);if(response.status!==202)throw invalid();let accepted=await jsonBody(response,aborter.signal,65536);if(!object(accepted)||!validId(accepted.conversation_id,"conv")||!validId(accepted.request_id,"req")||conversation&&accepted.conversation_id!==conversation)throw invalid();if(conversation=accepted.conversation_id,requestId=accepted.request_id,io.progress?.("Question accepted"),!machineOutput){let hint=`You can leave this terminal; the request will continue.
|
|
159
166
|
Resume: amp ask --watch --conversation ${conversation}
|
|
160
167
|
Cancel: amp ask --cancel --conversation ${conversation}`;status?status.note(hint):io.stderr(hint+`
|
|
161
168
|
`)}}interactive||io.stderr(`Conversation: ${conversation}${requestId?` \xB7 Request: ${requestId}`:""}
|
|
162
|
-
`);let get=async()=>snapshot(await jsonBody(await request(route()),aborter.signal,4259840),conversation),view=await get();if(requestId??=view.active_request_id??view.transcript.entries.at(-1)?.request_id,!requestId)throw new CliError("ASK_FAILED","Conversation has no saved question.");let find=()=>view.transcript.entries.find(entry2=>entry2.request_id===requestId),failureCode;if(!find()?.outcome)for(let attempt=0;;attempt++)try{failureCode=await events(await request(`${route()}/events`,"GET",void 0,void 0,!0),conversation,requestId,io,aborter.signal),view=await get();break}catch(error){if(!(error instanceof StreamDisconnected)||aborter.signal.aborted||attempt>=2)throw error;if(io.progress?.("Connection lost \u2014 reconnecting to the same request"),view=await get(),find()?.outcome)break}let entry=find();if(!entry?.outcome)throw new CliError("ASK_FAILED","Answer is not persisted yet; reattach to check current status.");if(entry.outcome==="failed"&&failureCode)throw new CliError("ASK_FAILED",failureCode==="unsupported_answer"?"Ask could not produce an answer with valid evidence. Try narrowing your question.":"Evidence could not be verified. Please retry.");if(entry.outcome!=="completed")throw new CliError("ASK_FAILED",`Ask request ${entry.outcome}; conversation history is preserved.`);let finalAnswer=answer(entry.answer);if(status?.stop(),interactive&&process.stdin.isTTY&&process.stdout.isTTY&&injected.stdout===void 0&&!options.plain){let model={text:finalAnswer.text,limitations:finalAnswer.limitations,sources:finalAnswer.evidence.map(ref=>({description:object(ref)&&typeof ref.description=="string"?ref.description:"Source unavailable",url:evidenceUrl(ref)}))};await showAnswerView(model,aborter.signal),io.stdout(`
|
|
169
|
+
`);let get=async()=>snapshot(await jsonBody(await request(route()),aborter.signal,4259840),conversation),view=await get();if(view.status==="active"&&io.progress?.("Running \u2014 waiting for progress"),requestId??=view.active_request_id??view.transcript.entries.at(-1)?.request_id,!requestId)throw new CliError("ASK_FAILED","Conversation has no saved question.");let find=()=>view.transcript.entries.find(entry2=>entry2.request_id===requestId),failureCode;if(!find()?.outcome)for(let attempt=0;;attempt++)try{failureCode=await events(await request(`${route()}/events`,"GET",void 0,void 0,!0),conversation,requestId,io,aborter.signal),view=await get();break}catch(error){if(!(error instanceof StreamDisconnected)||aborter.signal.aborted||attempt>=2)throw error;if(io.progress?.("Connection lost \u2014 reconnecting to the same request"),view=await get(),find()?.outcome)break}let entry=find();if(!entry?.outcome)throw new CliError("ASK_FAILED","Answer is not persisted yet; reattach to check current status.");if(entry.outcome==="failed"&&failureCode)throw new CliError("ASK_FAILED",failureCode==="unsupported_answer"?"Ask could not produce an answer with valid evidence. Try narrowing your question.":"Evidence could not be verified. Please retry.");if(entry.outcome!=="completed")throw new CliError("ASK_FAILED",`Ask request ${entry.outcome}; conversation history is preserved.`);let finalAnswer=answer(entry.answer);if(status?.stop(),interactive&&process.stdin.isTTY&&process.stdout.isTTY&&injected.stdout===void 0&&!options.plain){let model={text:finalAnswer.text+diagramBlock(finalAnswer),limitations:finalAnswer.limitations,sources:finalAnswer.evidence.map(ref=>({description:object(ref)&&typeof ref.description=="string"?ref.description:"Source unavailable",url:evidenceUrl(ref),...object(ref)&&ref.source==="memory"&&object(ref.history)&&typeof ref.history.excerpt=="string"?{excerpt:ref.history.excerpt}:{}}))};await showAnswerView(model,aborter.signal),io.stdout(`
|
|
163
170
|
${paint("&","accent")} ${paint("Answer","strong")}
|
|
164
171
|
|
|
165
172
|
${linkedCitations(model.text,model.sources)}
|
|
@@ -172,7 +179,7 @@ ${paint(`Conversation: ${conversation}`,"muted")}
|
|
|
172
179
|
`)}catch(error){if(status?.stop(),interactive&&!conversation&&submissionKey&&io.stderr(`Retry the same question with --idempotency-key ${submissionKey}
|
|
173
180
|
`),conversation&&io.stderr(`Reattach: amp ask --watch --conversation ${conversation}
|
|
174
181
|
Disconnecting does not cancel work.
|
|
175
|
-
`),experience().enabled&&conversation){let failure=error instanceof CliError?error:new CliError("ASK_INTERRUPTED","The answer is not available yet. Resume the conversation; disconnecting does not cancel work.");throw failure.nextCommand??=`amp ask --watch --conversation ${conversation}`,failure.details={...failure.details,conversation_id:conversation,resume_command:`amp ask --watch --conversation ${conversation}`},failure}throw error}finally{status?.stop(),io.signal?.removeEventListener("abort",onAbort),aborter.abort()}}function registerAsk(program2){program2.command("ask [question]").description("Ask a question about your workspace data").option("--plain","Print the full answer and sources without the interactive reader").option("--timezone <zone>","Timezone for dates (defaults to your computer timezone)").option("--conversation <id>","continue or attach to a conversation in your tenant").option("--watch","reattach to current status or the latest saved answer").option("--cancel","explicitly cancel current work, preserving history").option("--json","write one final structured answer to stdout").option("--idempotency-key <key>","retry the exact same submission without launching twice").action(async(question,opts)=>{let controller=new AbortController,interrupt=()=>controller.abort();process.once("SIGINT",interrupt),process.once("SIGTERM",interrupt);try{await ask(question,opts,{signal:controller.signal})}finally{process.removeListener("SIGINT",interrupt),process.removeListener("SIGTERM",interrupt)}})}init_brand();init_experience();init_machine();init_request_credential();var USAGE_VIEWS=["summary","users","trend","questions","failures","conversation"],object2=value=>!!value&&typeof value=="object"&&!Array.isArray(value),MAX_BYTES2=16*1048576;function relative(value,now){let match=/^(\d{1,3})([dwm])$/.exec(value);if(!match)return value;let days=Number(match[1])*{d:1,w:7,m:30}[match[2]];return new Date(now-days*864e5).toISOString()}function usageBody(view,options,timezone,now=Date.now()){let limit=options.limit===void 0?void 0:Number(options.limit);if(limit!==void 0&&(!Number.isSafeInteger(limit)||limit<1||limit>1e3))throw new CliError("INVALID_ARGUMENT","--limit must be an integer from 1 to 1000.");if(options.by!==void 0&&!["day","week","month"].includes(options.by))throw new CliError("INVALID_ARGUMENT","--by must be day, week or month.");return{view,...options.since?{since:relative(options.since,now)}:{},...options.until?{until:relative(options.until,now)}:{},...options.user?.length?{users:options.user}:{},...options.by?{by:options.by}:{},...timezone?{timezone}:{},...limit!==void 0?{limit}:{},...options.withAnswers?{with_answers:!0}:{}}}var pct=value=>typeof value=="number"?`${Math.round(value*1e3)/10}%`:"\u2014",secs=value=>typeof value=="number"?`${Math.round(value/100)/10}s`:"\u2014",who=row=>String(row.display_name??row.email??row.user_id??""),clean2=value=>String(value??"").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g," ");function formatUsage(view,data){let rows=Array.isArray(data.rows)?data.rows.filter(object2):[],coverage=data.complete===!1?`
|
|
182
|
+
`),experience().enabled&&conversation){let failure=error instanceof CliError?error:new CliError("ASK_INTERRUPTED","The answer is not available yet. Resume the conversation; disconnecting does not cancel work.");throw failure.nextCommand??=`amp ask --watch --conversation ${conversation}`,failure.details={...failure.details,conversation_id:conversation,resume_command:`amp ask --watch --conversation ${conversation}`},failure}throw error}finally{status?.stop(),io.signal?.removeEventListener("abort",onAbort),aborter.abort()}}function registerAsk(program2){program2.command("ask [question]").description("Ask a question about your workspace data").option("--plain","Print the full answer and sources without the interactive reader").option("--timezone <zone>","Timezone for dates (defaults to your computer timezone)").option("--conversation <id>","continue or attach to a conversation in your tenant").option("--transcript","export saved conversation JSON for trace coverage auditing (requires --conversation)").option("--watch","reattach to current status or the latest saved answer").option("--cancel","explicitly cancel current work, preserving history").option("--json","write one final structured answer to stdout").option("--idempotency-key <key>","retry the exact same submission without launching twice").action(async(question,opts)=>{let controller=new AbortController,interrupt=()=>controller.abort();process.once("SIGINT",interrupt),process.once("SIGTERM",interrupt);try{await ask(question,opts,{signal:controller.signal})}finally{process.removeListener("SIGINT",interrupt),process.removeListener("SIGTERM",interrupt)}})}init_brand();init_experience();init_machine();init_request_credential();var USAGE_VIEWS=["summary","users","trend","questions","failures","conversation","cost"],object2=value=>!!value&&typeof value=="object"&&!Array.isArray(value),MAX_BYTES2=16*1048576;function relative(value,now){let match=/^(\d{1,3})([dwm])$/.exec(value);if(!match)return value;let days=Number(match[1])*{d:1,w:7,m:30}[match[2]];return new Date(now-days*864e5).toISOString()}function costWindow(options,now=Date.now()){let parse=(value,fallback,flag)=>{if(value===void 0)return fallback;let ms=Date.parse(relative(value,now));if(!Number.isFinite(ms))throw new CliError("INVALID_ARGUMENT",`${flag} must be a date, an ISO time with a zone, or 7d/4w/3m.`);return ms},until=parse(options.until,now,"--until"),since=parse(options.since,until-30*864e5,"--since");if(since>=until)throw new CliError("INVALID_ARGUMENT","--since must be before --until.");return{since,until}}function usageBody(view,options,timezone,now=Date.now()){let limit=options.limit===void 0?void 0:Number(options.limit);if(limit!==void 0&&(!Number.isSafeInteger(limit)||limit<1||limit>1e3))throw new CliError("INVALID_ARGUMENT","--limit must be an integer from 1 to 1000.");if(options.by!==void 0&&!["day","week","month"].includes(options.by))throw new CliError("INVALID_ARGUMENT","--by must be day, week or month.");return{view,...options.since?{since:relative(options.since,now)}:{},...options.until?{until:relative(options.until,now)}:{},...options.user?.length?{users:options.user}:{},...options.by?{by:options.by}:{},...timezone?{timezone}:{},...limit!==void 0?{limit}:{},...options.withAnswers?{with_answers:!0}:{}}}var usd=value=>typeof value=="number"?`$${value.toFixed(value<1?4:2)}`:"\u2014",SERVICE_LABEL={AmazonBedrock:"Bedrock (tokens)",Anthropic:"Anthropic (tokens)",OpenAI:"OpenAI (tokens)",AWSLambda:"Sandbox VMs (Lambda)"},pct=value=>typeof value=="number"?`${Math.round(value*1e3)/10}%`:"\u2014",secs=value=>typeof value=="number"?`${Math.round(value/100)/10}s`:"\u2014",who=row=>String(row.display_name??row.email??row.user_id??""),clean2=value=>String(value??"").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g," ");function formatUsage(view,data){let rows=Array.isArray(data.rows)?data.rows.filter(object2):[],coverage=data.complete===!1?`
|
|
176
183
|
Incomplete: ${data.truncated?"the read was truncated":""}${data.truncated&&Number(data.unreadable_transcripts)?"; ":""}${Number(data.unreadable_transcripts)?`${data.unreadable_transcripts} transcript(s) could not be read`:""}.`:"",range=data.since&&data.until?`${String(data.since).slice(0,10)} \u2192 ${String(data.until).slice(0,10)}`:"";switch(view){case"summary":return[`Ask usage ${range}`,`${data.turns} questions in ${data.conversations} conversations from ${data.active_users} people`,`answered ${data.completed} \xB7 failed ${data.failed} \xB7 interrupted ${data.interrupted} \xB7 cancelled ${data.cancelled} \xB7 in flight ${data.in_flight}`,`success ${pct(data.success_rate)} \xB7 p50 ${secs(data.latency_p50_ms)} \xB7 p95 ${secs(data.latency_p95_ms)}`].join(`
|
|
177
184
|
`)+coverage;case"users":return humanTable(rows.map(row=>({person:who(row),email:row.email??"",questions:row.turns,conversations:row.conversations,success:pct(row.success_rate),p95:secs(row.latency_p95_ms),last:String(row.last_asked_at??"").slice(0,16)})))+coverage;case"trend":return humanTable(rows.map(row=>({[String(data.by??"period")]:row.period,questions:row.turns,started:row.conversations_started,people:row.active_users,failed:Number(row.failed)+Number(row.interrupted),success:pct(row.success_rate),p50:secs(row.latency_p50_ms)})))+coverage;case"questions":return rows.map(row=>{let head=`${String(row.accepted_at).slice(0,16).replace("T"," ")} ${who(row)} ${row.outcome}${typeof row.latency_ms=="number"?` in ${secs(row.latency_ms)}`:""} ${row.conversation_id}`,answer2=object2(row.answer)&&typeof row.answer.text=="string"?`
|
|
178
185
|
\u2192 ${clean2(row.answer.text).slice(0,400)}`:"";return`${head}
|
|
@@ -184,12 +191,17 @@ ${rows.length} of ${data.total} questions${coverage}`;case"failures":return[`Fai
|
|
|
184
191
|
${row.outcome}: ${row.count}`,...(Array.isArray(row.recent)?row.recent.filter(object2):[]).map(item=>` ${String(item.accepted_at).slice(0,16).replace("T"," ")} ${who(item)} ${clean2(item.question).slice(0,120)}`)].join(`
|
|
185
192
|
`)),typeof data.note=="string"?`
|
|
186
193
|
${data.note}`:""].join(`
|
|
187
|
-
`)+coverage;case"
|
|
194
|
+
`)+coverage;case"cost":{let services=Array.isArray(data.by_service)?data.by_service.filter(object2):[],conversations=Array.isArray(data.by_conversation)?data.by_conversation.filter(object2):[],total=services.reduce((sum,row)=>sum+(typeof row.usd=="number"?row.usd:0),0);return[`Spend ${range} \xB7 ${usd(total)} total`,humanTable(services.map(row=>({service:SERVICE_LABEL[String(row.service)]??String(row.service??"uncarded"),usd:usd(row.usd),tokens_in:row.tokens_in,tokens_out:row.tokens_out}))),conversations.length?`
|
|
195
|
+
Top conversations (tokens + sandbox)`:`
|
|
196
|
+
No conversation-attributed spend in this range.`,conversations.length?humanTable(conversations.map(row=>({conversation:row.conversation_id??"(not tied to a conversation)",usd:usd(row.usd)}))):""].join(`
|
|
197
|
+
`)}case"conversation":{let transcript=object2(data.transcript)&&Array.isArray(data.transcript.entries)?data.transcript.entries.filter(object2):[],conversation=object2(data.conversation)?data.conversation:{};return[`${conversation.id??""} \xB7 ${clean2(conversation.title)} \xB7 ${data.status}`,...transcript.map((entry,index)=>{let answer2=object2(entry.answer)&&typeof entry.answer.text=="string"?formatAskAnswer(entry.answer):`(${entry.outcome??"in flight"})`;return`
|
|
188
198
|
[${index+1}] ${String(entry.accepted_at??"").slice(0,16).replace("T"," ")} Q: ${clean2(entry.question)}
|
|
189
199
|
${answer2}`})].join(`
|
|
190
|
-
`)}}}async function usage(view,id,options,injected={}){if(requestCredentialConfigured()||machineConfigFromEnv())throw new CliError("INVALID_ARGUMENT","Usage requires a saved human session; machine and request credentials cannot read Ask usage.");if(!USAGE_VIEWS.includes(view))throw new CliError("INVALID_ARGUMENT",`Unknown view "${view}". Use one of: ${USAGE_VIEWS.join(", ")}.`,!1,"amp usage --help");if(view==="conversation"?!id||!/^conv_[A-Za-z0-9_-]{1,128}$/.test(id):id!==void 0)throw new CliError("INVALID_ARGUMENT",view==="conversation"?"Give a conversation id: usage conversation conv_<id>.":"Only the conversation view takes an id.");let io={...defaults,...injected},first=await io.token(),org=tokenOrg(first),store=origin(io.store(),!0),request=async(url,method,body)=>{let token=await io.token();if(tokenOrg(token)!==org)throw new CliError("ASK_FAILED","Human session changed tenant; refusing usage request.");let controller=new AbortController,timeout=setTimeout(()=>controller.abort(),12e4),response;try{response=await io.fetch(url,{method,redirect:"error",signal:controller.signal,headers:{Authorization:`Bearer ${token}`,Accept:"application/json",...body===void 0?{}:{"Content-Type":"application/json"}},...body===void 0?{}:{body:JSON.stringify(body)}})}catch{throw new CliError("ASK_FAILED","Ask connection unavailable or interrupted.",!0)}finally{clearTimeout(timeout)}if(!response.ok)throw response.body?.cancel().catch(()=>{}),response.status===403?new CliError("ACCESS_DENIED"
|
|
200
|
+
`)}}}async function usage(view,id,options,injected={}){if(requestCredentialConfigured()||machineConfigFromEnv())throw new CliError("INVALID_ARGUMENT","Usage requires a saved human session; machine and request credentials cannot read Ask usage.");if(!USAGE_VIEWS.includes(view))throw new CliError("INVALID_ARGUMENT",`Unknown view "${view}". Use one of: ${USAGE_VIEWS.join(", ")}.`,!1,"amp usage --help");if(view==="cost"&&(options.user?.length||options.by||options.withAnswers))throw new CliError("INVALID_ARGUMENT","The cost view takes only --since, --until, --timezone, --limit and --json.");if(view==="conversation"?!id||!/^conv_[A-Za-z0-9_-]{1,128}$/.test(id):id!==void 0)throw new CliError("INVALID_ARGUMENT",view==="conversation"?"Give a conversation id: usage conversation conv_<id>.":"Only the conversation view takes an id.");let io={...defaults,...injected},first=await io.token(),org=tokenOrg(first),store=origin(io.store(),!0),request=async(url,method,body)=>{let token=await io.token();if(tokenOrg(token)!==org)throw new CliError("ASK_FAILED","Human session changed tenant; refusing usage request.");let controller=new AbortController,timeout=setTimeout(()=>controller.abort(),12e4),response;try{response=await io.fetch(url,{method,redirect:"error",signal:controller.signal,headers:{Authorization:`Bearer ${token}`,Accept:"application/json","x-org-id":org,...body===void 0?{}:{"Content-Type":"application/json"}},...body===void 0?{}:{body:JSON.stringify(body)}})}catch{throw new CliError("ASK_FAILED","Ask connection unavailable or interrupted.",!0)}finally{clearTimeout(timeout)}if(!response.ok)throw response.body?.cancel().catch(()=>{}),response.status===403?new CliError("ACCESS_DENIED",`${view==="cost"?"Cost":"Ask usage"} is admin-only. Ask your workspace administrator for access.`,!1,"amp login --status"):response.status===400?new CliError("INVALID_ARGUMENT","The usage request was rejected. Check --since/--until (a date, an ISO time with a zone, or 7d/4w/3m), --by and --user.",!1,"amp usage --help"):httpError(response.status);let text=await response.text();if(Buffer.byteLength(text)>MAX_BYTES2)throw new CliError("ASK_FAILED","Ask usage response exceeds the supported size; narrow --since/--until or --limit.");try{return JSON.parse(text)}catch{throw new CliError("ASK_FAILED","Invalid Ask response.")}};if(view==="cost"){let window=costWindow(options),tz=options.timezone??io.timezone?.(),limit=options.limit===void 0?25:Number(options.limit);if(!Number.isSafeInteger(limit)||limit<1||limit>1e3)throw new CliError("INVALID_ARGUMENT","--limit must be an integer from 1 to 1000.");let rollup=async groupBy=>{let params=new URLSearchParams({group_by:groupBy,granularity:"daily",since:String(window.since),until:String(window.until),...tz?{tz}:{}}),body=await request(`${store.origin}/v1/usage-cost-rollups?${params}`,"GET");return object2(body)&&Array.isArray(body.buckets)?body.buckets.filter(object2):[]},fold=(buckets,key)=>{let totals=new Map;for(let b of buckets){let entity=typeof b.entity_id=="string"?b.entity_id:null,cur=totals.get(entity)??{usd:0,tokens_in:0,tokens_out:0};cur.usd+=typeof b.value=="number"?b.value:0,cur.tokens_in+=typeof b.tokens_in=="number"?b.tokens_in:0,cur.tokens_out+=typeof b.tokens_out=="number"?b.tokens_out:0,totals.set(entity,cur)}return[...totals].map(([entity,t])=>({[key]:entity,usd:Math.round(t.usd*1e6)/1e6,tokens_in:t.tokens_in,tokens_out:t.tokens_out})).sort((a,b)=>Number(b.usd)-Number(a.usd))},[byService,byConversation]=await Promise.all([rollup("service"),rollup("conversation_id")]),data2={view:"cost",since:new Date(window.since).toISOString(),until:new Date(window.until).toISOString(),...tz?{timezone:tz}:{},by_service:fold(byService,"service"),by_conversation:fold(byConversation,"conversation_id").filter(row=>row.conversation_id!==null).slice(0,limit).map(({conversation_id,usd:usd2})=>({conversation_id,usd:usd2}))},machine2=options.json||experience().json||experience().agent;return io.stdout(machine2?`${JSON.stringify(experience().agent?envelope(data2,{view}):data2)}
|
|
201
|
+
`:`${formatUsage("cost",data2)}
|
|
202
|
+
`),data2}let config=await request(`${store.origin}/v1/ask/config`,"GET");if(!object2(config)||config.org_id!==org||config.protocol_version!==1)throw new CliError("ASK_FAILED","Ask discovery does not match this session's tenant/protocol.");let endpoint=origin(config.endpoint,loopback(store)).origin,data=view==="conversation"?await request(`${endpoint}/v1/conversations/${id}`,"GET"):await request(`${endpoint}/v1/usage`,"POST",usageBody(view,options,options.timezone??io.timezone?.()));if(!object2(data))throw new CliError("ASK_FAILED","Invalid Ask response.");let machine=options.json||experience().json||experience().agent;return io.stdout(machine?`${JSON.stringify(experience().agent?envelope(data,{view}):data)}
|
|
191
203
|
`:`${formatUsage(view,data)}
|
|
192
|
-
`),data}function registerUsage(program2,{hidden=!1}={}){program2.command("usage [view] [id]",{hidden}).description("Ask usage (admin only): summary, users, trend, questions, failures,
|
|
204
|
+
`),data}function registerUsage(program2,{hidden=!1}={}){program2.command("usage [view] [id]",{hidden}).description("Ask usage (admin only): summary, users, trend, questions, failures, one conversation's full history, or cost (tokens + sandbox, by service and per conversation)").option("--since <when>","start: a date, an ISO time with a zone, or 7d / 4w / 3m ago (default 30 days)").option("--until <when>","end (exclusive), same forms (default now)").option("--user <email-or-id>","only this person; repeatable",(value,previous=[])=>[...previous,value]).option("--by <period>","trend buckets: day, week or month (default day)").option("--timezone <zone>","timezone for days and weeks (defaults to your computer timezone)").option("--limit <n>","questions to list, 1-1000 (default 100)").option("--with-answers","include answer text in the questions view").option("--json","write the structured response to stdout").addHelpText("after",`
|
|
193
205
|
Examples:
|
|
194
206
|
amp usage 30-day summary
|
|
195
207
|
amp usage users --since 2026-09-01 who uses Ask, and how much
|
|
@@ -197,6 +209,7 @@ Examples:
|
|
|
197
209
|
amp usage questions --user <email> --with-answers
|
|
198
210
|
amp usage failures --since 7d what failed, recently
|
|
199
211
|
amp usage conversation conv_<id> every question and answer in one conversation
|
|
212
|
+
amp usage cost --since 7d what it cost: tokens beside sandbox VMs, and the top conversations
|
|
200
213
|
|
|
201
214
|
Turns are attributed to the person who started the conversation.`).action(async(view,id,opts)=>{await usage(view??"summary",id,opts)})}init_experience();init_brand();init_brand();init_http();async function catalog(includeHidden=!1){let result=JSON.parse(await doorRequest("GET","/v1/ontology/object-types"));if(!Array.isArray(result.objectTypes))throw new Error("Invalid schema response.");return result.objectTypes}function publicSchema(row){if(typeof row.apiName!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(row.apiName))throw new Error("Invalid schema response.");return{name:row.apiName,table:`main_${row.apiName}`,description:row.description??"",columns:Object.entries(row.properties??{}).map(([name,property])=>({name,type:property.baseType??"unknown"}))}}async function schema(table,opts){readContext();let rows=(await catalog()).map(publicSchema);if(table!==void 0){let row=rows.find(row2=>row2.name===table||row2.table===table);if(!row)throw new CliError("NOT_FOUND","That table is unavailable in this workspace.",!1,"amp schema");emit(row,opts,()=>`${row.table} \u2014 ${row.description}
|
|
202
215
|
${humanTable(row.columns)}`)}else emit({tables:rows},opts,()=>humanTable(rows.map(row=>({table:row.table,description:row.description,columns:row.columns.length}))))}function registerSchema(program2){program2.command("schema").argument("[table]","type name from the authorized catalog").description("List authorized types and columns").option("--json","compact JSON output").option("--out <file>","write results to a file").action(schema)}init_request_credential();init_machine();init_experience();init_http();var program=new Command().name("amp").description("Query your data with Ampersand").version(CLI_VERSION).option("--profile <name>","select a saved tenant profile").option("--agent","structured JSON for agents; never prompt").option("--human","readable output for people").option("--quiet","hide routine progress").option("--no-input","never open a browser or wait for input").exitOverride().configureOutput({writeErr:()=>{}}).hook("preAction",(_,action)=>{if(configureExperience({...program.opts(),json:action.opts().json}),requestCredentialConfigured()||machineConfigFromEnv())throw new CliError("UNSUPPORTED_IDENTITY","Use a saved human session. Remove machine/request credentials first. Agent output does not change identity.");setProfileOverride(program.opts().profile)});installTerminalHelp(program,{edition:"workspace CLI",examples:["amp login --store <workspace-url>","amp schema",'amp ask "What data is available to me?"']});program.command("login").description("Sign in or manage your local session").option("--store <url>","tenant store HTTPS origin").option("--name <profile>","saved profile name").option("--org <id>","select your organization during sign-in").option("--status","show the signed-in identity").option("--logout","remove credentials for the selected profile on this machine").option("--json","raw JSON output (compatible with existing scripts)").option("--install-skill","install the bundled skill; can run without signing in").option("--skill-dir <path>","install SKILL.md in this directory; requires --install-skill").option("--skill-service <origin>","install a skill that refreshes from this HTTPS Ask service; requires --install-skill").action(async opts=>{if(opts.status&&opts.logout||(opts.status||opts.logout)&&(opts.store||opts.name||opts.org||opts.installSkill||opts.skillDir||opts.skillService))throw new CliError("INVALID_ARGUMENT","Use --status or --logout alone, or sign in with --store.");if(opts.skillDir&&!opts.installSkill)throw new CliError("INVALID_ARGUMENT","--skill-dir requires --install-skill.");if(opts.skillService&&!opts.installSkill)throw new CliError("INVALID_ARGUMENT","--skill-service requires --install-skill.");if(opts.skillService)try{skillUrl(opts.skillService)}catch{throw new CliError("INVALID_ARGUMENT","Use --skill-service with an HTTPS Ask service origin, without a path or credentials.")}let install=()=>{let skill=installSkill(opts.skillDir?join8(resolve2(opts.skillDir),"SKILL.md"):void 0,opts.skillService),hook=opts.skillDir||skill==="kept-hand-edited"?"skipped":installSessionHook();return{skill,hook}},describe=({skill,hook})=>`Skill ${skill}.${hook==="installed"?" Claude Code will update amp and refresh the skill when each new session starts.":""}`;if(opts.status)return whoami();if(opts.installSkill&&!opts.store&&!opts.name&&!opts.org){let result=install();emit(result,opts,()=>`${describe(result)} Start a new agent session to load it.`);return}if(opts.logout){let path=credentialsPath();existsSync7(path)&&await withFileLock(`${path}.lock`,async()=>{rmSync3(path,{force:!0})}),emit({logged_out:!0},opts,()=>"Local session removed.");return}let store=opts.store??activeProfile()?.store_url;if(!store)throw new CliError("WORKSPACE_REQUIRED","Choose your workspace to sign in.",!1,"amp login --store <your tenant store URL>");await login({store,name:opts.name,orgId:opts.org}),opts.installSkill&&process.stdout.write(`${describe(install())}
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amp-cli
|
|
3
|
-
description:
|
|
3
|
+
description: Use Amp for evidence-backed questions about what a person or team worked on, activity on a case or document, or how recorded work was performed—even without mentioning Amp or prior chat history. Also use for explicit Amp requests, related follow-ups, and authorized workspace schema or read-only SQL analysis. Exclude general advice, public research, and tasks answerable from supplied content alone.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Ampersand
|
|
7
7
|
|
|
8
|
+
## When to trigger Amp CLI
|
|
9
|
+
|
|
10
|
+
Select this skill from the current request; no prior session history or explicit
|
|
11
|
+
mention of Amp is required. Use Amp when answering requires evidence from the
|
|
12
|
+
connected workspace about a person's or team's work, activity on a case or
|
|
13
|
+
document, or how a workflow was actually performed. For example:
|
|
14
|
+
|
|
15
|
+
- “What did Alex work on yesterday?”
|
|
16
|
+
- “What happened on this matter last week?”
|
|
17
|
+
- “Where did the team repeat work?”
|
|
18
|
+
|
|
19
|
+
These requests trigger retrieval; they do not guarantee the evidence is available.
|
|
20
|
+
Also use this skill for explicit Amp requests and authorized schema or read-only
|
|
21
|
+
SQL analysis. For related questions and corrections, follow Conversation continuity
|
|
22
|
+
below even when the user does not repeat “Amp”.
|
|
23
|
+
|
|
24
|
+
Do not query Amp for general productivity advice, public research, local coding
|
|
25
|
+
or file edits, or answers fully supported by supplied content. Reword or shorten
|
|
26
|
+
an existing answer locally. For questions about how Amp or this skill works,
|
|
27
|
+
consult instructions or CLI help rather than querying workspace activity.
|
|
28
|
+
If it is unclear whether the user wants internal activity evidence, clarify the
|
|
29
|
+
scope before retrieving it.
|
|
30
|
+
|
|
8
31
|
## Setup
|
|
9
32
|
|
|
10
33
|
These steps apply in any agent environment: Claude Code, Codex, Cursor, a cloud sandbox, or a plain terminal.
|
|
@@ -52,4 +75,9 @@ answer locally. Start a fresh conversation for an independent investigation or
|
|
|
52
75
|
an explicit fresh start. A changed date or person alone can still be a comparison
|
|
53
76
|
within the same investigation. Never carry a conversation into a different tenant.
|
|
54
77
|
If multiple prior conversations could fit, ask which one; do not guess.
|
|
78
|
+
Resolve references such as “her” or “that case” from available context; ask for
|
|
79
|
+
the missing person or case when necessary. Without a prior conversation ID,
|
|
80
|
+
start a new question unless the user explicitly asks to resume an existing
|
|
81
|
+
conversation, in which case recover or request its ID. Do not assume history
|
|
82
|
+
from another session is available.
|
|
55
83
|
If work is active, watch it rather than submitting a duplicate request.
|