@tsuga/cli 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +3 -3
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -170,10 +170,10 @@ ${c}`:a||c}function fh(e,{maxDepth:t=1e3,numbersAsFloat:n=!1}={}){if(pc(e)!=="ob
170
170
  `?o+`
171
171
  `:o}var i6={CLAUDE_CODE_ENABLE_TELEMETRY:"1",CLAUDE_CODE_ENHANCED_TELEMETRY_BETA:"1",OTEL_METRICS_EXPORTER:"otlp",OTEL_LOGS_EXPORTER:"otlp",OTEL_TRACES_EXPORTER:"otlp",OTEL_EXPORTER_OTLP_PROTOCOL:"http/json",OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE:"delta"};function a6(e,t,n,o){let i=(0,na.join)(e,".claude","settings.json");V(`Claude Code \u2192 ${i}`),(0,hh.mkdirSync)((0,na.dirname)(i),{recursive:!0});let a=As(i),c;try{c=JSON.parse(a??"{}")}catch{throw new Error(`${i} is not valid JSON. Fix or remove it before re-running.`)}if(!bo(c))throw new Error(`${i} must contain a JSON object at the top level.`);let l=c.env??{};if(!bo(l))throw new Error(`${i} "env" must be a JSON object.`);let m=o?"1":"0";c.env={...l,...i6,OTEL_EXPORTER_OTLP_ENDPOINT:t,OTEL_LOG_USER_PROMPTS:m,OTEL_LOG_TOOL_DETAILS:m,OTEL_LOG_TOOL_CONTENT:m,OTEL_EXPORTER_OTLP_HEADERS:`Authorization=Bearer ${n}`};let f=Os(i,a);Ss(i,`${JSON.stringify(c,null,2)}
172
172
  `),V(f===void 0?" created":` patched (backup: ${f})`)}function s6(e,t,n){return{environment:"dev",log_user_prompt:n,exporter:{"otlp-http":{endpoint:`${e}/v1/logs`,protocol:"binary",headers:{Authorization:`Bearer ${t}`}}},trace_exporter:{"otlp-http":{endpoint:`${e}/v1/traces`,protocol:"binary",headers:{Authorization:`Bearer ${t}`}}}}}function c6(e,t,n,o){let i=(0,na.join)(e,".codex"),a=(0,na.join)(i,"config.toml");V(`Codex \u2192 ${a}`),(0,hh.mkdirSync)(i,{recursive:!0});let c=As(a),l=c===void 0?{}:uh(c),m="otel"in l;delete l.otel,l.otel=s6(t,n,o);let f=Os(a,c);Ss(a,fh(l)),V(f===void 0?" created":m?` replaced existing [otel] tables (backup: ${f})`:` appended (backup: ${f})`)}function ek(){return new Mr("--endpoint <OTLP_ENDPOINT>","OTLP base URL").env("TSUGA_OTLP_ENDPOINT").makeOptionMandatory()}function tk(){return new Mr("--ingestion-api-key <INGESTION_API_KEY>","Tsuga ingestion API key").env("TSUGA_INGESTION_API_KEY").makeOptionMandatory()}var rk="Restart your claude / codex session to load the new config.";function nk(e){let t=e.command("setup").description("Configure local LLM CLIs to ship OTEL telemetry to Tsuga");t.command("claude-code").description("Patch ~/.claude/settings.json with Tsuga OTEL env").addOption(ek()).addOption(tk()).option("--no-include-prompts","Strip user prompts + tool content from telemetry (default: include)").action(n=>{a6((0,gh.homedir)(),n.endpoint.replace(/\/+$/,""),n.ingestionApiKey,n.includePrompts),V(rk)}),t.command("codex").description("Patch ~/.codex/config.toml with Tsuga OTEL settings (replaces any existing [otel] table)").addOption(ek()).addOption(tk()).option("--no-include-prompts","Strip user prompts from telemetry (default: include)").action(n=>{c6((0,gh.homedir)(),n.endpoint.replace(/\/+$/,""),n.ingestionApiKey,n.includePrompts),V(rk)})}var Co="/v0/logs";var yh=["CRITICAL","ERROR","WARN","NOTICE","INFO","DEBUG","TRACE"];var uc=0,Th=r.Object({__tsuga_metadata__:r.Object({id:r.String(),source:r.Optional(r.String())},{additionalProperties:!0}),context:r.Optional(r.Object({team:r.Optional(r.String({description:"Team name associated with this log."})),tag_policy_warnings:r.Optional(r.Record(r.String(),r.String()))},{additionalProperties:!0,description:"Context attributes attached to the log after ingestion and route processing. `team` comes from telemetry or route-enriched context when present."})),message:r.Optional(r.Unknown({description:"Log message after ingestion and route processing. It can be any JSON value because processors may map arbitrary data into `message`."})),timestamp:r.Number({description:"Log timestamp as a Unix timestamp in milliseconds."}),level:r.Enum(yh,{description:"Log severity, normalized from the source severity into one of the fixed Tsuga levels (ordered `CRITICAL` to `TRACE`)."}),[mx]:r.Optional(r.String({description:"ID of the cluster this log was read from. Only set when the request spanned several clusters."}))},{additionalProperties:!0}),ok=100,p6="timestamp",u6=r.Union([r.Literal(p6)]),kn=r.Partial(r.Object({...at.properties,query:u({type:"tsuga-query",description:"Tsuga query used to filter logs in the selected time range."}),maxResults:r.Integer({minimum:1,maximum:1e3,description:"Maximum number of logs to return. Valid values are 1 through 1000."}),shouldRecommendAttributes:r.Boolean({description:"Whether Tsuga should return suggested attributes to highlight in results."}),triggeredBy:r.Literal("refresh",{description:"Indicates the query was triggered by a refresh action."}),startOffset:r.Number({minimum:uc,description:"Result offset used when fetching additional log rows."}),startOffsets:r.Array(r.Number({minimum:uc}),{description:`Result offsets used when fetching additional log rows, one per entry of \`clusterIds\` and in the same order. Echo back each cluster's \`nextStartOffset\` from the previous response. Must have the same length as \`clusterIds\`. Defaults to ${uc} for every cluster.`}),clusterId:u({allowEmpty:!1,description:"ID of the cluster to target. Omit to use the default cluster."}),clusterIds:ts,sortBy:u6,sortDirection:r.Union([r.Literal("asc"),r.Literal("desc")],{description:"Timestamp sort direction for log results."})})),zrt=d({tags:["logs"],url:`${Co}/count`,method:"GET",query:r.Pick(kn,["from","to","query","clusterId","clusterIds"]),response:r.Object({count:r.Number(),clusterResponses:r.Optional(ui())})}),lc=r.Object({value:r.String(),priority:r.Number()}),Yrt=d({tags:["logs"],url:`${Co}/search`,method:"GET",query:kn,response:r.Object({logs:r.Optional(r.Array(Th)),attributesToHighlight:r.Optional(r.Array(lc)),clusterResponses:r.Optional(ui(r.Object({nextStartOffset:r.Number({description:"Number of logs consumed on this cluster so far. Echo it back at this cluster's position in `startOffsets` to fetch the next page."})})))},{additionalProperties:!0})}),l6=r.Cyclic({TokenOutput:r.Union([r.Object({type:r.Literal("top"),values:r.Array(r.String())}),r.Object({type:r.Literal("ip"),value:r.String()}),r.Object({type:r.Literal("range"),min:r.Number(),max:r.Number()}),r.Object({type:r.Literal("variable"),value:r.String()}),r.Object({type:r.Literal("text"),value:r.String()}),r.Object({type:r.Literal("date"),value:r.String()}),r.Object({type:r.Literal("url"),value:r.String()}),r.Object({type:r.Literal("structure"),key:r.Array(r.String()),value:r.Array(r.Ref("TokenOutput"))})])},"TokenOutput",{description:"A token identified as part of the pattern"}),m6=r.Object({id:r.String(),pattern:r.Array(l6),size:r.Integer({description:"Number of logs that matched the pattern"}),examples:r.Array(r.Object({id:r.String(),timestampUnix:r.Number()})),timeDistribution:r.Array(r.Number()),staticFeatures:r.Array(r.Object({key:r.String(),value:r.String()},{description:"Fixed groups present in all logs matching the pattern"}))}),Vrt=d({tags:["logs"],url:`${Co}/patterns`,method:"GET",query:r.Object({...at.properties,query:u({type:"tsuga-query"}),clusteringThreshold:r.Optional(r.Number()),clusterId:r.Optional(u({allowEmpty:!1}))}),response:r.Object({patterns:r.Array(m6),sampleSize:r.Number()})}),Qrt=d({tags:["logs"],url:`${Co}/attributes`,method:"GET",query:r.Object({...at.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"})),clusterIds:r.Optional(ts)}),response:r.Object({attributes:r.Array(r.String()),clusterResponses:r.Optional(ui())})});var d6=["pending","running","completed","incomplete","failed"],ik=r.Object({id:r.String({description:"ID of the export."}),status:r.Enum(d6,{description:"Lifecycle of the export. `pending` until the download is claimed, `running` while the CSV streams, then `completed`, `incomplete` when the file is usable but some matched logs could not be reached, or `failed` if the export was interrupted."}),rowsWritten:r.Number({description:"Logs written to the CSV so far. Updated periodically while the download streams, so it lags slightly behind the bytes on the wire."}),totalRows:r.Number({description:"Logs this export will write, already clamped to the export maximum. Use it as the denominator for progress."}),isTruncated:r.Boolean({description:"Whether the query matched more logs than the export maximum, so the CSV holds only the first logs in the requested sort order."}),errorMessage:r.Optional(r.String({description:"Why the export is incomplete or failed."}))}),Wrt=d({tags:["logs"],url:`${Co}/exports`,method:"POST",body:r.Object({...r.Pick(kn,["from","to","query","clusterId","clusterIds","sortBy","sortDirection"]).properties,columns:r.Array(u({allowEmpty:!1}),{minItems:1,description:"Log attributes to write as CSV columns, in order. Each becomes one column, resolved against the log with dotted-path lookup."})}),response:r.Object({...ik.properties,downloadUrl:r.String({description:"Relative URL to fetch the CSV from. It carries a single-use token that expires shortly after creation, so the caller must start the download immediately."})})}),Zrt=d({tags:["logs"],url:`${Co}/exports/:exportId`,method:"GET",params:r.Object({exportId:r.String()}),response:ik}),Xrt=d({tags:["logs"],url:`${Co}/exports/:exportId/download`,method:"GET",params:r.Object({exportId:r.String()}),query:r.Object({token:u({allowEmpty:!1,description:"Single-use token minted when the export was created."})}),response:r.String({description:"CSV file, streamed as it is produced."})});var mc=`/${"v1"}/logs`,Eh=r.Object({context:r.Optional(r.Object({team:r.Optional(r.String({description:"Team name associated with this log."}))},{additionalProperties:!0,$id:"LogContext",description:"Context attributes attached to the log after ingestion and route processing. `team` comes from telemetry or route-enriched context when present."})),...r.Pick(Th,["message","timestamp","level"]).properties},{additionalProperties:!0,$id:"LogEvent",description:"Log event including message, timestamp, severity level, and optional enriched context attributes."}),f6={context:{team:"platform"},message:"Request completed successfully",timestamp:17040672e5,level:"INFO"};Object.assign(Eh,{examples:[f6]});var ak=r.Object({query:r.Optional(u({type:"tsuga-query",default:"*",description:"Tsuga query selecting logs to return. Omit to search all logs with `*`."})),maxResults:r.Optional(r.Integer({minimum:1,maximum:1e3,default:ok,description:"Maximum number of logs to return. Defaults to 100. Valid values are 1 through 1000."})),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."})),...bt.properties},{description:"Log search query. Provide a time range, optional Tsuga query, and optional limit."}),g6={query:"level:ERROR",maxResults:100,from:1704067200,to:1704070800};Object.assign(ak,{examples:[g6]});var sk=d({description:"Searches logs in a required time range using Tsuga query syntax. Use this endpoint to retrieve raw log events for investigations or automation. See [Logs](/documentation/explore/logs) and [Query syntax](/documentation/explore/query-syntax).",operationId:"searchLogs",tags:["public","logs"],url:`${mc}/search`,method:"GET",query:ak,response:r.Object({logs:r.Array(Eh,{description:"Log events matching the query and time range."})})}),h6=r.Object({pattern:r.String({description:"Clustered log pattern formatted as a readable string."}),size:r.Integer({description:"Number of logs that matched the pattern"}),groups:r.Array(r.Object({key:r.String({description:"Attribute dot-path shared by all logs in the pattern."}),value:r.String({description:"Value of the attribute identified by `key`, common to all logs in the pattern."})},{description:"Attribute key-value pair that is constant across every log matching this pattern, e.g. a shared `level` or `context.team`."}))}),ck=r.Object({patterns:r.Array(h6,{description:"Log patterns clustered from logs matching the query."}),sampleSize:r.Number({description:"Number of logs sampled while computing patterns."})},{}),y6={patterns:[{pattern:"Resolved segment SegmentId([0:1]) from cache",size:37004,groups:[{key:"level",value:"DEBUG"},{key:"context.team",value:"infra"}]}],sampleSize:100};Object.assign(ck,{examples:[y6]});var pk=d({description:"Clusters matching logs into readable patterns for the requested time range. Use this to summarize common log shapes before drilling into raw events. See [Logs](/documentation/explore/logs).",operationId:"getLogPatterns",tags:["public","logs"],url:`${mc}/patterns`,method:"GET",query:r.Object({...bt.properties,query:u({type:"tsuga-query",description:"Tsuga query selecting logs to cluster into patterns."}),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))}),response:ck}),T6=r.Object({exampleLog:r.Optional(Eh),team:r.String({description:"Team associated with the new error pattern."}),env:r.String({description:"Environment associated with the new error pattern."}),service:r.String({description:"Service associated with the new error pattern."}),firstSeen:r.Number({description:"Unix timestamp in seconds when this pattern was first seen."}),lastSeen:r.Number({description:"Unix timestamp in seconds when this pattern was last seen."})}),uk=r.Object({newErrorPatterns:r.Array(T6,{description:"Error patterns first seen in the requested time range."})},{}),E6={newErrorPatterns:[{team:"platform",env:"production",service:"api-gateway",firstSeen:1704067200,lastSeen:1704153600,exampleLog:{message:"ConnectionError: Failed to connect to database",timestamp:17041536e5,level:"ERROR"}}]};Object.assign(uk,{examples:[E6]});var lk=d({description:"Finds error patterns first seen in the requested time range, optionally filtered by team, environment, or service. Use this to detect newly appearing failures. See [Logs](/documentation/explore/logs).",operationId:"getNewErrorPatterns",tags:["public","logs"],url:`${mc}/patterns/new`,method:"GET",query:r.Object({...bt.properties,team:r.Optional(u({description:"Team name to restrict pattern detection to."})),env:r.Optional(u({description:"Environment to restrict pattern detection to."})),service:r.Optional(u({description:"Service name to restrict pattern detection to."})),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))}),response:uk}),_6=r.Object({team:r.String({description:"Team associated with the error pattern."}),env:r.String({description:"Environment associated with the error pattern."}),pattern:r.String({description:"Error pattern formatted as a readable string."}),increaseTimestamps:r.Array(r.Number({description:"Unix timestamp in milliseconds of a detected spike in occurrence of this pattern. One entry per spike detected in the requested time range."}))}),mk=r.Object({errorPatternIncreases:r.Array(_6,{description:"Error patterns with increased occurrence in the requested time range."})},{}),I6={errorPatternIncreases:[{team:"platform",env:"production",pattern:"Non-resolved segment SegmentId(*) from cache",increaseTimestamps:[17041536e5,17042392e5]}]};Object.assign(mk,{examples:[I6]});var dk=d({description:"Finds error patterns whose occurrence increased for a team in the requested time range. Use this to identify growing failure modes. See [Logs](/documentation/explore/logs).",operationId:"getErrorPatternIncrease",tags:["public","logs"],url:`${mc}/patterns/increase`,method:"GET",query:r.Object({...bt.properties,team:u({description:"Team name to analyze for pattern increases."}),env:r.Optional(u({description:"Environment to restrict the analysis to."})),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))}),response:mk}),fk=r.Object({attributes:r.Array(r.String(),{description:"Log attribute dot-paths observed in the time range"})},{}),b6={attributes:["context.env","context.k8s.pod.name","context.service.name","context.team","level","message","timestamp"]};Object.assign(fk,{examples:[b6]});var S6=r.Object({...bt.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))},{description:"Log attribute discovery query. Provide a time range and optional cluster ID."}),gk=d({description:"Lists log attribute dot-paths observed in the requested time range. This endpoint requires a public API credential that can read all relevant teams because attribute discovery is not team-filtered. Use this to discover fields for Tsuga queries, filters, and grouping. See [Logs](/documentation/explore/logs).",operationId:"getLogAttributes",tags:["public","logs"],url:`${mc}/attributes`,method:"GET",query:S6,response:fk});var en="/v0/metrics",A6=r.Object({__tsuga_metadata__:r.Object({},{additionalProperties:!0}),context:r.Optional(r.Object({},{additionalProperties:!0})),message:r.Optional(r.String()),timestamp:r.Number(),level:r.Enum(yh)},{additionalProperties:!0}),mnt=d({tags:["metrics"],url:`${en}/count`,method:"GET",query:r.Pick(kn,["from","to","query","clusterId"]),response:r.Object({count:r.Number()})}),dnt=d({tags:["metrics"],url:`${en}/search`,method:"GET",query:kn,response:r.Object({logs:r.Optional(r.Array(A6)),totalHits:r.Optional(r.Number()),attributesToHighlight:r.Optional(r.Array(lc))},{additionalProperties:!0})}),Il=r.Enum(["counter","gauge","histogram","summary","composite_histogram"],{description:"Metric instrument type, set by the reporting instrumentation. `counter` accumulates a monotonically increasing total (aggregate with `sum` or `increase`); `gauge` reports a point-in-time value that can go up or down (aggregate with `average`, `max`, `min`, or `last`); `histogram`, `composite_histogram`, and `summary` report a distribution of values, from which percentiles can be computed. Tsuga displays `composite_histogram` metrics as Histogram in the app."}),_h=r.Enum(["delta","cumulative","none"],{description:"Metric temporality. `delta` reports changes per collection interval, `cumulative` reports running totals, and `none` means temporality is not applicable or unknown."}),hk=r.Number({description:"Timestamp of the most recent data point recorded for this metric, in milliseconds since the Unix epoch. Omitted when Tsuga has no record of it. Computed from a periodically refreshed metadata snapshot, so it can lag ingestion by a few minutes and should not be used to detect sub-minute gaps."}),O6=r.Object({capabilities:r.Optional(r.Array(r.String(),{description:"Aggregate function types (such as `max`, `min`, `sum`) supported when querying or graphing this metric. If this field is omitted or empty, all aggregate types are supported."})),description:r.Optional(r.String({description:"Human-readable description of the metric"})),tags:r.Optional(r.Array(r.String(),{description:"Attribute names available as dimensions for filtering or grouping this metric. Exposed publicly as `attributes`."})),temporality:r.Optional(_h),type:r.Optional(Il),unit:r.Optional(r.String({description:"Metric unit reported by instrumentation or inferred metadata."})),lastSeen:r.Optional(hk)}),fnt=d({url:`${en}/metadata`,method:"GET",query:r.Object({...at.properties,metricName:u({allowEmpty:!1}),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:O6}),gnt=d({tags:["metrics"],url:en,method:"GET",query:r.Object({...at.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:r.Array(r.String())}),bl=r.Array(r.Object({name:r.String({description:"Metric name observed by Tsuga."}),type:Il,lastSeen:r.Optional(hk)}),{description:"Metrics observed in the requested time range, with the instrument type each was reported as."}),x6=[{name:"http.server.request.duration",type:"histogram"},{name:"http.server.active_requests",type:"gauge"},{name:"http.server.request.count",type:"counter"}];Object.assign(bl,{examples:[x6]});var hnt=d({tags:["metrics"],url:`${en}/names-and-types`,method:"GET",query:r.Object({...at.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:bl}),dc=r.Array(r.String(),{description:"Attribute names available as dimensions for filtering or grouping the requested metric(s)."}),R6=["service","env","host","method","status_code"];Object.assign(dc,{examples:[R6]});var ynt=d({tags:["metrics"],url:`${en}/attributes`,method:"GET",query:r.Object({...at.properties,metricName:u({allowEmpty:!1}),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:dc}),Tnt=d({tags:["metrics"],url:`${en}/attributes/batch`,method:"GET",query:r.Object({...at.properties,metricNames:r.Array(u({allowEmpty:!1}),{minItems:1,description:"Metric names to fetch attributes for"}),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:dc}),Ih=r.Object({assets:r.Array(r.Object({type:r.Enum(["dashboard","monitor"],{description:"Kind of asset referencing the metric."}),id:r.String({description:"Dashboard or monitor ID for the asset that queries this metric."}),name:r.String({description:"Display name of the referencing dashboard or monitor."}),owner:r.String({description:"Team ID that owns the referencing dashboard or monitor."})},{description:"A dashboard or monitor that queries this metric."}),{description:"Dashboards and monitors that currently query this metric."})}),Ent=d({tags:["metrics"],url:`${en}/assets-usage`,method:"GET",query:r.Object({metricName:u({allowEmpty:!1})}),response:Ih}),_nt=d({url:`${en}/per-attribute`,method:"GET",query:r.Object({from:r.Optional(r.Number()),to:r.Optional(r.Number()),attribute:u(),clusterId:r.Optional(u({allowEmpty:!1}))}),response:r.Array(r.String())});var bh=`/${"v1"}/metrics`,C6=[{name:"http.server.request.duration",type:"histogram"},{name:"http.server.active_requests",type:"gauge"},{name:"http.server.request.count",type:"counter"}];Object.assign(bl,{examples:[C6]});var Sh=r.Object({name:r.String({description:"Metric name observed by Tsuga. Use this value in metric queries and aggregations."}),type:Il,description:r.Optional(r.String({description:"Human-readable description of what the metric measures, set by the reporting instrumentation. Omitted when the instrumentation did not provide one."})),unit:r.Optional(r.String({description:"Metric unit reported by instrumentation or inferred metadata."})),temporality:r.Optional(_h),capabilities:r.Optional(r.Array(r.String(),{description:"Aggregate function types (such as `max`, `min`, `sum`) supported when querying or graphing this metric. An empty or omitted array means all aggregate types are supported."})),attributes:r.Optional(r.Array(r.String(),{description:"Metric attribute names available as dimensions for filtering or grouping."}))}),P6={name:"http.server.request.duration",type:"histogram",description:"Duration of HTTP server requests",unit:"ms",temporality:"delta",capabilities:["max","min","sum"],attributes:["context.team","context.environment","context.region"]};Object.assign(Sh,{examples:[P6]});var yk=r.Object({...bt.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))},{description:"Metric metadata query. Provide the time range to inspect and optionally a cluster ID."}),Tk=d({description:"Lists metrics observed in the requested time range with metadata such as type, unit, temporality, capabilities, and attributes. Use this to discover metric names before querying or graphing them. See [Metrics](/documentation/explore/metrics/index).",operationId:"listMetrics",tags:["public","metrics"],url:bh,method:"GET",query:yk,response:r.Array(Sh)}),Ek=d({description:"Retrieves metadata for one metric name in the requested time range. Use this to inspect available dimensions and supported query behavior for a metric. See [Metric details](/documentation/explore/metrics/metric-details).",operationId:"getMetric",tags:["public","metrics"],url:`${bh}/:name`,method:"GET",params:r.Object({name:u({allowEmpty:!1,description:"Metric name to retrieve, for example `http.server.request.duration`."})}),query:yk,response:Sh}),U6=["service","env","host","method","status_code"];Object.assign(dc,{examples:[U6]});var _k=r.Pick(Ih,["assets"]),w6={assets:[{type:"dashboard",id:"dash-123-abc",name:"API Performance Overview",owner:"team-123-abc"},{type:"monitor",id:"mon-789-ghi",name:"High Latency Alert",owner:"team-123-abc"}]};Object.assign(_k,{examples:[w6]});var Ik=d({description:"Retrieve dashboards and monitors that use a specific metric",operationId:"getMetricAssetsUsage",tags:["private"],url:`${bh}/assets-usage`,method:"GET",query:r.Object({metricName:u({allowEmpty:!1,description:"The name of the metric to find usage for"})}),response:_k});var oa="/v0/traces",Lnt=d({tags:["traces"],url:`${oa}/count`,method:"GET",query:r.Pick(kn,["from","to","query","clusterId"]),response:r.Object({count:r.Number()})}),v6=["unset","ok","error"],N6=r.Enum(v6),L6=["unspecified","internal","server","client","producer","consumer"],k6=r.Enum(L6),Sk=r.Object({__tsuga_metadata__:r.Optional(r.Object({},{additionalProperties:!0})),context:r.Optional(r.Object({telemetry:r.Optional(r.Object({sdk:r.Optional(r.Object({language:r.Optional(r.String())},{additionalProperties:!0}))},{additionalProperties:!0})),tag_policy_warnings:r.Optional(r.Record(r.String(),r.String())),service:r.Optional(r.Object({name:r.Optional(r.String()),version:r.Optional(r.String())},{additionalProperties:!0})),env:r.Optional(r.String())},{additionalProperties:!0})),parent_span_id:r.Optional(r.String()),resource_attributes:r.Object({},{additionalProperties:!0}),span_attributes:r.Object({},{additionalProperties:!0}),duration:r.Number({description:"In milliseconds."}),duration_ns:r.String({description:"In nanoseconds. Transmitted as a string to avoid losing precision, as BigInt is not JSON-compatible."}),start_time:r.String({description:"In nanoseconds. Transmitted as a string to avoid losing precision, as BigInt is not JSON-compatible."}),end_time:r.String({description:"In nanoseconds. Transmitted as a string to avoid losing precision, as BigInt is not JSON-compatible."}),error:r.Optional(r.Object({message:r.String()})),events:r.Optional(r.Array(r.Object({event_attributes:r.Object({},{additionalProperties:!0}),event_name:r.String(),event_timestamp_nanos:r.String({description:"In nanoseconds. Transmitted as a string to avoid losing precision, as BigInt is not JSON-compatible."})}))),links:r.Optional(r.Array(r.Object({},{additionalProperties:!0}))),span_id:r.String(),span:r.Object({kind:k6,name:r.String()}),status_code:r.Optional(N6),trace_id:r.String()}),Ak=100,M6="start_time",D6="duration_ns",G6=r.Union([r.Literal(M6),r.Literal(D6)]),$6={...at.properties,query:r.Optional(u({type:"tsuga-query"})),maxResults:r.Optional(r.Integer({minimum:1,maximum:1e4})),shouldRecommendAttributes:r.Optional(r.Boolean()),startOffset:r.Optional(r.Number({minimum:uc})),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"})),sortBy:r.Optional(G6),sortDirection:r.Optional(r.Union([r.Literal("asc"),r.Literal("desc")]))},knt=d({tags:["traces"],url:`${oa}/search`,method:"GET",query:r.Object($6),response:r.Object({spans:r.Optional(r.Array(Sk)),attributesToHighlight:r.Optional(r.Array(lc))})});var j6=r.Object({groupByAttributes:r.Array(u({allowEmpty:!1}),{description:'Glob patterns of span_attributes keys to use as additional grouping dimensions (e.g. "db.*"). Matches against flattened dotted keys of span_attributes only.'}),minLeafSpansToAggregate:r.Integer({minimum:2,description:"Minimum leaf-group size before aggregation occurs. Default 5."}),maxParentDepth:r.Integer({minimum:-1,description:"Max depth of recursive parent aggregation. 0 = leaves only, -1 = unlimited (default)."}),aggregationHistogramBucketsSeconds:r.Array(r.Number({minimum:0}),{description:"Upper bounds for the latency histogram in seconds. Empty array disables the histogram attributes."}),sortSpans:r.Boolean({description:"Whether to sort the returned spans by start_time (span_id tie-breaker). Set false when the caller reconstructs the trace tree and does not need ordering. Default true."})}),F6=r.Object({...at.properties,traceId:u({allowEmpty:!1,description:"Trace ID to summarize"}),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"})),...r.Partial(j6).properties}),Mnt=d({tags:["traces"],url:`${oa}/summarize`,method:"GET",query:F6,response:r.Object({spans:r.Array(Sk)})}),Dnt=d({tags:["traces"],url:`${oa}/attributes`,method:"GET",query:r.Object({...at.properties,clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:r.Object({attributes:r.Array(r.String())})}),q6=r.Object({key:r.String({description:"Opaque stable key identifying this contributing service within the response. Tsuga assigns it while building the summary; `ranges[].contributions[].serviceKey` and `serviceTotals[].serviceKey` reference this value."}),name:r.String({description:"Observed service name that contributed leaf-span time to the trace. Comes from trace service context and is returned for every contributing service."}),env:r.Optional(r.String({description:"Observed service environment for this contributing service. Comes from trace service context and is absent when the spans do not carry an environment."})),id:r.Optional(r.String({description:"Service catalog ID for this contributing service. Tsuga sets it when exactly one catalog entry matches the observed service and environment; absent when the service is not cataloged or matches ambiguously."})),namespace:r.Optional(r.String({description:"Service namespace from the matched service catalog entry. Returned only when Tsuga can resolve the contributing service to one catalog entry with a namespace."}))}),B6=r.Object({fromNs:r.String({description:"Range start time in nanoseconds since Unix epoch. Tsuga sets it from the trace timeline split where active leaf spans change. Transmitted as a string to avoid losing precision."}),toNs:r.String({description:"Range end time in nanoseconds since Unix epoch. Tsuga sets it from the trace timeline split where active leaf spans change. Transmitted as a string to avoid losing precision."}),durationNs:r.String({description:"Range duration in nanoseconds, computed as `toNs - fromNs`. Transmitted as a string to avoid losing precision."}),contributions:r.Array(r.Object({serviceKey:r.String({description:"Contributing service key for this range. References `services[].key` in the same response."}),weight:r.Number({description:"Share of this range attributed to the service, from `0` through `1`. Tsuga computes the weight from active leaf spans in the range; weights inside one range sum to `1`."}),durationNs:r.String({description:"Estimated duration attributed to this service in the range, computed as `weight` times the range's own `durationNs` and rounded to whole nanoseconds. Use `serviceTotals` as authoritative when rounded range values do not add up exactly."})}),{description:"Services contributing active leaf-span time to this range, with weighted shares for the range duration."}),spanIds:r.Array(r.String(),{description:"Leaf span IDs that produced this range. Tsuga returns IDs from the leaf-only attribution set, including merged sub-ranges after short-range collapsing."})}),H6=r.Object({serviceKey:r.String({description:"Contributing service key for this aggregate total. References `services[].key` in the same response."}),durationNs:r.String({description:"Total wall-clock contribution of this service across the trace, in nanoseconds. Sum of `weight * durationNs` over every range the service contributes to; authoritative when it disagrees with rounded per-range values. Transmitted as a string to avoid losing precision."}),share:r.Number({description:"Share of total trace duration attributed to this service, from `0` through `1`. Computed as `durationNs / totalDurationNs`; multiply by 100 for a percentage."})}),Ah=r.Optional(r.Number({minimum:0,description:"Minimum range duration in milliseconds. Shorter ranges are collapsed (absorbed into a neighbor or blended with adjacent shorts) to keep the timeline readable. When omitted, the default depends on the trace: max(1ms, 1% of the total trace duration), so e.g. a 10s trace collapses ranges shorter than 100ms. Pass 0 to disable collapsing."})),Oh=r.Optional(r.Boolean({default:!0,description:"When false, omits the per-range timeline (returns `ranges: []`) and returns only `services` and `serviceTotals`. Cuts payload size for callers that only need the headline. Defaults to true."})),Sl=r.Object({traceId:r.String({description:"Trace ID summarized by the response. Echoes the required `traceId` query parameter."}),startTimeNs:r.String({description:"Earliest `start_time` across fetched spans, in nanoseconds since Unix epoch. If the request window does not cover the full trace, this reflects the fetched subset, not necessarily the trace's true start. Transmitted as a string to avoid losing precision."}),endTimeNs:r.String({description:"Latest `end_time` across fetched spans, in nanoseconds since Unix epoch. If the request window does not cover the full trace, this reflects the fetched subset, not necessarily the trace's true end. Transmitted as a string to avoid losing precision."}),totalDurationNs:r.String({description:"Total fetched trace duration in nanoseconds, computed as `endTimeNs - startTimeNs`. Transmitted as a string to avoid losing precision."}),truncated:r.Boolean({description:"Whether the underlying span fetch hit its result cap. When true, the latency summary may be incomplete because not every span in the trace was available."}),services:r.Array(q6,{description:"Unique services that contributed leaf-span time to the trace, enriched with catalog IDs when Tsuga can resolve them. Referenced by `serviceKey` from `ranges` and `serviceTotals`."}),ranges:r.Array(B6,{description:"Sequential trace timeline split into ranges wherever the active leaf-span set changes. Empty when the request sets `includeRanges` to false."}),serviceTotals:r.Array(H6,{description:"Aggregated wall-clock contribution per service across the fetched trace. Use this for service-level latency ownership without processing the per-range timeline."})}),K6={traceId:"78c99c5bb1e4a30297fa134b9dbec64f",startTimeNs:"1704067200000000000",endTimeNs:"1704067202400000000",totalDurationNs:"2400000000",truncated:!1,services:[{key:"checkout|prod",name:"checkout",env:"prod",id:"123-456-abc",namespace:"default"},{key:"payments|prod",name:"payments",env:"prod",id:"456-789-def",namespace:"default"}],ranges:[{fromNs:"1704067200000000000",toNs:"1704067201200000000",durationNs:"1200000000",contributions:[{serviceKey:"checkout|prod",weight:1,durationNs:"1200000000"}],spanIds:["3df8cc3449378f66"]},{fromNs:"1704067201200000000",toNs:"1704067202400000000",durationNs:"1200000000",contributions:[{serviceKey:"checkout|prod",weight:.5,durationNs:"600000000"},{serviceKey:"payments|prod",weight:.5,durationNs:"600000000"}],spanIds:["187a47da1b2560f8","3df8cc3449378f66"]}],serviceTotals:[{serviceKey:"checkout|prod",durationNs:"1800000000",share:.75},{serviceKey:"payments|prod",durationNs:"600000000",share:.25}]};Object.assign(Sl,{examples:[K6]});var Gnt=d({tags:["traces"],url:`${oa}/latency-summary`,method:"GET",query:r.Object({...at.properties,traceId:u({allowEmpty:!1,description:"Trace ID to summarize"}),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"})),minRangeDurationMs:Ah,includeRanges:Oh}),response:Sl}),bk=r.Object({filter:u({type:"tsuga-query",description:"TQL filter defining the group"}),timeRange:li}),z6=r.Object({value:r.String(),supportA:r.Number(),supportB:r.Number()}),Y6=r.Object({value:r.String(),supportA:r.Number(),supportB:r.Number(),lift:r.Union([r.Number(),r.Null()]),pValue:r.Number()}),V6=r.Object({attr:r.String(),values:r.Array(Y6),otherValues:r.Array(z6)}),$nt=d({tags:["traces"],url:`${oa}/contrast-sets`,method:"POST",body:r.Object({groupA:bk,groupB:bk,candidateAttrs:r.Optional(r.Array(u({allowEmpty:!1}))),topK:r.Optional(r.Integer({minimum:1,maximum:100})),minLift:r.Optional(r.Number({minimum:1})),minSupport:r.Optional(r.Number({minimum:0,maximum:100})),clusterId:r.Optional(u({allowEmpty:!1,description:"Cluster ID"}))}),response:r.Object({contrastSets:r.Array(V6),groupACount:r.Number(),groupBCount:r.Number()})});var xh=`/${"v1"}/traces`,Rh=r.Object({context:r.Optional(r.Object({service:r.Optional(r.Object({name:r.Optional(r.String({description:"Service name associated with the span."})),version:r.Optional(r.String({description:"Service version associated with the span."}))},{additionalProperties:!0,description:"Service identity associated with the span, from OpenTelemetry service resource attributes."}))},{additionalProperties:!0,description:"OpenTelemetry resource context resolved for the span, holding the identity of the emitting service."})),parentSpanId:r.Optional(r.String({description:"`spanId` of this span's parent within the same trace. Absent for the root span."})),resourceAttributes:r.Object({},{additionalProperties:!0,description:"OpenTelemetry resource attributes describing the process/host that emitted the span (e.g. `host.name`, `k8s.pod.name`), as a flat key-value map set by the instrumented service or its SDK."}),spanAttributes:r.Object({},{additionalProperties:!0,description:"OpenTelemetry span attributes describing this operation (e.g. HTTP method, DB statement), as a flat key-value map set by the instrumented code or auto-instrumentation."}),duration:r.Number({description:"Span duration in milliseconds, derived from the nanosecond `startTime` and `endTime` values."}),startTime:r.String({description:"Span start time as a Unix epoch timestamp in nanoseconds, encoded as a string to preserve precision."}),endTime:r.String({description:"Span end time as a Unix epoch timestamp in nanoseconds, encoded as a string to preserve precision."}),error:r.Optional(r.Object({message:r.String({description:"Error message associated with the span."})},{description:"Error details associated with the span, separate from the OpenTelemetry `statusCode` field."})),events:r.Optional(r.Array(r.Object({event_attributes:r.Object({},{additionalProperties:!0,description:"Key-value attributes recorded with this event, set by the instrumented code when the event was added to the span (e.g. exception details)."}),event_name:r.String({description:"Name of the span event."}),event_timestamp_nanos:r.String({description:"Event timestamp in nanoseconds, transmitted as a string for precision."})},{description:"A single timestamped event recorded on the span, with its name and attributes."}),{description:"Events recorded on the span."})),spanId:r.String({description:"Unique span ID within the trace."}),span:r.Object({kind:r.String({description:"OpenTelemetry span kind describing the relationship to remote calls: `unspecified`, `internal`, `server`, `client`, `producer`, or `consumer`."}),name:r.String({description:"Operation name recorded on the span."})},{description:"Operation identity for the span, combining the recorded operation name and the OpenTelemetry span kind."}),statusCode:r.Optional(r.String({description:"OpenTelemetry span status set by the instrumented code: `unset` (default, no explicit status), `ok`, or `error`. Absent if the source span never set a status."})),traceId:r.String({description:"Trace ID shared by all spans in the trace."})},{additionalProperties:!0,$id:"SpanEvent",description:"Distributed tracing span including timing, identifiers, OpenTelemetry attributes, events, and status metadata."}),Q6={context:{service:{name:"my-service",version:"1.0.0"}},parentSpanId:"187a47da1b2560f8",resourceAttributes:{},spanAttributes:{busyNs:374640,code:{file:{path:"src/queue/sqs.rs"},line:{number:162},module:{name:"queue::sqs"}},idleNs:23621445,maxMessages:"1",target:"queue::sqs",thread:{id:2,name:"tokio-runtime-worker"}},duration:23,startTime:"1770923469995353600",endTime:"1770923470019349500",spanId:"3df8cc3449378f66",span:{kind:"internal",name:"receive_messages"},statusCode:"ok",traceId:"78c99c5bb1e4a30297fa134b9dbec64f"};Object.assign(Rh,{examples:[Q6]});var W6={from:1704067200,to:1704070800,query:"context.service.name:my-service",maxResults:100},Ok=r.Object({...bt.properties,query:r.Optional(u({type:"tsuga-query",default:"*",description:"Tsuga query selecting spans to return. Omit to search all spans in the range with `*`."})),maxResults:r.Optional(r.Integer({minimum:1,maximum:1e4,default:Ak,description:"Maximum number of spans to return. Defaults to 100. Valid values are 1 through 10000."})),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."}))},{description:"Span search query. Provide a time range, optional Tsuga query, and optional limit."});Object.assign(Ok,{examples:[W6]});var xk=d({description:"Searches spans in a required time range using Tsuga query syntax. Use this endpoint to retrieve trace spans for investigations or automation. See [Traces](/documentation/explore/traces) and [Query syntax](/documentation/explore/query-syntax).",operationId:"searchSpans",tags:["public","traces"],url:`${xh}/search`,method:"GET",query:Ok,response:r.Object({spans:r.Array(Rh,{description:"Spans matching the query and time range."})})}),Z6={from:1704067200,to:1704070800,traceId:"78c99c5bb1e4a30297fa134b9dbec64f"},Rk=r.Object({...bt.properties,traceId:u({allowEmpty:!1,description:"Trace ID to summarize. Use the `traceId` returned by span search, the trace details page, or your tracing instrumentation. Required; the requested time range must include the trace."}),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."})),groupByAttributes:r.Optional(r.Array(u({allowEmpty:!1}),{description:"Glob patterns of span attribute keys to use as additional grouping dimensions. Use this when spans should be summarized separately for matching attributes, such as `db.*`. Matches flattened dotted span attribute keys; omitted by default."})),minLeafSpansToAggregate:r.Optional(r.Integer({minimum:2,default:5,description:"Minimum number of similar leaf spans in a group before Tsuga replaces them with one synthetic summary span. Use higher values for less aggressive summarization. Optional; defaults to 5 and must be at least 2."})),maxParentDepth:r.Optional(r.Integer({minimum:-1,default:-1,description:"Maximum depth of recursive parent aggregation after leaf spans are summarized. Use this to limit parent-level aggregation. Use `0` to aggregate leaf spans only or `-1` for unlimited depth; defaults to `-1`."})),aggregationHistogramBucketsSeconds:r.Optional(r.Array(r.Number({minimum:0}),{description:"Upper-bound bucket values, in seconds, for the latency histogram recorded on synthetic summary spans. Provide custom buckets when the default latency buckets are not suitable. Pass an empty array to omit histogram attributes; omitted values use Tsuga defaults from 5 ms through 10 s."})),sortSpans:r.Optional(r.Boolean({default:!0,description:"Whether Tsuga sorts returned spans by start time, using span ID as the tie-breaker for identical start times. Defaults to `true`; set to `false` only when reconstructing the trace tree yourself, in which case summary spans are appended after surviving spans."}))},{description:"Trace summarization query. Provide a trace ID, a time range covering the trace, and optional aggregation tuning."});Object.assign(Rk,{examples:[Z6]});var Ck=d({description:"Summarizes a trace by aggregating groups of similar spans into synthetic summary spans, producing a compact view of large traces. Summary spans are marked with `spanAttributes.aggregation.is_summary` and carry span counts, duration statistics, and an optional latency histogram. See [Traces](/documentation/explore/traces).",operationId:"summarizeTrace",tags:["public","traces"],url:`${xh}/summarize`,method:"GET",query:Rk,response:r.Object({spans:r.Array(Rh,{description:"Summarized trace spans, sorted by start time unless `sortSpans` is false. Aggregated groups are replaced by synthetic summary spans marked with `spanAttributes.aggregation.is_summary`."})})}),X6={from:1704067200,to:1704070800,traceId:"78c99c5bb1e4a30297fa134b9dbec64f"},Pk=r.Object({...bt.properties,traceId:u({allowEmpty:!1,description:"Trace ID to summarize. Use the `traceId` returned by span search, the trace details page, or your tracing instrumentation. Required; the requested time range must include the trace."}),clusterId:r.Optional(u({allowEmpty:!1,description:"Telemetry cluster to target. Required when the organization has multiple clusters; omit only for single-cluster organizations."})),minRangeDurationMs:Ah,includeRanges:Oh},{description:"Trace latency summary query. Provide a trace ID, a time range covering the trace, and optional timeline tuning."});Object.assign(Pk,{examples:[X6]});var Uk=d({description:"Summarizes where wall-clock time is spent in a trace, attributing each slice of the trace timeline to the services with active leaf spans. Returns per-service totals and an optional sequential range timeline. See [Traces](/documentation/explore/traces).",operationId:"getTraceLatencySummary",tags:["public","traces"],url:`${xh}/latency-summary`,method:"GET",query:Pk,response:Sl});var Nk=["timestamp","level","message"],Lk=["count","ratio","team","level","pattern"];function gt(e,t){let n=t??jr(e);if(!n)throw new Error(`--${e} is required`);return Ic(n)}function wk(e){let t={from:gt("from",e.from),to:gt("to",e.to)},n=e.query??jr("query");n!==void 0&&(t.query=n);let o=e.maxResults??jr("max-results");if(o!==void 0){let i=Number(o);if(Number.isNaN(i))throw new Error(`--max-results must be a number (got: ${o})`);t.maxResults=i}return t}function Ne(e){let t=jr(e);return t===void 0?"":` (default: ${t})`}function J6(e){let t=Zm(e.output),n=e.fields===void 0?void 0:Xm(e.fields);return t==="json"?{format:t,fields:n}:{format:t,fields:n??Nk}}function e5(e){let t=Zm(e.output),n=e.fields===void 0?void 0:Xm(e.fields);return t==="json"?{format:t,fields:n}:{format:t,fields:n??Lk}}function vk(e,t){let n={count:e.size,ratio:t>0?e.size/t:0,pattern:e.pattern};for(let{key:o,value:i}of e.groups)o==="context.team"?n.team=i:n[o]=i;return n}function t5(e,t){if(t.format==="json"){if(t.fields===void 0){ee(e);return}let{fields:i}=t;ee({patterns:e.patterns.map(a=>vk(a,e.sampleSize)).map(a=>Jm(a,i)),sampleSize:e.sampleSize});return}let n=e.patterns.map(i=>vk(i,e.sampleSize)),o=ed(t.format,n,t.fields);o.length>0&&V(o)}function r5(e,t){if(t.format==="json"){if(t.fields===void 0){ee(e);return}let{fields:o}=t;ee({logs:e.logs.map(i=>Jm(i,o))});return}let n=ed(t.format,e.logs,t.fields);n.length>0&&V(n)}function kk(e,t){let n=e.command("logs").description("Search and analyze logs");n.command("search").description("Search logs").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).option("--query <TSUGA_QUERY>",`Tsuga query to filter logs${Ne("query")}`).option("--max-results <COUNT>",`Maximum number of results, 1-1000${Ne("max-results")}`).option("--fields <FIELDS>",`Comma-separated dot-paths to project from each log (default for --output=tsv|csv: ${Nk.join(",")})`).option("-o, --output <FORMAT>","Output format: json | tsv | csv","json").action(async c=>{let l=J6(c),m=await t()(sk,{query:wk(c)});r5(m,l)}),n.command("attributes").description("List log attribute dot-paths observed in the time range").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).action(async c=>{let l=await t()(gk,{query:{from:gt("from",c.from),to:gt("to",c.to)}});ee(l)}),n.command("patterns").description("List log patterns").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).option("--query <TSUGA_QUERY>",`Tsuga query to filter logs before clustering into patterns${Ne("query")}`).option("--fields <FIELDS>",`Comma-separated dot-paths to project from each pattern (default for --output=tsv|csv: ${Lk.join(",")})`).option("-o, --output <FORMAT>","Output format: json | tsv | csv","json").action(async c=>{let l=e5(c),m={from:gt("from",c.from),to:gt("to",c.to)},f=c.query??jr("query");f!==void 0&&(m.query=f);let h=await t()(pk,{query:m});t5(h,l)}),n.command("new-error-patterns").description("List error patterns first seen in this time range").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).option("--team <TEAM_NAME>","Filter by team").option("--env <ENV_NAME>","Filter by environment").option("--service <SERVICE_NAME>","Filter by service").action(async c=>{let l={from:gt("from",c.from),to:gt("to",c.to)};c.team!==void 0&&(l.team=c.team),c.env!==void 0&&(l.env=c.env),c.service!==void 0&&(l.service=c.service);let m=await t()(lk,{query:l});ee(m)}),n.command("error-pattern-increases").description("List error pattern increases for a given team").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).requiredOption("--team <TEAM_NAME>","Team to query").option("--env <ENV_NAME>","Filter by environment").action(async c=>{let l={from:gt("from",c.from),to:gt("to",c.to),team:c.team};c.env!==void 0&&(l.env=c.env);let m=await t()(dk,{query:l});ee(m)});let o=e.command("metrics").description("List and inspect metrics");o.command("list").description("List all metrics with their metadata").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).action(async c=>{let l=await t()(Tk,{query:{from:gt("from",c.from),to:gt("to",c.to)}});ee(l)}),o.command("get <METRIC_NAME>").description("Retrieve a metric by name with its metadata").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).action(async(c,l)=>{let m=await t()(Ek,{params:{name:c},query:{from:gt("from",l.from),to:gt("to",l.to)}});ee(m)}),o.command("assets-usage <METRIC_NAME>").description("List dashboards and monitors that use a specific metric").action(async c=>{let l=await t()(Ik,{query:{metricName:c}});ee(l)});let i=e.command("traces").description("Search and summarize traces");i.command("search").description("Search spans").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).option("--query <TSUGA_QUERY>",`Tsuga query to filter spans${Ne("query")}`).option("--max-results <COUNT>",`Maximum number of results, 1-10000${Ne("max-results")}`).action(async c=>{let l=await t()(xk,{query:wk(c)});ee(l)}),i.command("summarize").description("Summarize a trace by aggregating groups of similar spans into summary spans").requiredOption("--trace-id <TRACE_ID>","Trace ID to summarize").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).action(async c=>{let l=await t()(Ck,{query:{traceId:c.traceId,from:gt("from",c.from),to:gt("to",c.to)}});ee(l)}),i.command("latency-summary").description("Summarize where wall-clock time is spent in a trace, attributed per service").requiredOption("--trace-id <TRACE_ID>","Trace ID to summarize").option("--from <TIMESTAMP>",`Start of time range${Ge}${Ne("from")}`).option("--to <TIMESTAMP>",`End of time range${Ge}${Ne("to")}`).option("--min-range-duration-ms <MILLISECONDS>","Collapse timeline ranges shorter than this (default: max(1ms, 1% of trace duration); 0 disables collapsing)").option("--no-ranges","Omit the per-range timeline; return only services and serviceTotals").action(async c=>{let l={traceId:c.traceId,from:gt("from",c.from),to:gt("to",c.to)};if(c.minRangeDurationMs!==void 0){let f=Number(c.minRangeDurationMs);if(Number.isNaN(f))throw new Error(`--min-range-duration-ms must be a number (got: ${c.minRangeDurationMs})`);l.minRangeDurationMs=f}c.ranges||(l.includeRanges=!1);let m=await t()(Uk,{query:l});ee(m)});let a=e.command("aggregation").description("Run aggregation queries");Zr(a,"scalar","Run aggregation queries and return scalar results",Vw,t),Zr(a,"timeseries","Run aggregation queries and return timeseries results",Ww,t)}var n5="x-tsuga-session-id",Ch="x-tsuga-source",Ph="x-tsuga-source-version",Uh="x-tsuga-source-commit",wh="x-tsuga-source-date",vh="x-tsuga-rationale",o5="x-tsuga-dashboard-id",i5="x-tsuga-widget-id",a5="x-request-id",oot={[n5]:"Session identifier for request correlation",[Ch]:"SDK or client source identifier (e.g., web-frontend, terraform-provider)",[Ph]:"SDK version",[Uh]:"SDK commit hash",[wh]:"SDK commit date",[vh]:"Optional caller-supplied rationale for this request, recorded for telemetry",[o5]:"ID of the dashboard a query is issued from, when the request originates from a dashboard widget",[i5]:"ID of the dashboard widget (graph) a query is issued from, when the request originates from a dashboard widget",[a5]:"Unique identifier for this API request, useful for debugging and support"};var Mn=class e extends Error{constructor(n,o,i,a,c){super(n);this.statusCode=o;this.code=i;this.requestId=a;this.details=c;this.name="CliError"}static fromErrorResponse(n,o){return new e(n.message,n.statusCode,n.code,o,n.details)}};function Mk(e){if(e instanceof Mn){let t=[`Error: ${e.message}`];return e.code&&t.push(`Code: ${e.code}`),e.statusCode&&t.push(`Status: ${e.statusCode}`),e.requestId&&t.push(`Request ID: ${e.requestId}`),e.details&&t.push(`Details: ${JSON.stringify(e.details,null,2)}`),t.join(`
173
- `)}return e instanceof Error?`Error: ${e.message}`:String(e)}function s5(e){return e.replace(/\s+/g," ").replace(/[^\x20-\x7e\x80-\xff]/g,"").trim()}function Dk(e,t,n,o){return async function(a,c={}){let{body:l,params:m,query:f}=c,h;if(a.url!==void 0)h=a.url;else if(a.buildUrl){if(!m)throw new Mn("Internal error: params required for this endpoint");h=a.buildUrl(m)}else throw new Mn("Internal error: endpoint has neither url nor buildUrl");let E=n===void 0?f:{clusterId:n,...f},I=oO(`${e}${h}`,E),g={Accept:"application/json",Authorization:`Bearer ${t}`,[Ch]:"cli",[Ph]:"1.26.0",[Uh]:"792428896",[wh]:"2026-08-31T07:14:11.378Z"};l&&(g["Content-Type"]="application/json");let T=o?.(),S=T?s5(T):"";S&&(g[vh]=S);let b=await fetch(I,{method:a.method,headers:g,body:l?JSON.stringify(l):null}),x;try{x=await b.json()}catch{throw new Mn(b.status===504?"The server timed out (504). Try a smaller time range.":`Server returned a non-JSON response (HTTP ${b.status}).`,b.status)}if(x.error)throw Mn.fromErrorResponse(x.error,x.requestId);return p5(x,c5(c)),x.data}}function c5({body:e,query:t}){let n=t?.offset??e?.offset;return typeof n=="number"?n:0}function p5(e,t){let n=e.metadata?.pagination;if(n===void 0||!Array.isArray(e.data))return;let o=t+e.data.length;o>=n.totalCount||hO(`Showing ${e.data.length} of ${n.totalCount} (offset ${t}). Use --limit (max ${oi}) and --offset ${o} for the next page.`)}function Nh(e,t=n=>{process.exit(n)}){e.on("error",n=>{if(n.code==="EPIPE"){t(process.exitCode);return}throw n})}var u5=3e3,l5="https://registry.npmjs.org/@tsuga/cli/latest";function m5(e,t){let n=e.split(".").map(Number),o=t.split(".").map(Number);for(let i=0;i<Math.max(n.length,o.length);i++){let a=n[i]??0,c=o[i]??0;if(a>c)return!0;if(a<c)return!1}return!1}async function Gk(e){try{let t=JO();if(t&&Date.now()-new Date(t).getTime()<_c)return;let n=await fetch(l5,{signal:AbortSignal.timeout(u5)});if(!n.ok)return;let i=(await n.json()).version;m5(i,e)&&(process.stderr.write(`
173
+ `)}return e instanceof Error?`Error: ${e.message}`:String(e)}function s5(e){return e.replace(/\s+/g," ").replace(/[^\x20-\x7e\x80-\xff]/g,"").trim()}function Dk(e,t,n,o){return async function(a,c={}){let{body:l,params:m,query:f}=c,h;if(a.url!==void 0)h=a.url;else if(a.buildUrl){if(!m)throw new Mn("Internal error: params required for this endpoint");h=a.buildUrl(m)}else throw new Mn("Internal error: endpoint has neither url nor buildUrl");let E=n===void 0?f:{clusterId:n,...f},I=oO(`${e}${h}`,E),g={Accept:"application/json",Authorization:`Bearer ${t}`,[Ch]:"cli",[Ph]:"1.27.0",[Uh]:"196d5a9ff",[wh]:"2026-08-31T09:48:56.629Z"};l&&(g["Content-Type"]="application/json");let T=o?.(),S=T?s5(T):"";S&&(g[vh]=S);let b=await fetch(I,{method:a.method,headers:g,body:l?JSON.stringify(l):null}),x;try{x=await b.json()}catch{throw new Mn(b.status===504?"The server timed out (504). Try a smaller time range.":`Server returned a non-JSON response (HTTP ${b.status}).`,b.status)}if(x.error)throw Mn.fromErrorResponse(x.error,x.requestId);return p5(x,c5(c)),x.data}}function c5({body:e,query:t}){let n=t?.offset??e?.offset;return typeof n=="number"?n:0}function p5(e,t){let n=e.metadata?.pagination;if(n===void 0||!Array.isArray(e.data))return;let o=t+e.data.length;o>=n.totalCount||hO(`Showing ${e.data.length} of ${n.totalCount} (offset ${t}). Use --limit (max ${oi}) and --offset ${o} for the next page.`)}function Nh(e,t=n=>{process.exit(n)}){e.on("error",n=>{if(n.code==="EPIPE"){t(process.exitCode);return}throw n})}var u5=3e3,l5="https://registry.npmjs.org/@tsuga/cli/latest";function m5(e,t){let n=e.split(".").map(Number),o=t.split(".").map(Number);for(let i=0;i<Math.max(n.length,o.length);i++){let a=n[i]??0,c=o[i]??0;if(a>c)return!0;if(a<c)return!1}return!1}async function Gk(e){try{let t=JO();if(t&&Date.now()-new Date(t).getTime()<_c)return;let n=await fetch(l5,{signal:AbortSignal.timeout(u5)});if(!n.ok)return;let i=(await n.json()).version;m5(i,e)&&(process.stderr.write(`
174
174
  Update available: ${e} \u2192 ${i}
175
- `),process.stderr.write("Run `tsuga self-update` to update\n")),ex(new Date().toISOString())}catch{}}Nh(process.stdout);Nh(process.stderr);var Re=new Po().name("tsuga").description("Manage Tsuga resources, telemetry, and configuration from the command line").version("1.26.0").addHelpText("before",()=>`${dO()}
176
- `).configureHelp({sortSubcommands:!0}).helpOption("-h, --help","Display help for command").helpCommand("help [command]","Display help for command").option("--cluster <CLUSTER_ID>","Target cluster (overrides config and TSUGA_CLUSTER_ID)"),ia,$k;function Nr(){if(ia)return ia;let e=Re.opts(),t=si({clusterId:e.cluster}),n=HO(t);n||(console.error("Error: no credentials. Authenticate with one of:"),console.error(" tsuga auth login # log in as a user (OAuth)"),console.error(" tsuga auth operation-key <OPERATION_API_KEY> # save an operation API key"),console.error(" export TSUGA_OPERATION_API_KEY=<OPERATION_API_KEY>"),process.exit(1));let o=l=>Dk(t.apiUrl,l,t.clusterId,()=>$k),{credential:i}=t;if(i?.kind!=="user-session")return ia=o(n),ia;let a=i.userAuth,c=o(a.accessToken);return ia=async(l,m)=>{let f=await Fp({userAuth:a,now:new Date,reload:Ja,refresh:h=>$p(t.apiUrl,h),persist:Yp});return f!==a&&(a=f,c=o(a.accessToken)),c(l,m)},ia}var jk="Setup & config:",Fk="Documentation, help & feedback:";Re.commandsGroup(jk);rx(Re);Wx(Re);var d5=new Set(Re.commands);Re.commandsGroup("Resources:");KL(Re,Nr);Re.commandsGroup("Query & explore:");kk(Re,Nr);QR(Re,Nr);rw(Re,Nr);cw(Re,Nr);Fw(Re,Nr);Xw(Re,Nr);Re.commandsGroup(Fk);rR(Re,Nr);aR(Re,Nr);Re.commandsGroup(jk);zx(Re,Nr);nk(Re);aw(Re);YL(Re);Qx(Re);Re.commandsGroup(Fk).helpCommand("help [command]");function qk(e){if(e.commands.length===0){e.option("--rationale <RATIONALE>",'Why you are making this call, e.g. "confirming the deploy caused the error spike". Recorded to explain intent; does not affect the result.');return}for(let t of e.commands)qk(t)}var f5=new Set(["completion","self-update","install","setup","feedback"]);for(let e of Re.commands)!d5.has(e)&&!f5.has(e.name())&&qk(e);Re.hook("preAction",(e,t)=>{let{rationale:n}=t.opts();$k=n});Re.parseAsync().then(()=>Gk("1.26.0")).catch(e=>{console.error(Mk(e)),process.exitCode=1});
175
+ `),process.stderr.write("Run `tsuga self-update` to update\n")),ex(new Date().toISOString())}catch{}}Nh(process.stdout);Nh(process.stderr);var Re=new Po().name("tsuga").description("Manage Tsuga resources, telemetry, and configuration from the command line").version("1.27.0").addHelpText("before",()=>`${dO()}
176
+ `).configureHelp({sortSubcommands:!0}).helpOption("-h, --help","Display help for command").helpCommand("help [command]","Display help for command").option("--cluster <CLUSTER_ID>","Target cluster (overrides config and TSUGA_CLUSTER_ID)"),ia,$k;function Nr(){if(ia)return ia;let e=Re.opts(),t=si({clusterId:e.cluster}),n=HO(t);n||(console.error("Error: no credentials. Authenticate with one of:"),console.error(" tsuga auth login # log in as a user (OAuth)"),console.error(" tsuga auth operation-key <OPERATION_API_KEY> # save an operation API key"),console.error(" export TSUGA_OPERATION_API_KEY=<OPERATION_API_KEY>"),process.exit(1));let o=l=>Dk(t.apiUrl,l,t.clusterId,()=>$k),{credential:i}=t;if(i?.kind!=="user-session")return ia=o(n),ia;let a=i.userAuth,c=o(a.accessToken);return ia=async(l,m)=>{let f=await Fp({userAuth:a,now:new Date,reload:Ja,refresh:h=>$p(t.apiUrl,h),persist:Yp});return f!==a&&(a=f,c=o(a.accessToken)),c(l,m)},ia}var jk="Setup & config:",Fk="Documentation, help & feedback:";Re.commandsGroup(jk);rx(Re);Wx(Re);var d5=new Set(Re.commands);Re.commandsGroup("Resources:");KL(Re,Nr);Re.commandsGroup("Query & explore:");kk(Re,Nr);QR(Re,Nr);rw(Re,Nr);cw(Re,Nr);Fw(Re,Nr);Xw(Re,Nr);Re.commandsGroup(Fk);rR(Re,Nr);aR(Re,Nr);Re.commandsGroup(jk);zx(Re,Nr);nk(Re);aw(Re);YL(Re);Qx(Re);Re.commandsGroup(Fk).helpCommand("help [command]");function qk(e){if(e.commands.length===0){e.option("--rationale <RATIONALE>",'Why you are making this call, e.g. "confirming the deploy caused the error spike". Recorded to explain intent; does not affect the result.');return}for(let t of e.commands)qk(t)}var f5=new Set(["completion","self-update","install","setup","feedback"]);for(let e of Re.commands)!d5.has(e)&&!f5.has(e.name())&&qk(e);Re.hook("preAction",(e,t)=>{let{rationale:n}=t.opts();$k=n});Re.parseAsync().then(()=>Gk("1.27.0")).catch(e=>{console.error(Mk(e)),process.exitCode=1});
177
177
  /**
178
178
  * The following is a list of HTTP status codes and their meanings.
179
179
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tsuga/cli",
3
3
  "description": "Tsuga CLI - manage resources from the command line",
4
- "version": "1.26.0",
4
+ "version": "1.27.0",
5
5
  "bin": {
6
6
  "tsuga": "dist/cli.js"
7
7
  },