duoops 0.0.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.
- package/LICENSE +22 -0
- package/README.md +181 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +7 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +8 -0
- package/dist/commands/act.d.ts +12 -0
- package/dist/commands/act.js +61 -0
- package/dist/commands/ask.d.ts +8 -0
- package/dist/commands/ask.js +22 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +97 -0
- package/dist/commands/job/logs.d.ts +13 -0
- package/dist/commands/job/logs.js +26 -0
- package/dist/commands/measure/calculate.d.ts +19 -0
- package/dist/commands/measure/calculate.js +208 -0
- package/dist/commands/measure/component.d.ts +5 -0
- package/dist/commands/measure/component.js +23 -0
- package/dist/commands/measure/seed.d.ts +5 -0
- package/dist/commands/measure/seed.js +62 -0
- package/dist/commands/pipelines/list.d.ts +14 -0
- package/dist/commands/pipelines/list.js +62 -0
- package/dist/commands/pipelines/show.d.ts +13 -0
- package/dist/commands/pipelines/show.js +68 -0
- package/dist/commands/portal.d.ts +8 -0
- package/dist/commands/portal.js +139 -0
- package/dist/commands/undo.d.ts +5 -0
- package/dist/commands/undo.js +35 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/ai/agent.d.ts +6 -0
- package/dist/lib/ai/agent.js +139 -0
- package/dist/lib/ai/model.d.ts +2 -0
- package/dist/lib/ai/model.js +22 -0
- package/dist/lib/ai/tools/editing.d.ts +3 -0
- package/dist/lib/ai/tools/editing.js +61 -0
- package/dist/lib/ai/tools/filesystem.d.ts +4 -0
- package/dist/lib/ai/tools/filesystem.js +44 -0
- package/dist/lib/ai/tools/gitlab.d.ts +4 -0
- package/dist/lib/ai/tools/gitlab.js +81 -0
- package/dist/lib/ai/tools/measure.d.ts +3 -0
- package/dist/lib/ai/tools/measure.js +26 -0
- package/dist/lib/config.d.ts +18 -0
- package/dist/lib/config.js +72 -0
- package/dist/lib/gitlab/client.d.ts +6 -0
- package/dist/lib/gitlab/client.js +18 -0
- package/dist/lib/gitlab/index.d.ts +6 -0
- package/dist/lib/gitlab/index.js +49 -0
- package/dist/lib/gitlab/provider.d.ts +14 -0
- package/dist/lib/gitlab/provider.js +72 -0
- package/dist/lib/gitlab/types.d.ts +34 -0
- package/dist/lib/gitlab/types.js +5 -0
- package/dist/lib/integrations/bigquery-sink.d.ts +12 -0
- package/dist/lib/integrations/bigquery-sink.js +47 -0
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.js +11 -0
- package/dist/lib/measure/bigquery-service.d.ts +2 -0
- package/dist/lib/measure/bigquery-service.js +54 -0
- package/dist/lib/measure/carbon-calculator.d.ts +13 -0
- package/dist/lib/measure/carbon-calculator.js +125 -0
- package/dist/lib/measure/cli-utils.d.ts +2 -0
- package/dist/lib/measure/cli-utils.js +107 -0
- package/dist/lib/measure/intensity-provider.d.ts +6 -0
- package/dist/lib/measure/intensity-provider.js +34 -0
- package/dist/lib/measure/power-profile-repository.d.ts +19 -0
- package/dist/lib/measure/power-profile-repository.js +129 -0
- package/dist/lib/measure/types.d.ts +137 -0
- package/dist/lib/measure/types.js +1 -0
- package/dist/lib/measure/zone-mapper.d.ts +16 -0
- package/dist/lib/measure/zone-mapper.js +104 -0
- package/dist/lib/state.d.ts +4 -0
- package/dist/lib/state.js +21 -0
- package/dist/portal/assets/index-BP8FwWqA.css +1 -0
- package/dist/portal/assets/index-MU6EBerh.js +188 -0
- package/dist/portal/duoops.svg +4 -0
- package/dist/portal/index.html +24 -0
- package/dist/portal/vite.svg +1 -0
- package/oclif.manifest.json +415 -0
- package/package.json +103 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export class PowerProfileRepository {
|
|
4
|
+
dataDir;
|
|
5
|
+
awsMachines = null;
|
|
6
|
+
cpuPhysicalSpecs = null;
|
|
7
|
+
cpuProfiles = null;
|
|
8
|
+
gcpMachines = null;
|
|
9
|
+
constructor(dataDir) {
|
|
10
|
+
this.dataDir = dataDir;
|
|
11
|
+
}
|
|
12
|
+
async getCpuPowerProfile(provider, machineType) {
|
|
13
|
+
const profile = await this.getMachineProfile(provider, machineType);
|
|
14
|
+
if (!profile)
|
|
15
|
+
return null;
|
|
16
|
+
let basePowerProfile = null;
|
|
17
|
+
let cpuName;
|
|
18
|
+
if (profile.cpuPowerProfile && profile.cpuPowerProfile.length > 0) {
|
|
19
|
+
basePowerProfile = profile.cpuPowerProfile;
|
|
20
|
+
cpuName = profile.matchedCpuProfile || profile.platformCpu;
|
|
21
|
+
}
|
|
22
|
+
else if (profile.matchedCpuProfile) {
|
|
23
|
+
const cpuProfiles = this.loadCpuProfiles();
|
|
24
|
+
const rawCpu = cpuProfiles[profile.matchedCpuProfile];
|
|
25
|
+
if (rawCpu) {
|
|
26
|
+
basePowerProfile = this.normalizePowerPoints(rawCpu.power_profile);
|
|
27
|
+
cpuName = profile.matchedCpuProfile;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!basePowerProfile)
|
|
31
|
+
return null;
|
|
32
|
+
// Apply physical vCPU correction for accurate power estimation
|
|
33
|
+
// Formula: power_per_vm = (TDP / physical_threads) * vm_vcpus * ratio
|
|
34
|
+
// The base profile has watts scaled by vm_vcpus, we need to correct by dividing
|
|
35
|
+
// by vm_vcpus and multiplying by (vm_vcpus / physical_threads)
|
|
36
|
+
const physicalSpec = cpuName ? this.findPhysicalSpec(cpuName) : null;
|
|
37
|
+
if (physicalSpec && physicalSpec.threads > 0 && profile.vcpus > 0) {
|
|
38
|
+
// The gcp_machine_power_profiles.json has: watts = TDP * ratio * vm_vcpus (inflated)
|
|
39
|
+
// Correct formula: watts = TDP * ratio * (vm_vcpus / physical_threads)
|
|
40
|
+
// Correction factor = correct / inflated = 1 / physical_threads
|
|
41
|
+
const correctionFactor = 1 / physicalSpec.threads;
|
|
42
|
+
return basePowerProfile.map((p) => ({
|
|
43
|
+
percentage: p.percentage,
|
|
44
|
+
watts: p.watts * correctionFactor,
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
// Fallback: return profile as-is (may be overestimated for GCP)
|
|
48
|
+
return basePowerProfile;
|
|
49
|
+
}
|
|
50
|
+
async getMachineProfile(provider, machineType) {
|
|
51
|
+
const machines = this.loadMachineData(provider);
|
|
52
|
+
const raw = machines[machineType];
|
|
53
|
+
if (!raw)
|
|
54
|
+
return null;
|
|
55
|
+
const powerProfile = raw.cpu_power_profile ? this.normalizePowerPoints(raw.cpu_power_profile) : [];
|
|
56
|
+
return {
|
|
57
|
+
cpuPowerProfile: powerProfile,
|
|
58
|
+
machineType,
|
|
59
|
+
matchedCpuProfile: raw.matched_cpu_profile,
|
|
60
|
+
memoryGb: this.normalizeNumber(raw.memory_gb),
|
|
61
|
+
platformCpu: raw.platform_cpu,
|
|
62
|
+
scope3EmissionsHourly: raw.scope3_emissions_hourly,
|
|
63
|
+
vcpus: this.normalizeNumber(raw.vcpus),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
listMachines(provider) {
|
|
67
|
+
const machines = this.loadMachineData(provider);
|
|
68
|
+
return Object.keys(machines).sort();
|
|
69
|
+
}
|
|
70
|
+
findPhysicalSpec(cpuName) {
|
|
71
|
+
const specs = this.loadCpuPhysicalSpecs();
|
|
72
|
+
// Try exact match first
|
|
73
|
+
if (specs[cpuName] && typeof specs[cpuName] === 'object' && 'threads' in specs[cpuName]) {
|
|
74
|
+
return specs[cpuName];
|
|
75
|
+
}
|
|
76
|
+
// Try partial match
|
|
77
|
+
for (const [key, spec] of Object.entries(specs)) {
|
|
78
|
+
if (key === '_metadata')
|
|
79
|
+
continue;
|
|
80
|
+
if (cpuName.includes(key) || key.includes(cpuName)) {
|
|
81
|
+
return spec;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
loadCpuPhysicalSpecs() {
|
|
87
|
+
if (!this.cpuPhysicalSpecs) {
|
|
88
|
+
this.cpuPhysicalSpecs = this.loadJsonFile('cpu_physical_specs.json');
|
|
89
|
+
}
|
|
90
|
+
return this.cpuPhysicalSpecs;
|
|
91
|
+
}
|
|
92
|
+
loadCpuProfiles() {
|
|
93
|
+
if (!this.cpuProfiles) {
|
|
94
|
+
this.cpuProfiles = this.loadJsonFile('cpu_power_profiles.json');
|
|
95
|
+
}
|
|
96
|
+
return this.cpuProfiles;
|
|
97
|
+
}
|
|
98
|
+
loadJsonFile(filename) {
|
|
99
|
+
const filePath = path.join(this.dataDir, filename);
|
|
100
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
101
|
+
return JSON.parse(raw);
|
|
102
|
+
}
|
|
103
|
+
loadMachineData(provider) {
|
|
104
|
+
if (provider === 'gcp') {
|
|
105
|
+
if (!this.gcpMachines) {
|
|
106
|
+
this.gcpMachines = this.loadJsonFile('gcp_machine_power_profiles.json');
|
|
107
|
+
}
|
|
108
|
+
return this.gcpMachines;
|
|
109
|
+
}
|
|
110
|
+
if (!this.awsMachines) {
|
|
111
|
+
this.awsMachines = this.loadJsonFile('aws_machine_power_profiles.json');
|
|
112
|
+
}
|
|
113
|
+
return this.awsMachines;
|
|
114
|
+
}
|
|
115
|
+
normalizeNumber(value) {
|
|
116
|
+
if (typeof value === 'number')
|
|
117
|
+
return value;
|
|
118
|
+
if (!value)
|
|
119
|
+
return 0;
|
|
120
|
+
const parsed = Number.parseFloat(value);
|
|
121
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
122
|
+
}
|
|
123
|
+
normalizePowerPoints(points) {
|
|
124
|
+
return points.map((p) => ({
|
|
125
|
+
percentage: Number(p.percentage),
|
|
126
|
+
watts: Number(p.watts),
|
|
127
|
+
})).sort((a, b) => a.percentage - b.percentage);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export type CloudProvider = 'aws' | 'gcp';
|
|
2
|
+
export interface CliCalculateOptions {
|
|
3
|
+
awsEndTime?: string;
|
|
4
|
+
awsInstanceId?: string;
|
|
5
|
+
awsPeriod?: number;
|
|
6
|
+
awsRegion?: string;
|
|
7
|
+
awsStartTime?: string;
|
|
8
|
+
budget?: number;
|
|
9
|
+
cpuTimeseries?: string;
|
|
10
|
+
failOnBudget?: boolean;
|
|
11
|
+
fromCloudwatch?: boolean;
|
|
12
|
+
gitlab?: boolean;
|
|
13
|
+
machine?: string;
|
|
14
|
+
outJson?: string;
|
|
15
|
+
outMd?: string;
|
|
16
|
+
postNote?: boolean;
|
|
17
|
+
provider?: CloudProvider;
|
|
18
|
+
ramSizeTimeseries?: string;
|
|
19
|
+
ramUsedTimeseries?: string;
|
|
20
|
+
region?: string;
|
|
21
|
+
runnerTags?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PowerPoint {
|
|
24
|
+
percentage: number;
|
|
25
|
+
watts: number;
|
|
26
|
+
}
|
|
27
|
+
export interface MachineProfile {
|
|
28
|
+
cpuPowerProfile: PowerPoint[];
|
|
29
|
+
machineType: string;
|
|
30
|
+
matchedCpuProfile?: string;
|
|
31
|
+
memoryGb: number;
|
|
32
|
+
platformCpu?: string;
|
|
33
|
+
scope3EmissionsHourly?: number;
|
|
34
|
+
vcpus: number;
|
|
35
|
+
}
|
|
36
|
+
export interface TimeseriesPoint {
|
|
37
|
+
timestamp: string;
|
|
38
|
+
value: number;
|
|
39
|
+
}
|
|
40
|
+
export interface GcpMetricPointValue {
|
|
41
|
+
doubleValue?: number;
|
|
42
|
+
int64Value?: number | string;
|
|
43
|
+
}
|
|
44
|
+
export interface GcpMetricPoint {
|
|
45
|
+
interval?: {
|
|
46
|
+
endTime?: string;
|
|
47
|
+
startTime?: string;
|
|
48
|
+
};
|
|
49
|
+
value?: GcpMetricPointValue;
|
|
50
|
+
}
|
|
51
|
+
export interface GcpTimeSeries {
|
|
52
|
+
points?: GcpMetricPoint[];
|
|
53
|
+
}
|
|
54
|
+
export interface GcpMonitoringResponse {
|
|
55
|
+
timeSeries?: GcpTimeSeries[];
|
|
56
|
+
}
|
|
57
|
+
export interface CloudWatchMetricDataResult {
|
|
58
|
+
Id?: string;
|
|
59
|
+
Timestamps?: Array<Date | number | string>;
|
|
60
|
+
Values?: Array<number | string>;
|
|
61
|
+
}
|
|
62
|
+
export interface CloudWatchMetricDataEnvelope {
|
|
63
|
+
MetricDataResults?: CloudWatchMetricDataResult[];
|
|
64
|
+
metricDataResults?: CloudWatchMetricDataResult[];
|
|
65
|
+
}
|
|
66
|
+
export type CloudWatchMetricDataResultArray = CloudWatchMetricDataResult[];
|
|
67
|
+
export interface CloudWatchDatapoint {
|
|
68
|
+
Average?: number;
|
|
69
|
+
Maximum?: number;
|
|
70
|
+
Minimum?: number;
|
|
71
|
+
Sum?: number;
|
|
72
|
+
Timestamp?: Date | number | string;
|
|
73
|
+
}
|
|
74
|
+
export interface CloudWatchDatapointResponse {
|
|
75
|
+
Datapoints?: CloudWatchDatapoint[];
|
|
76
|
+
}
|
|
77
|
+
export interface LegacyTimeseriesPoint {
|
|
78
|
+
timestamp?: Date | number | string;
|
|
79
|
+
Timestamp?: Date | number | string;
|
|
80
|
+
value?: number | string;
|
|
81
|
+
Value?: number | string;
|
|
82
|
+
}
|
|
83
|
+
export type LegacyTimeseriesArray = LegacyTimeseriesPoint[];
|
|
84
|
+
export type TimestampLike = Date | number | string;
|
|
85
|
+
export type NumericLike = number | string;
|
|
86
|
+
export type TimeseriesFileInput = CloudWatchDatapointResponse | CloudWatchMetricDataEnvelope | CloudWatchMetricDataResultArray | GcpMonitoringResponse | LegacyTimeseriesArray;
|
|
87
|
+
export interface JobInput {
|
|
88
|
+
cpuTimeseries: TimeseriesPoint[];
|
|
89
|
+
machineType: string;
|
|
90
|
+
provider: CloudProvider;
|
|
91
|
+
ramSizeTimeseries: TimeseriesPoint[];
|
|
92
|
+
ramUsedTimeseries: TimeseriesPoint[];
|
|
93
|
+
region: string;
|
|
94
|
+
}
|
|
95
|
+
export interface EmissionsResult {
|
|
96
|
+
carbonIntensity: number;
|
|
97
|
+
cpuEmissions: number;
|
|
98
|
+
cpuEnergyKwh: number;
|
|
99
|
+
machineType: string;
|
|
100
|
+
provider: CloudProvider;
|
|
101
|
+
pue: number;
|
|
102
|
+
ramEmissions: number;
|
|
103
|
+
ramEnergyKwh: number;
|
|
104
|
+
region: string;
|
|
105
|
+
runtimeHours: number;
|
|
106
|
+
scope3Emissions: number;
|
|
107
|
+
totalEmissions: number;
|
|
108
|
+
}
|
|
109
|
+
export interface GitLabContext {
|
|
110
|
+
jobId: number;
|
|
111
|
+
jobToken: string;
|
|
112
|
+
mergeRequestIid?: number;
|
|
113
|
+
pipelineId?: number;
|
|
114
|
+
projectId: number;
|
|
115
|
+
serverUrl: string;
|
|
116
|
+
}
|
|
117
|
+
export interface CarbonReport {
|
|
118
|
+
json: Record<string, unknown>;
|
|
119
|
+
markdown: string;
|
|
120
|
+
summary: string;
|
|
121
|
+
}
|
|
122
|
+
export interface BudgetOptions {
|
|
123
|
+
carbonBudgetGrams?: number;
|
|
124
|
+
failOnBreach?: boolean;
|
|
125
|
+
}
|
|
126
|
+
export interface BudgetResult {
|
|
127
|
+
budgetConfigured: boolean;
|
|
128
|
+
limitGrams?: number;
|
|
129
|
+
overBudget: boolean;
|
|
130
|
+
}
|
|
131
|
+
export type ErrorLike = boolean | Error | null | number | string | undefined;
|
|
132
|
+
export interface AwsInstanceSummary {
|
|
133
|
+
instanceId: string;
|
|
134
|
+
name?: string;
|
|
135
|
+
state?: string;
|
|
136
|
+
type?: string;
|
|
137
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CloudProvider } from './types.js';
|
|
2
|
+
export interface ZoneResolution {
|
|
3
|
+
pue: number;
|
|
4
|
+
zone: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ZoneMapper {
|
|
7
|
+
private readonly dataDir;
|
|
8
|
+
private readonly fallbackPue;
|
|
9
|
+
private readonly awsZoneMapping;
|
|
10
|
+
private readonly gcpZoneMapping;
|
|
11
|
+
private runtimePueData;
|
|
12
|
+
constructor(dataDir: string, fallbackPue?: number);
|
|
13
|
+
resolve(provider: CloudProvider, region: string): ZoneResolution;
|
|
14
|
+
private loadRuntimePueData;
|
|
15
|
+
private normalizeRegion;
|
|
16
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export class ZoneMapper {
|
|
4
|
+
dataDir;
|
|
5
|
+
fallbackPue;
|
|
6
|
+
awsZoneMapping = {
|
|
7
|
+
'ap-northeast-1': { pue: 1.2, zone: 'JP-TK' },
|
|
8
|
+
'ap-south-1': { pue: 1.22, zone: 'IN-NO' },
|
|
9
|
+
'ap-southeast-1': { pue: 1.2, zone: 'SG' },
|
|
10
|
+
'eu-central-1': { pue: 1.16, zone: 'DE' },
|
|
11
|
+
'eu-west-1': { pue: 1.18, zone: 'IE' },
|
|
12
|
+
'eu-west-2': { pue: 1.18, zone: 'GB' },
|
|
13
|
+
'us-east-1': { pue: 1.18, zone: 'US-NE-ISNE' },
|
|
14
|
+
'us-east-2': { pue: 1.18, zone: 'US-MIDA-PJM' },
|
|
15
|
+
'us-west-1': { pue: 1.15, zone: 'US-CAL-CISO' },
|
|
16
|
+
'us-west-2': { pue: 1.14, zone: 'US-NW-PACW' },
|
|
17
|
+
};
|
|
18
|
+
gcpZoneMapping = {
|
|
19
|
+
'asia-east1': 'TW',
|
|
20
|
+
'asia-east2': 'HK',
|
|
21
|
+
'asia-northeast1': 'JP-TK',
|
|
22
|
+
'asia-northeast2': 'JP-KN',
|
|
23
|
+
'asia-northeast3': 'KR',
|
|
24
|
+
'asia-south1': 'IN-WE',
|
|
25
|
+
'asia-southeast1': 'SG',
|
|
26
|
+
'asia-southeast2': 'ID-JW',
|
|
27
|
+
'australia-southeast1': 'AU-NSW',
|
|
28
|
+
'europe-north1': 'FI',
|
|
29
|
+
'europe-west1': 'BE',
|
|
30
|
+
'europe-west2': 'GB',
|
|
31
|
+
'europe-west3': 'DE',
|
|
32
|
+
'europe-west4': 'NL',
|
|
33
|
+
'europe-west6': 'CH',
|
|
34
|
+
'us-central1': 'US-CENT-SWPP',
|
|
35
|
+
'us-east1': 'US-NE-ISNE',
|
|
36
|
+
'us-east4': 'US-MIDA-PJM',
|
|
37
|
+
'us-west1': 'US-CAL-CISO',
|
|
38
|
+
'us-west2': 'US-NW-PACW',
|
|
39
|
+
'us-west3': 'US-SW-AZPS',
|
|
40
|
+
'us-west4': 'US-NW-NEVP',
|
|
41
|
+
};
|
|
42
|
+
runtimePueData = null;
|
|
43
|
+
constructor(dataDir, fallbackPue = 1.2) {
|
|
44
|
+
this.dataDir = dataDir;
|
|
45
|
+
this.fallbackPue = fallbackPue;
|
|
46
|
+
}
|
|
47
|
+
resolve(provider, region) {
|
|
48
|
+
const normalizedRegion = this.normalizeRegion(region, provider);
|
|
49
|
+
if (provider === 'gcp') {
|
|
50
|
+
const runtime = this.loadRuntimePueData();
|
|
51
|
+
const runtimeRegion = runtime?.regions?.[normalizedRegion];
|
|
52
|
+
const runtimePue = runtimeRegion?.industryStandardPUE;
|
|
53
|
+
const runtimeZone = runtimeRegion?.zone;
|
|
54
|
+
const zone = runtimeZone && runtimeZone !== 'FALLBACK'
|
|
55
|
+
? runtimeZone
|
|
56
|
+
: this.gcpZoneMapping[normalizedRegion];
|
|
57
|
+
if (!zone) {
|
|
58
|
+
throw new Error(`No zone mapping for GCP region ${normalizedRegion}`);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
pue: runtimePue || this.fallbackPue,
|
|
62
|
+
zone,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const awsMapping = this.awsZoneMapping[normalizedRegion];
|
|
66
|
+
if (!awsMapping) {
|
|
67
|
+
throw new Error(`No zone mapping for AWS region ${normalizedRegion}`);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
pue: awsMapping.pue || this.fallbackPue,
|
|
71
|
+
zone: awsMapping.zone,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
loadRuntimePueData() {
|
|
75
|
+
if (this.runtimePueData)
|
|
76
|
+
return this.runtimePueData;
|
|
77
|
+
const filePath = path.join(this.dataDir, 'runtime-pue-mappings.json');
|
|
78
|
+
if (!fs.existsSync(filePath)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
83
|
+
this.runtimePueData = JSON.parse(raw);
|
|
84
|
+
return this.runtimePueData;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
normalizeRegion(region, provider) {
|
|
91
|
+
if (provider === 'gcp') {
|
|
92
|
+
// Convert us-central1-a to us-central1
|
|
93
|
+
if (/^[a-z]+-[a-z0-9]+-[a-z]$/.test(region)) {
|
|
94
|
+
return region.split('-').slice(0, 2).join('-');
|
|
95
|
+
}
|
|
96
|
+
return region;
|
|
97
|
+
}
|
|
98
|
+
// Convert us-east-1a to us-east-1
|
|
99
|
+
if (/^[a-z]+-[a-z]+-[0-9][a-z]$/.test(region)) {
|
|
100
|
+
return region.slice(0, -1);
|
|
101
|
+
}
|
|
102
|
+
return region;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const DUOOPS_DIR = path.join(process.cwd(), '.duoops');
|
|
4
|
+
export const PATCHES_DIR = path.join(DUOOPS_DIR, 'patches');
|
|
5
|
+
export const ensureDuoOpsDir = () => {
|
|
6
|
+
if (!fs.existsSync(PATCHES_DIR)) {
|
|
7
|
+
fs.mkdirSync(PATCHES_DIR, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export const getLatestPatch = () => {
|
|
11
|
+
if (!fs.existsSync(PATCHES_DIR))
|
|
12
|
+
return null;
|
|
13
|
+
const files = fs.readdirSync(PATCHES_DIR)
|
|
14
|
+
.filter(f => f.endsWith('.patch'))
|
|
15
|
+
.map(f => ({
|
|
16
|
+
name: f,
|
|
17
|
+
time: fs.statSync(path.join(PATCHES_DIR, f)).mtime.getTime()
|
|
18
|
+
}))
|
|
19
|
+
.sort((a, b) => b.time - a.time);
|
|
20
|
+
return files.length > 0 ? path.join(PATCHES_DIR, files[0].name) : null;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports ((-webkit-hyphens:none) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:"Inter",system-ui,sans-serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-900:oklch(39.6% .141 25.723);--color-amber-500:oklch(76.9% .188 70.08);--color-emerald-500:oklch(69.6% .17 162.48);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-purple-500:oklch(62.7% .265 303.9);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-black:#000;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-4xl:56rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-border:hsl(var(--border));--color-background:hsl(var(--background));--color-foreground:hsl(var(--foreground))}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::-moz-placeholder{opacity:1}::placeholder{opacity:1}@supports (not (-webkit-appearance:-apple-pay-button)) or (contain-intrinsic-size:1px){::-moz-placeholder{color:currentColor}::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::-moz-placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--color-border)}body{background-color:var(--color-background);color:var(--color-foreground)}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.right-0{right:calc(var(--spacing)*0)}.right-3{right:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.z-50{z-index:50}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.flex{display:flex}.grid{display:grid}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-14{height:calc(var(--spacing)*14)}.h-80{height:calc(var(--spacing)*80)}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:calc(var(--spacing)*48)}.w-1{width:calc(var(--spacing)*1)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-64{width:calc(var(--spacing)*64)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.animate-bounce{animation:var(--animate-bounce)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-red-900\/50{border-color:#82181a80}@supports (color:color-mix(in lab,red,red)){.border-red-900\/50{border-color:color-mix(in oklab,var(--color-red-900)50%,transparent)}}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-800{border-color:var(--color-zinc-800)}.border-zinc-800\/50{border-color:#27272a80}@supports (color:color-mix(in lab,red,red)){.border-zinc-800\/50{border-color:color-mix(in oklab,var(--color-zinc-800)50%,transparent)}}.bg-amber-500\/80{background-color:#f99c00cc}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/80{background-color:color-mix(in oklab,var(--color-amber-500)80%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-blue-500{background-color:var(--color-blue-500)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-purple-500{background-color:var(--color-purple-500)}.bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900)20%,transparent)}}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-600{background-color:var(--color-zinc-600)}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-800\/50{background-color:#27272a80}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/50{background-color:color-mix(in oklab,var(--color-zinc-800)50%,transparent)}}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-900\/30{background-color:#18181b4d}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/30{background-color:color-mix(in oklab,var(--color-zinc-900)30%,transparent)}}.bg-zinc-900\/50{background-color:#18181b80}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/50{background-color:color-mix(in oklab,var(--color-zinc-900)50%,transparent)}}.bg-zinc-900\/80{background-color:#18181bcc}@supports (color:color-mix(in lab,red,red)){.bg-zinc-900\/80{background-color:color-mix(in oklab,var(--color-zinc-900)80%,transparent)}}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-zinc-950\/50{background-color:#09090b80}@supports (color:color-mix(in lab,red,red)){.bg-zinc-950\/50{background-color:color-mix(in oklab,var(--color-zinc-950)50%,transparent)}}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-6{padding-block:calc(var(--spacing)*6)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-16{padding-right:calc(var(--spacing)*16)}.pl-1{padding-left:calc(var(--spacing)*1)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-6{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-pre-wrap{white-space:pre-wrap}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-red-200{color:var(--color-red-200)}.text-red-400{color:var(--color-red-400)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-950{color:var(--color-zinc-950)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline{text-decoration-line:underline}.opacity-50{opacity:.5}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.\[animation-delay\:-0\.3s\]{animation-delay:-.3s}.\[animation-delay\:-0\.15s\]{animation-delay:-.15s}.\[animation-delay\:0\.2s\]{animation-delay:.2s}.\[animation-delay\:0\.4s\]{animation-delay:.4s}.group-open\/reasoning\:rotate-90:is(:where(.group\/reasoning):is([open],:popover-open,:open) *){rotate:90deg}.selection\:bg-zinc-800 ::-moz-selection{background-color:var(--color-zinc-800)}.selection\:bg-zinc-800 ::selection{background-color:var(--color-zinc-800)}.selection\:bg-zinc-800::-moz-selection{background-color:var(--color-zinc-800)}.selection\:bg-zinc-800::selection{background-color:var(--color-zinc-800)}.placeholder\:text-zinc-600::-moz-placeholder{color:var(--color-zinc-600)}.placeholder\:text-zinc-600::placeholder{color:var(--color-zinc-600)}.last\:mb-0:last-child{margin-bottom:calc(var(--spacing)*0)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media(hover:hover){.hover\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\:bg-zinc-800\/20:hover{background-color:#27272a33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-800\/20:hover{background-color:color-mix(in oklab,var(--color-zinc-800)20%,transparent)}}.hover\:bg-zinc-900:hover{background-color:var(--color-zinc-900)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-zinc-100:hover{color:var(--color-zinc-100)}.hover\:text-zinc-400:hover{color:var(--color-zinc-400)}}.focus\:border-zinc-700:focus{border-color:var(--color-zinc-700)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-zinc-700:focus{--tw-ring-color:var(--color-zinc-700)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}:root{--background:0 0% 100%;--foreground:222.2 84% 4.9%;--card:0 0% 100%;--card-foreground:222.2 84% 4.9%;--popover:0 0% 100%;--popover-foreground:222.2 84% 4.9%;--primary:222.2 47.4% 11.2%;--primary-foreground:210 40% 98%;--secondary:210 40% 96.1%;--secondary-foreground:222.2 47.4% 11.2%;--muted:210 40% 96.1%;--muted-foreground:215.4 16.3% 46.9%;--accent:210 40% 96.1%;--accent-foreground:222.2 47.4% 11.2%;--destructive:0 84.2% 60.2%;--destructive-foreground:210 40% 98%;--border:214.3 31.8% 91.4%;--input:214.3 31.8% 91.4%;--ring:222.2 84% 4.9%;--radius:.5rem}.dark{--background:222.2 84% 4.9%;--foreground:210 40% 98%;--card:222.2 84% 4.9%;--card-foreground:210 40% 98%;--popover:222.2 84% 4.9%;--popover-foreground:210 40% 98%;--primary:210 40% 98%;--primary-foreground:222.2 47.4% 11.2%;--secondary:217.2 32.6% 17.5%;--secondary-foreground:210 40% 98%;--muted:217.2 32.6% 17.5%;--muted-foreground:215 20.2% 65.1%;--accent:217.2 32.6% 17.5%;--accent-foreground:210 40% 98%;--destructive:0 62.8% 30.6%;--destructive-foreground:210 40% 98%;--border:217.2 32.6% 17.5%;--input:217.2 32.6% 17.5%;--ring:212.7 26.8% 83.9%}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}
|