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.
Files changed (79) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +181 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +7 -0
  5. package/bin/run.cmd +3 -0
  6. package/bin/run.js +8 -0
  7. package/dist/commands/act.d.ts +12 -0
  8. package/dist/commands/act.js +61 -0
  9. package/dist/commands/ask.d.ts +8 -0
  10. package/dist/commands/ask.js +22 -0
  11. package/dist/commands/init.d.ts +5 -0
  12. package/dist/commands/init.js +97 -0
  13. package/dist/commands/job/logs.d.ts +13 -0
  14. package/dist/commands/job/logs.js +26 -0
  15. package/dist/commands/measure/calculate.d.ts +19 -0
  16. package/dist/commands/measure/calculate.js +208 -0
  17. package/dist/commands/measure/component.d.ts +5 -0
  18. package/dist/commands/measure/component.js +23 -0
  19. package/dist/commands/measure/seed.d.ts +5 -0
  20. package/dist/commands/measure/seed.js +62 -0
  21. package/dist/commands/pipelines/list.d.ts +14 -0
  22. package/dist/commands/pipelines/list.js +62 -0
  23. package/dist/commands/pipelines/show.d.ts +13 -0
  24. package/dist/commands/pipelines/show.js +68 -0
  25. package/dist/commands/portal.d.ts +8 -0
  26. package/dist/commands/portal.js +139 -0
  27. package/dist/commands/undo.d.ts +5 -0
  28. package/dist/commands/undo.js +35 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +1 -0
  31. package/dist/lib/ai/agent.d.ts +6 -0
  32. package/dist/lib/ai/agent.js +139 -0
  33. package/dist/lib/ai/model.d.ts +2 -0
  34. package/dist/lib/ai/model.js +22 -0
  35. package/dist/lib/ai/tools/editing.d.ts +3 -0
  36. package/dist/lib/ai/tools/editing.js +61 -0
  37. package/dist/lib/ai/tools/filesystem.d.ts +4 -0
  38. package/dist/lib/ai/tools/filesystem.js +44 -0
  39. package/dist/lib/ai/tools/gitlab.d.ts +4 -0
  40. package/dist/lib/ai/tools/gitlab.js +81 -0
  41. package/dist/lib/ai/tools/measure.d.ts +3 -0
  42. package/dist/lib/ai/tools/measure.js +26 -0
  43. package/dist/lib/config.d.ts +18 -0
  44. package/dist/lib/config.js +72 -0
  45. package/dist/lib/gitlab/client.d.ts +6 -0
  46. package/dist/lib/gitlab/client.js +18 -0
  47. package/dist/lib/gitlab/index.d.ts +6 -0
  48. package/dist/lib/gitlab/index.js +49 -0
  49. package/dist/lib/gitlab/provider.d.ts +14 -0
  50. package/dist/lib/gitlab/provider.js +72 -0
  51. package/dist/lib/gitlab/types.d.ts +34 -0
  52. package/dist/lib/gitlab/types.js +5 -0
  53. package/dist/lib/integrations/bigquery-sink.d.ts +12 -0
  54. package/dist/lib/integrations/bigquery-sink.js +47 -0
  55. package/dist/lib/logger.d.ts +2 -0
  56. package/dist/lib/logger.js +11 -0
  57. package/dist/lib/measure/bigquery-service.d.ts +2 -0
  58. package/dist/lib/measure/bigquery-service.js +54 -0
  59. package/dist/lib/measure/carbon-calculator.d.ts +13 -0
  60. package/dist/lib/measure/carbon-calculator.js +125 -0
  61. package/dist/lib/measure/cli-utils.d.ts +2 -0
  62. package/dist/lib/measure/cli-utils.js +107 -0
  63. package/dist/lib/measure/intensity-provider.d.ts +6 -0
  64. package/dist/lib/measure/intensity-provider.js +34 -0
  65. package/dist/lib/measure/power-profile-repository.d.ts +19 -0
  66. package/dist/lib/measure/power-profile-repository.js +129 -0
  67. package/dist/lib/measure/types.d.ts +137 -0
  68. package/dist/lib/measure/types.js +1 -0
  69. package/dist/lib/measure/zone-mapper.d.ts +16 -0
  70. package/dist/lib/measure/zone-mapper.js +104 -0
  71. package/dist/lib/state.d.ts +4 -0
  72. package/dist/lib/state.js +21 -0
  73. package/dist/portal/assets/index-BP8FwWqA.css +1 -0
  74. package/dist/portal/assets/index-MU6EBerh.js +188 -0
  75. package/dist/portal/duoops.svg +4 -0
  76. package/dist/portal/index.html +24 -0
  77. package/dist/portal/vite.svg +1 -0
  78. package/oclif.manifest.json +415 -0
  79. package/package.json +103 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Younes Laaroussi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # DuoOps
2
+
3
+ > **Toolset for Explainable and Sustainable CI on GitLab.**
4
+
5
+ DuoOps is a developer-focused CLI and Web Portal designed to make GitLab CI/CD pipelines more transparent, debuggable, and environmentally sustainable. It leverages AI agents to explain failures and provides tools to measure and visualize the carbon footprint of your CI jobs.
6
+
7
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
8
+ ![Version](https://img.shields.io/badge/version-0.0.0-green.svg)
9
+
10
+ ## 🌟 Key Features
11
+
12
+ * **🤖 AI-Powered Troubleshooting**: Ask questions about your pipelines in plain English. The AI agent (powered by GitLab Duo or Google Vertex AI) analyzes logs, identifies root causes, and suggests fixes.
13
+ * **🔍 Deep Pipeline Inspection**: View pipeline status, job details, and logs directly from your terminal.
14
+ * **🌱 Carbon Measurement**: Calculate the carbon emissions of your CI jobs based on CPU/RAM usage timeseries. Track your environmental impact over time.
15
+ * **📊 Web Portal**: A local React-based dashboard to visualize pipeline metrics and chat with the AI assistant in a rich interface.
16
+ * **🧠 Intelligent Summarization**: Automatically summarizes long job logs using sub-agents to provide concise failure analysis without token limit issues.
17
+
18
+ ## 🚀 Quick Start
19
+
20
+ ### Prerequisites
21
+
22
+ * **Node.js** >= 18.0.0
23
+ * **GitLab Account**: A Personal Access Token with `api` scope.
24
+ * **(Optional) Google Cloud SDK**: Required only for BigQuery metrics.
25
+ * Install the `gcloud` CLI.
26
+ * Authenticate: `gcloud auth application-default login`
27
+
28
+ ### Installation
29
+
30
+ #### Install from npm (recommended)
31
+
32
+ ```bash
33
+ # Install the published CLI globally
34
+ npm install -g duoops
35
+
36
+ # Verify the binary is available
37
+ duoops --help
38
+ ```
39
+
40
+ #### Install from source (development)
41
+
42
+ ```bash
43
+ # Clone the repository
44
+ git clone https://github.com/youneslaaroussi/duoops.git
45
+ cd duoops
46
+
47
+ # Install dependencies
48
+ pnpm install
49
+
50
+ # Build the project
51
+ pnpm build
52
+
53
+ # Link the CLI globally
54
+ npm link
55
+ ```
56
+
57
+ ### Configuration
58
+
59
+ Run the interactive initialization command to set up your credentials:
60
+
61
+ ```bash
62
+ duoops init
63
+ ```
64
+
65
+ This will prompt you for:
66
+ * GitLab URL (default: `https://gitlab.com`)
67
+ * GitLab Personal Access Token
68
+ * (Optional) BigQuery settings for sustainability tracking
69
+
70
+ Alternatively, you can configure DuoOps using environment variables:
71
+
72
+ | Variable | Description |
73
+ | :--- | :--- |
74
+ | `GITLAB_TOKEN` | Your GitLab Personal Access Token |
75
+ | `GITLAB_URL` | GitLab instance URL (default: `https://gitlab.com`) |
76
+ | `DUOOPS_AI_PROVIDER` | AI Provider: `gitlab` (default) or `vertex` |
77
+ | `GCP_PROJECT_ID` | Google Cloud Project ID (required for Vertex AI) |
78
+ | `GCP_LOCATION` | Google Cloud Location (default: `us-central1`) |
79
+
80
+ ## 📖 Usage
81
+
82
+ ### 1. Inspect Pipelines
83
+
84
+ List recent pipelines for a project:
85
+
86
+ ```bash
87
+ duoops pipelines list <project-id-or-path>
88
+ # Example: duoops pipelines list my-group/my-project
89
+ ```
90
+
91
+ ### 2. Analyze Jobs & Logs
92
+
93
+ Get details and logs for a specific job:
94
+
95
+ ```bash
96
+ duoops job show <project-id> <job-id>
97
+ # Example: duoops job show 12345 987654
98
+ ```
99
+
100
+ ### 3. Ask the AI Agent
101
+
102
+ Troubleshoot failures or ask general CI questions:
103
+
104
+ ```bash
105
+ duoops ask "Why did the last pipeline fail?"
106
+ duoops ask "How do I optimize my .gitlab-ci.yml for faster builds?"
107
+ ```
108
+
109
+ ### 4. Measure Carbon Footprint
110
+
111
+ Calculate emissions for a job using CPU/RAM usage data (JSON timeseries):
112
+
113
+ ```bash
114
+ duoops measure calculate \
115
+ --provider gcp \
116
+ --machine e2-standard-4 \
117
+ --region us-central1 \
118
+ --cpu-timeseries ./data/cpu.json \
119
+ --ram-used-timeseries ./data/ram.json \
120
+ --out-md report.md
121
+ ```
122
+
123
+ This will output a report to the console and save a Markdown summary to `report.md`.
124
+
125
+ **Persist from CI:** To save results to BigQuery when the measure component runs in GitLab CI, set these CI/CD variables (masked where appropriate):
126
+
127
+ | Variable | Description |
128
+ | :--- | :--- |
129
+ | `DUOOPS_BQ_DATASET` | BigQuery dataset name |
130
+ | `DUOOPS_BQ_TABLE` | BigQuery table name |
131
+ | `GCP_PROJECT_ID` | Google Cloud project ID |
132
+
133
+ The same GCP service account used for the measure component (e.g. `GCP_SA_KEY_BASE64`) is used for BigQuery; the component sets `GOOGLE_APPLICATION_CREDENTIALS` so the BigQuery client can authenticate. Ensure the table exists (e.g. run `duoops init` locally with BigQuery enabled to create it).
134
+
135
+ #### Act on recent data
136
+
137
+ Run `duoops act [project-id] [--limit N]` to fetch recent CI carbon metrics from BigQuery and get the agent's recommendations (e.g. set a budget, optimize jobs, suggest .gitlab-ci changes). Project ID can be omitted if set via `duoops init`.
138
+
139
+ ### 5. Launch the Web Portal
140
+
141
+ Start the local dashboard to visualize metrics and chat:
142
+
143
+ ```bash
144
+ duoops portal
145
+ ```
146
+
147
+ Open your browser to `http://localhost:3000`.
148
+
149
+ ## 🛠️ Development
150
+
151
+ ### Project Structure
152
+
153
+ * `bin/`: CLI entry point.
154
+ * `src/commands/`: Oclif command implementations.
155
+ * `src/lib/`: Core logic (AI agents, GitLab client, Measurement tools).
156
+ * `portal/`: React frontend application.
157
+
158
+ ### Running Tests
159
+
160
+ ```bash
161
+ pnpm test
162
+ ```
163
+
164
+ ### Building for Production
165
+
166
+ ```bash
167
+ pnpm build
168
+ ```
169
+
170
+ This compiles the TypeScript CLI and builds the React frontend, copying assets to the `dist/` directory.
171
+
172
+ ## 📦 Publishing to npm
173
+
174
+ 1. Update `package.json` with the release version you want to publish.
175
+ 2. Run the checks: `pnpm test` (this runs lint afterwards) to ensure the CLI and portal build cleanly.
176
+ 3. Inspect the publish payload locally with `pnpm pack` (this runs the `prepack` script, which now builds the CLI, portal, and Oclif manifest automatically). Untar the generated `.tgz` if you want to double-check the contents.
177
+ 4. When you're satisfied, publish the package: `pnpm publish --access public`.
178
+
179
+ ## 📄 License
180
+
181
+ MIT
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning
2
+
3
+ // eslint-disable-next-line n/no-unpublished-import
4
+ import 'dotenv/config'
5
+ import {execute} from '@oclif/core'
6
+
7
+ await execute({development: true, dir: import.meta.url})
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Suppress AI SDK compatibility warnings (e.g. specificationVersion v2) when portal runs
4
+ globalThis.AI_SDK_LOG_WARNINGS = false
5
+
6
+ import {execute} from '@oclif/core'
7
+
8
+ await execute({dir: import.meta.url})
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Act extends Command {
3
+ static args: {
4
+ 'project-id': import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,61 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { runAgent } from '../lib/ai/agent.js';
3
+ import { configManager } from '../lib/config.js';
4
+ import { fetchCarbonMetrics } from '../lib/measure/bigquery-service.js';
5
+ export default class Act extends Command {
6
+ static args = {
7
+ 'project-id': Args.string({
8
+ description: 'GitLab project ID (or use default from duoops init)',
9
+ required: false,
10
+ }),
11
+ };
12
+ static description = 'Fetch recent CI carbon metrics from BigQuery and get the agent\'s recommendations';
13
+ static examples = [
14
+ `<%= config.bin %> <%= command.id %>`,
15
+ `<%= config.bin %> <%= command.id %> 12345 --limit 20`,
16
+ ];
17
+ static flags = {
18
+ limit: Flags.integer({
19
+ char: 'n',
20
+ default: 20,
21
+ description: 'Number of recent job records to fetch',
22
+ }),
23
+ };
24
+ async run() {
25
+ const { args, flags } = await this.parse(Act);
26
+ const config = configManager.get();
27
+ const projectId = args['project-id'] ?? config.defaultProjectId;
28
+ if (!projectId) {
29
+ this.error('Project ID is required. Pass it as an argument or run "duoops init" and set a default project.');
30
+ }
31
+ this.log('Fetching recent carbon metrics...');
32
+ let metrics;
33
+ try {
34
+ metrics = await fetchCarbonMetrics(projectId, flags.limit);
35
+ }
36
+ catch (error) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ this.error(message.includes('BigQuery') ? message : `Failed to fetch metrics: ${message}`);
39
+ }
40
+ if (metrics.length === 0) {
41
+ this.log('No carbon metrics found for this project. Run the measure component in CI with BigQuery env vars set to persist data.');
42
+ return;
43
+ }
44
+ const dataBlob = JSON.stringify(metrics, null, 2);
45
+ const prompt = `Here are the last ${metrics.length} CI job carbon metrics for GitLab project ${projectId} (from BigQuery):
46
+
47
+ \`\`\`json
48
+ ${dataBlob}
49
+ \`\`\`
50
+
51
+ Analyze this data and recommend concrete actions. For example: set an emission budget, add or tune the measure component in .gitlab-ci.yml, optimize the heaviest jobs, suggest region or machine type changes, or link to specific job logs. Be concise and actionable.`;
52
+ this.log('Thinking...');
53
+ try {
54
+ const response = await runAgent(prompt);
55
+ this.log('\n' + response);
56
+ }
57
+ catch (error) {
58
+ this.error(error instanceof Error ? error.message : String(error));
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Ask extends Command {
3
+ static args: {
4
+ question: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ run(): Promise<void>;
8
+ }
@@ -0,0 +1,22 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { runAgent } from '../lib/ai/agent.js';
3
+ export default class Ask extends Command {
4
+ static args = {
5
+ question: Args.string({
6
+ description: 'The question to ask the agent',
7
+ required: true,
8
+ }),
9
+ };
10
+ static description = 'Ask questions about your CI/CD pipelines, logs, and sustainability';
11
+ async run() {
12
+ const { args } = await this.parse(Ask);
13
+ this.log('Thinking...');
14
+ try {
15
+ const response = await runAgent(args.question);
16
+ this.log('\n' + response);
17
+ }
18
+ catch (error) {
19
+ this.error(error instanceof Error ? error.message : String(error));
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,5 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Init extends Command {
3
+ static description: string;
4
+ run(): Promise<void>;
5
+ }
@@ -0,0 +1,97 @@
1
+ import { BigQuery } from '@google-cloud/bigquery';
2
+ import { confirm, input, password } from '@inquirer/prompts';
3
+ import { Command } from '@oclif/core';
4
+ import { configManager } from '../lib/config.js';
5
+ export default class Init extends Command {
6
+ static description = 'Initialize the configuration for DuoOps';
7
+ async run() {
8
+ this.log('Welcome to DuoOps! Let\'s set up your configuration.');
9
+ this.log('You will need a GitLab Personal Access Token with the "api" scope.');
10
+ this.log('You can generate one at: https://gitlab.com/-/user_settings/personal_access_tokens');
11
+ this.log('');
12
+ const gitlabUrl = await input({
13
+ default: 'https://gitlab.com',
14
+ message: 'GitLab URL',
15
+ });
16
+ const gitlabToken = await password({
17
+ mask: '*',
18
+ message: 'GitLab Personal Access Token',
19
+ });
20
+ const enableMeasure = await confirm({
21
+ message: 'Enable Measure/Sustainability Tracking (BigQuery)?',
22
+ });
23
+ let bigqueryDataset;
24
+ let bigqueryTable;
25
+ let googleProjectId;
26
+ if (enableMeasure) {
27
+ this.log('Note: You must have Google Cloud SDK installed and authenticated (gcloud auth application-default login) or a service account key.');
28
+ googleProjectId = await input({
29
+ message: 'Google Cloud Project ID',
30
+ });
31
+ bigqueryDataset = await input({
32
+ default: 'measure_data',
33
+ message: 'BigQuery Dataset Name',
34
+ });
35
+ bigqueryTable = await input({
36
+ default: 'emissions',
37
+ message: 'BigQuery Table Name',
38
+ });
39
+ const shouldCreate = await confirm({
40
+ message: `Attempt to create BigQuery Dataset (${bigqueryDataset}) and Table (${bigqueryTable}) if missing?`,
41
+ });
42
+ if (shouldCreate) {
43
+ try {
44
+ this.log('Checking BigQuery resources...');
45
+ const bigquery = new BigQuery({ projectId: googleProjectId });
46
+ // 1. Create Dataset
47
+ const dataset = bigquery.dataset(bigqueryDataset);
48
+ const [datasetExists] = await dataset.exists();
49
+ if (datasetExists) {
50
+ this.log(`Dataset '${bigqueryDataset}' already exists.`);
51
+ }
52
+ else {
53
+ this.log(`Creating dataset '${bigqueryDataset}'...`);
54
+ await dataset.create({ location: 'US' }); // Default location
55
+ this.log('Dataset created.');
56
+ }
57
+ // 2. Create Table with Schema
58
+ const table = dataset.table(bigqueryTable);
59
+ const [tableExists] = await table.exists();
60
+ if (tableExists) {
61
+ this.log(`Table '${bigqueryTable}' already exists.`);
62
+ }
63
+ else {
64
+ this.log(`Creating table '${bigqueryTable}'...`);
65
+ const schema = [
66
+ { name: 'ingested_at', type: 'TIMESTAMP' },
67
+ { name: 'gitlab_project_id', type: 'INTEGER' },
68
+ { name: 'gitlab_job_id', type: 'INTEGER' },
69
+ { name: 'gitlab_job_name', type: 'STRING' },
70
+ { name: 'gitlab_user_name', type: 'STRING' },
71
+ { name: 'machine_type', type: 'STRING' },
72
+ { name: 'region', type: 'STRING' },
73
+ { name: 'runtime_seconds', type: 'FLOAT' },
74
+ { name: 'cpu_utilization_avg', type: 'FLOAT' },
75
+ { name: 'ram_utilization_avg', type: 'FLOAT' },
76
+ { name: 'energy_kwh', type: 'FLOAT' },
77
+ { name: 'total_emissions_g', type: 'FLOAT' }
78
+ ];
79
+ await table.create({ schema });
80
+ this.log('Table created successfully.');
81
+ }
82
+ }
83
+ catch (error) {
84
+ this.warn(`Failed to create BigQuery resources: ${error instanceof Error ? error.message : String(error)}`);
85
+ this.warn('You may need to create them manually or check your permissions.');
86
+ }
87
+ }
88
+ }
89
+ configManager.set({
90
+ gitlabToken,
91
+ gitlabUrl,
92
+ measure: enableMeasure ? { bigqueryDataset, bigqueryTable, googleProjectId } : undefined,
93
+ });
94
+ this.log('Configuration saved successfully!');
95
+ this.log(`You can now use DuoOps commands. Try running '${this.config.bin} pipelines:list <project-id>'`);
96
+ }
97
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class JobLogs extends Command {
3
+ static args: {
4
+ project: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ job_id: import("@oclif/core/interfaces").Arg<number, {
6
+ max?: number;
7
+ min?: number;
8
+ }>;
9
+ };
10
+ static description: string;
11
+ static examples: string[];
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,26 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { getPipelineProvider } from '../../lib/gitlab/index.js';
3
+ export default class JobLogs extends Command {
4
+ static args = {
5
+ /* eslint-disable perfectionist/sort-objects -- project must be first for CLI arg order */
6
+ project: Args.string({
7
+ description: 'Project ID or path (e.g. group/project)',
8
+ required: true,
9
+ }),
10
+ job_id: Args.integer({
11
+ description: 'Job ID',
12
+ required: true,
13
+ }),
14
+ /* eslint-enable perfectionist/sort-objects */
15
+ };
16
+ static description = 'Get job log trace';
17
+ static examples = [
18
+ `<%= config.bin %> <%= command.id %> group/my-project 12345`,
19
+ ];
20
+ async run() {
21
+ const { args } = await this.parse(JobLogs);
22
+ const provider = getPipelineProvider();
23
+ const trace = await provider.getJobTrace(args.project, args.job_id);
24
+ this.log(trace);
25
+ }
26
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CarbonCalculate extends Command {
3
+ static args: {};
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ budget: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'cpu-timeseries': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'fail-on-budget': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ machine: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'out-json': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'out-md': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ provider: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'ram-size-timeseries': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ 'ram-used-timeseries': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
16
+ region: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<void>;
19
+ }