duoops 0.1.7 → 0.2.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/README.md +151 -63
- package/data/aws_machine_power_profiles.json +54 -0
- package/data/cpu_physical_specs.json +105 -0
- package/data/cpu_power_profiles.json +275 -0
- package/data/gcp_machine_power_profiles.json +1802 -0
- package/data/runtime-pue-mappings.json +183 -0
- package/dist/commands/ask.d.ts +3 -0
- package/dist/commands/ask.js +9 -3
- package/dist/commands/autofix-ci.d.ts +13 -0
- package/dist/commands/autofix-ci.js +114 -0
- package/dist/commands/autofix.d.ts +5 -0
- package/dist/commands/autofix.js +11 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.js +47 -0
- package/dist/commands/init.js +50 -27
- package/dist/commands/mcp/deploy.d.ts +13 -0
- package/dist/commands/mcp/deploy.js +139 -0
- package/dist/commands/measure/calculate.js +2 -2
- package/dist/commands/portal.js +428 -11
- package/dist/lib/ai/agent.d.ts +6 -2
- package/dist/lib/ai/agent.js +51 -57
- package/dist/lib/ai/tools/editing.js +28 -13
- package/dist/lib/ai/tools/gitlab.d.ts +4 -0
- package/dist/lib/ai/tools/gitlab.js +166 -11
- package/dist/lib/ai/tools/measure.js +7 -3
- package/dist/lib/ai/tools/types.d.ts +3 -0
- package/dist/lib/ai/tools/types.js +1 -0
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/gcloud.d.ts +7 -0
- package/dist/lib/gcloud.js +105 -0
- package/dist/lib/gitlab/pipelines-service.d.ts +23 -0
- package/dist/lib/gitlab/pipelines-service.js +146 -0
- package/dist/lib/gitlab/runner-service.d.ts +11 -0
- package/dist/lib/gitlab/runner-service.js +15 -0
- package/dist/lib/portal/settings.d.ts +3 -0
- package/dist/lib/portal/settings.js +48 -0
- package/dist/lib/scaffold.d.ts +5 -0
- package/dist/lib/scaffold.js +32 -0
- package/dist/portal/assets/HomeDashboard-DlkwSyKx.js +1 -0
- package/dist/portal/assets/JobDetailsDrawer-7kXXMSH8.js +1 -0
- package/dist/portal/assets/JobsDashboard-D4pNc9TM.js +1 -0
- package/dist/portal/assets/MetricsDashboard-BcgzvzBz.js +1 -0
- package/dist/portal/assets/PipelinesDashboard-BNrSM9GB.js +1 -0
- package/dist/portal/assets/allPaths-CXDKahbk.js +1 -0
- package/dist/portal/assets/allPathsLoader-BF5PAx2c.js +2 -0
- package/dist/portal/assets/cache-YerT0Slh.js +6 -0
- package/dist/portal/assets/core-Cz8f3oSB.js +19 -0
- package/dist/portal/assets/{index-B6bzT1Vv.js → index-B9sNUqEC.js} +1 -1
- package/dist/portal/assets/index-BWa_E8Y7.css +1 -0
- package/dist/portal/assets/index-Bp4RqK05.js +1 -0
- package/dist/portal/assets/index-DW6Qp0d6.js +64 -0
- package/dist/portal/assets/index-Uc4Xhv31.js +1 -0
- package/dist/portal/assets/progressBar-C4SmnGeZ.js +1 -0
- package/dist/portal/assets/splitPathsBySizeLoader-C-T9_API.js +1 -0
- package/dist/portal/index.html +2 -2
- package/oclif.manifest.json +282 -93
- package/package.json +2 -1
- package/templates/.gitlab/duo/flows/duoops.yaml +114 -0
- package/templates/agents/agent.yml +45 -0
- package/templates/duoops-autofix-component.yml +52 -0
- package/templates/flows/flow.yml +283 -0
- package/dist/portal/assets/MetricsDashboard-DIsoz4Sl.js +0 -71
- package/dist/portal/assets/index-BP8FwWqA.css +0 -1
- package/dist/portal/assets/index-DkVG3jel.js +0 -70
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as m,P as R,x as d,y as P,z as E,j as e,C as S,E as N,e as p}from"./index-DW6Qp0d6.js";const x=n=>{const{animate:r=!0,className:t,intent:l,stripes:i=!0,value:a,...o}=n,u=m(R,d(l),{[E]:!r,[P]:!i},t),s=a==null?void 0:100*N(a,0,1),c=s==null?void 0:s+"%";return e.jsx("div",{...o,"aria-valuemax":100,"aria-valuemin":0,"aria-valuenow":s==null?void 0:Math.round(s),className:u,role:"progressbar",children:e.jsx("div",{className:S,style:{width:c}})})};x.displayName=`${p}.ProgressBar`;export{x as P};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a6 as s,v as _,a5 as e}from"./index-DW6Qp0d6.js";const p=async(o,i)=>{const r=s(o);let t;return i===_.STANDARD?t=await e(()=>import("./index-Bp4RqK05.js").then(a=>a.I),[]):t=await e(()=>import("./index-Uc4Xhv31.js").then(a=>a.I),[]),t[r]};export{p as splitPathsBySizeLoader};
|
package/dist/portal/index.html
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
<meta property="og:description" content="Explainable and Sustainable CI on GitLab with AI-powered insights." />
|
|
16
16
|
|
|
17
17
|
<meta name="robots" content="noindex, nofollow" />
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-DW6Qp0d6.js"></script>
|
|
19
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BWa_E8Y7.css">
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
|
22
22
|
<div id="root"></div>
|
package/oclif.manifest.json
CHANGED
|
@@ -50,7 +50,16 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"description": "Ask questions about your CI/CD pipelines, logs, and sustainability",
|
|
53
|
-
"flags": {
|
|
53
|
+
"flags": {
|
|
54
|
+
"project": {
|
|
55
|
+
"char": "P",
|
|
56
|
+
"description": "GitLab Project ID to contextually use",
|
|
57
|
+
"name": "project",
|
|
58
|
+
"hasDynamicHelp": false,
|
|
59
|
+
"multiple": false,
|
|
60
|
+
"type": "option"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
54
63
|
"hasDynamicHelp": false,
|
|
55
64
|
"hiddenAliases": [],
|
|
56
65
|
"id": "ask",
|
|
@@ -66,6 +75,117 @@
|
|
|
66
75
|
"ask.js"
|
|
67
76
|
]
|
|
68
77
|
},
|
|
78
|
+
"autofix-ci": {
|
|
79
|
+
"aliases": [],
|
|
80
|
+
"args": {
|
|
81
|
+
"project-id": {
|
|
82
|
+
"description": "GitLab project ID or path (falls back to DUOOPS_PROJECT_ID, CI_PROJECT_ID, or duoops init default)",
|
|
83
|
+
"name": "project-id",
|
|
84
|
+
"required": false
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"description": "Headless autofix: analyze the latest failing pipeline and post results to an MR or stdout",
|
|
88
|
+
"examples": [
|
|
89
|
+
"<%= config.bin %> <%= command.id %>",
|
|
90
|
+
"<%= config.bin %> <%= command.id %> 12345 --mr 42",
|
|
91
|
+
"<%= config.bin %> <%= command.id %> my-group/my-project --pipeline 99999"
|
|
92
|
+
],
|
|
93
|
+
"flags": {
|
|
94
|
+
"mr": {
|
|
95
|
+
"description": "Merge request IID to comment the analysis on",
|
|
96
|
+
"name": "mr",
|
|
97
|
+
"hasDynamicHelp": false,
|
|
98
|
+
"multiple": false,
|
|
99
|
+
"type": "option"
|
|
100
|
+
},
|
|
101
|
+
"pipeline": {
|
|
102
|
+
"description": "Pipeline ID to analyze (defaults to latest failed)",
|
|
103
|
+
"name": "pipeline",
|
|
104
|
+
"hasDynamicHelp": false,
|
|
105
|
+
"multiple": false,
|
|
106
|
+
"type": "option"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"hasDynamicHelp": false,
|
|
110
|
+
"hiddenAliases": [],
|
|
111
|
+
"id": "autofix-ci",
|
|
112
|
+
"pluginAlias": "duoops",
|
|
113
|
+
"pluginName": "duoops",
|
|
114
|
+
"pluginType": "core",
|
|
115
|
+
"strict": true,
|
|
116
|
+
"enableJsonFlag": false,
|
|
117
|
+
"isESM": true,
|
|
118
|
+
"relativePath": [
|
|
119
|
+
"dist",
|
|
120
|
+
"commands",
|
|
121
|
+
"autofix-ci.js"
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
"autofix": {
|
|
125
|
+
"aliases": [],
|
|
126
|
+
"args": {},
|
|
127
|
+
"description": "Launch the DuoOps portal and pre-fill an autofix request to the agent",
|
|
128
|
+
"flags": {
|
|
129
|
+
"port": {
|
|
130
|
+
"char": "p",
|
|
131
|
+
"description": "Port to run the portal on",
|
|
132
|
+
"name": "port",
|
|
133
|
+
"default": 58327,
|
|
134
|
+
"hasDynamicHelp": false,
|
|
135
|
+
"multiple": false,
|
|
136
|
+
"type": "option"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"hasDynamicHelp": false,
|
|
140
|
+
"hiddenAliases": [],
|
|
141
|
+
"id": "autofix",
|
|
142
|
+
"pluginAlias": "duoops",
|
|
143
|
+
"pluginName": "duoops",
|
|
144
|
+
"pluginType": "core",
|
|
145
|
+
"strict": true,
|
|
146
|
+
"enableJsonFlag": false,
|
|
147
|
+
"isESM": true,
|
|
148
|
+
"relativePath": [
|
|
149
|
+
"dist",
|
|
150
|
+
"commands",
|
|
151
|
+
"autofix.js"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
"config": {
|
|
155
|
+
"aliases": [],
|
|
156
|
+
"args": {
|
|
157
|
+
"key": {
|
|
158
|
+
"description": "Config key (e.g., gitlabUrl, defaultProjectId)",
|
|
159
|
+
"name": "key",
|
|
160
|
+
"required": true
|
|
161
|
+
},
|
|
162
|
+
"value": {
|
|
163
|
+
"description": "Config value",
|
|
164
|
+
"name": "value",
|
|
165
|
+
"required": true
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"description": "Set a configuration value directly",
|
|
169
|
+
"examples": [
|
|
170
|
+
"<%= config.bin %> <%= command.id %> gitlabUrl https://gitlab.com",
|
|
171
|
+
"<%= config.bin %> <%= command.id %> defaultProjectId 123456"
|
|
172
|
+
],
|
|
173
|
+
"flags": {},
|
|
174
|
+
"hasDynamicHelp": false,
|
|
175
|
+
"hiddenAliases": [],
|
|
176
|
+
"id": "config",
|
|
177
|
+
"pluginAlias": "duoops",
|
|
178
|
+
"pluginName": "duoops",
|
|
179
|
+
"pluginType": "core",
|
|
180
|
+
"strict": true,
|
|
181
|
+
"enableJsonFlag": false,
|
|
182
|
+
"isESM": true,
|
|
183
|
+
"relativePath": [
|
|
184
|
+
"dist",
|
|
185
|
+
"commands",
|
|
186
|
+
"config.js"
|
|
187
|
+
]
|
|
188
|
+
},
|
|
69
189
|
"init": {
|
|
70
190
|
"aliases": [],
|
|
71
191
|
"args": {},
|
|
@@ -95,7 +215,7 @@
|
|
|
95
215
|
"char": "p",
|
|
96
216
|
"description": "Port to run the portal on",
|
|
97
217
|
"name": "port",
|
|
98
|
-
"default":
|
|
218
|
+
"default": 58327,
|
|
99
219
|
"hasDynamicHelp": false,
|
|
100
220
|
"multiple": false,
|
|
101
221
|
"type": "option"
|
|
@@ -171,6 +291,165 @@
|
|
|
171
291
|
"logs.js"
|
|
172
292
|
]
|
|
173
293
|
},
|
|
294
|
+
"mcp:deploy": {
|
|
295
|
+
"aliases": [],
|
|
296
|
+
"args": {},
|
|
297
|
+
"description": "Deploy the DuoOps MCP server to Google Cloud Run",
|
|
298
|
+
"flags": {
|
|
299
|
+
"bq-dataset": {
|
|
300
|
+
"description": "BigQuery Dataset ID",
|
|
301
|
+
"name": "bq-dataset",
|
|
302
|
+
"hasDynamicHelp": false,
|
|
303
|
+
"multiple": false,
|
|
304
|
+
"type": "option"
|
|
305
|
+
},
|
|
306
|
+
"bq-table": {
|
|
307
|
+
"description": "BigQuery Table ID",
|
|
308
|
+
"name": "bq-table",
|
|
309
|
+
"hasDynamicHelp": false,
|
|
310
|
+
"multiple": false,
|
|
311
|
+
"type": "option"
|
|
312
|
+
},
|
|
313
|
+
"gcp-project": {
|
|
314
|
+
"char": "p",
|
|
315
|
+
"description": "Google Cloud Project ID",
|
|
316
|
+
"name": "gcp-project",
|
|
317
|
+
"hasDynamicHelp": false,
|
|
318
|
+
"multiple": false,
|
|
319
|
+
"type": "option"
|
|
320
|
+
},
|
|
321
|
+
"gitlab-url": {
|
|
322
|
+
"description": "GitLab instance URL",
|
|
323
|
+
"name": "gitlab-url",
|
|
324
|
+
"default": "https://gitlab.com",
|
|
325
|
+
"hasDynamicHelp": false,
|
|
326
|
+
"multiple": false,
|
|
327
|
+
"type": "option"
|
|
328
|
+
},
|
|
329
|
+
"region": {
|
|
330
|
+
"char": "r",
|
|
331
|
+
"description": "Cloud Run region",
|
|
332
|
+
"name": "region",
|
|
333
|
+
"default": "us-central1",
|
|
334
|
+
"hasDynamicHelp": false,
|
|
335
|
+
"multiple": false,
|
|
336
|
+
"type": "option"
|
|
337
|
+
},
|
|
338
|
+
"source": {
|
|
339
|
+
"description": "Path to MCP server source directory",
|
|
340
|
+
"name": "source",
|
|
341
|
+
"default": "",
|
|
342
|
+
"hasDynamicHelp": false,
|
|
343
|
+
"multiple": false,
|
|
344
|
+
"type": "option"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"hasDynamicHelp": false,
|
|
348
|
+
"hiddenAliases": [],
|
|
349
|
+
"id": "mcp:deploy",
|
|
350
|
+
"pluginAlias": "duoops",
|
|
351
|
+
"pluginName": "duoops",
|
|
352
|
+
"pluginType": "core",
|
|
353
|
+
"strict": true,
|
|
354
|
+
"enableJsonFlag": false,
|
|
355
|
+
"isESM": true,
|
|
356
|
+
"relativePath": [
|
|
357
|
+
"dist",
|
|
358
|
+
"commands",
|
|
359
|
+
"mcp",
|
|
360
|
+
"deploy.js"
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
"pipelines:list": {
|
|
364
|
+
"aliases": [],
|
|
365
|
+
"args": {
|
|
366
|
+
"project": {
|
|
367
|
+
"description": "Project ID or path (e.g. group/project)",
|
|
368
|
+
"name": "project",
|
|
369
|
+
"required": false
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
"description": "List GitLab CI pipelines for a project",
|
|
373
|
+
"examples": [
|
|
374
|
+
"<%= config.bin %> <%= command.id %> group/my-project",
|
|
375
|
+
"<%= config.bin %> <%= command.id %> 123 --limit 20 --ref main"
|
|
376
|
+
],
|
|
377
|
+
"flags": {
|
|
378
|
+
"limit": {
|
|
379
|
+
"char": "n",
|
|
380
|
+
"description": "Maximum number of pipelines to return",
|
|
381
|
+
"name": "limit",
|
|
382
|
+
"default": 10,
|
|
383
|
+
"hasDynamicHelp": false,
|
|
384
|
+
"multiple": false,
|
|
385
|
+
"type": "option"
|
|
386
|
+
},
|
|
387
|
+
"ref": {
|
|
388
|
+
"description": "Filter by branch or tag",
|
|
389
|
+
"name": "ref",
|
|
390
|
+
"hasDynamicHelp": false,
|
|
391
|
+
"multiple": false,
|
|
392
|
+
"type": "option"
|
|
393
|
+
},
|
|
394
|
+
"status": {
|
|
395
|
+
"description": "Filter by status (created, pending, running, success, failed, canceled, skipped, manual, scheduled)",
|
|
396
|
+
"name": "status",
|
|
397
|
+
"hasDynamicHelp": false,
|
|
398
|
+
"multiple": false,
|
|
399
|
+
"type": "option"
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
"hasDynamicHelp": false,
|
|
403
|
+
"hiddenAliases": [],
|
|
404
|
+
"id": "pipelines:list",
|
|
405
|
+
"pluginAlias": "duoops",
|
|
406
|
+
"pluginName": "duoops",
|
|
407
|
+
"pluginType": "core",
|
|
408
|
+
"strict": true,
|
|
409
|
+
"enableJsonFlag": false,
|
|
410
|
+
"isESM": true,
|
|
411
|
+
"relativePath": [
|
|
412
|
+
"dist",
|
|
413
|
+
"commands",
|
|
414
|
+
"pipelines",
|
|
415
|
+
"list.js"
|
|
416
|
+
]
|
|
417
|
+
},
|
|
418
|
+
"pipelines:show": {
|
|
419
|
+
"aliases": [],
|
|
420
|
+
"args": {
|
|
421
|
+
"project": {
|
|
422
|
+
"description": "Project ID (e.g. 123456)",
|
|
423
|
+
"name": "project",
|
|
424
|
+
"required": true
|
|
425
|
+
},
|
|
426
|
+
"pipeline_id": {
|
|
427
|
+
"description": "Pipeline ID",
|
|
428
|
+
"name": "pipeline_id",
|
|
429
|
+
"required": true
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"description": "Show pipeline details and jobs",
|
|
433
|
+
"examples": [
|
|
434
|
+
"<%= config.bin %> <%= command.id %> 12345 67890"
|
|
435
|
+
],
|
|
436
|
+
"flags": {},
|
|
437
|
+
"hasDynamicHelp": false,
|
|
438
|
+
"hiddenAliases": [],
|
|
439
|
+
"id": "pipelines:show",
|
|
440
|
+
"pluginAlias": "duoops",
|
|
441
|
+
"pluginName": "duoops",
|
|
442
|
+
"pluginType": "core",
|
|
443
|
+
"strict": true,
|
|
444
|
+
"enableJsonFlag": false,
|
|
445
|
+
"isESM": true,
|
|
446
|
+
"relativePath": [
|
|
447
|
+
"dist",
|
|
448
|
+
"commands",
|
|
449
|
+
"pipelines",
|
|
450
|
+
"show.js"
|
|
451
|
+
]
|
|
452
|
+
},
|
|
174
453
|
"measure:calculate": {
|
|
175
454
|
"aliases": [],
|
|
176
455
|
"args": {},
|
|
@@ -320,96 +599,6 @@
|
|
|
320
599
|
"seed.js"
|
|
321
600
|
]
|
|
322
601
|
},
|
|
323
|
-
"pipelines:list": {
|
|
324
|
-
"aliases": [],
|
|
325
|
-
"args": {
|
|
326
|
-
"project": {
|
|
327
|
-
"description": "Project ID or path (e.g. group/project)",
|
|
328
|
-
"name": "project",
|
|
329
|
-
"required": false
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
"description": "List GitLab CI pipelines for a project",
|
|
333
|
-
"examples": [
|
|
334
|
-
"<%= config.bin %> <%= command.id %> group/my-project",
|
|
335
|
-
"<%= config.bin %> <%= command.id %> 123 --limit 20 --ref main"
|
|
336
|
-
],
|
|
337
|
-
"flags": {
|
|
338
|
-
"limit": {
|
|
339
|
-
"char": "n",
|
|
340
|
-
"description": "Maximum number of pipelines to return",
|
|
341
|
-
"name": "limit",
|
|
342
|
-
"default": 10,
|
|
343
|
-
"hasDynamicHelp": false,
|
|
344
|
-
"multiple": false,
|
|
345
|
-
"type": "option"
|
|
346
|
-
},
|
|
347
|
-
"ref": {
|
|
348
|
-
"description": "Filter by branch or tag",
|
|
349
|
-
"name": "ref",
|
|
350
|
-
"hasDynamicHelp": false,
|
|
351
|
-
"multiple": false,
|
|
352
|
-
"type": "option"
|
|
353
|
-
},
|
|
354
|
-
"status": {
|
|
355
|
-
"description": "Filter by status (created, pending, running, success, failed, canceled, skipped, manual, scheduled)",
|
|
356
|
-
"name": "status",
|
|
357
|
-
"hasDynamicHelp": false,
|
|
358
|
-
"multiple": false,
|
|
359
|
-
"type": "option"
|
|
360
|
-
}
|
|
361
|
-
},
|
|
362
|
-
"hasDynamicHelp": false,
|
|
363
|
-
"hiddenAliases": [],
|
|
364
|
-
"id": "pipelines:list",
|
|
365
|
-
"pluginAlias": "duoops",
|
|
366
|
-
"pluginName": "duoops",
|
|
367
|
-
"pluginType": "core",
|
|
368
|
-
"strict": true,
|
|
369
|
-
"enableJsonFlag": false,
|
|
370
|
-
"isESM": true,
|
|
371
|
-
"relativePath": [
|
|
372
|
-
"dist",
|
|
373
|
-
"commands",
|
|
374
|
-
"pipelines",
|
|
375
|
-
"list.js"
|
|
376
|
-
]
|
|
377
|
-
},
|
|
378
|
-
"pipelines:show": {
|
|
379
|
-
"aliases": [],
|
|
380
|
-
"args": {
|
|
381
|
-
"project": {
|
|
382
|
-
"description": "Project ID (e.g. 123456)",
|
|
383
|
-
"name": "project",
|
|
384
|
-
"required": true
|
|
385
|
-
},
|
|
386
|
-
"pipeline_id": {
|
|
387
|
-
"description": "Pipeline ID",
|
|
388
|
-
"name": "pipeline_id",
|
|
389
|
-
"required": true
|
|
390
|
-
}
|
|
391
|
-
},
|
|
392
|
-
"description": "Show pipeline details and jobs",
|
|
393
|
-
"examples": [
|
|
394
|
-
"<%= config.bin %> <%= command.id %> 12345 67890"
|
|
395
|
-
],
|
|
396
|
-
"flags": {},
|
|
397
|
-
"hasDynamicHelp": false,
|
|
398
|
-
"hiddenAliases": [],
|
|
399
|
-
"id": "pipelines:show",
|
|
400
|
-
"pluginAlias": "duoops",
|
|
401
|
-
"pluginName": "duoops",
|
|
402
|
-
"pluginType": "core",
|
|
403
|
-
"strict": true,
|
|
404
|
-
"enableJsonFlag": false,
|
|
405
|
-
"isESM": true,
|
|
406
|
-
"relativePath": [
|
|
407
|
-
"dist",
|
|
408
|
-
"commands",
|
|
409
|
-
"pipelines",
|
|
410
|
-
"show.js"
|
|
411
|
-
]
|
|
412
|
-
},
|
|
413
602
|
"runner:logs": {
|
|
414
603
|
"aliases": [],
|
|
415
604
|
"args": {},
|
|
@@ -480,5 +669,5 @@
|
|
|
480
669
|
]
|
|
481
670
|
}
|
|
482
671
|
},
|
|
483
|
-
"version": "0.
|
|
672
|
+
"version": "0.2.0"
|
|
484
673
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "duoops",
|
|
3
3
|
"description": "Toolset for Explainable and Sustainable CI on Gitlab.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"author": "Younes Laaroussi",
|
|
6
6
|
"bin": {
|
|
7
7
|
"duoops": "./bin/run.js"
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"files": [
|
|
62
62
|
"./bin",
|
|
63
63
|
"./dist",
|
|
64
|
+
"./data",
|
|
64
65
|
"./oclif.manifest.json",
|
|
65
66
|
"./templates"
|
|
66
67
|
],
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
image: node:22-slim
|
|
2
|
+
commands:
|
|
3
|
+
- echo "=== Installing DuoOps CLI ==="
|
|
4
|
+
- npm install -g duoops@latest
|
|
5
|
+
|
|
6
|
+
- echo "=== Installing glab CLI ==="
|
|
7
|
+
- apt-get update --quiet && apt-get install --yes curl wget gpg git python3 && rm --recursive --force /var/lib/apt/lists/*
|
|
8
|
+
- curl --silent --show-error --location "https://raw.githubusercontent.com/upciti/wakemeops/main/assets/install_repository" | bash
|
|
9
|
+
- apt-get install -y glab
|
|
10
|
+
|
|
11
|
+
- echo "=== Configuring glab ==="
|
|
12
|
+
- mkdir -p ~/.config/glab-cli
|
|
13
|
+
- |
|
|
14
|
+
cat > ~/.config/glab-cli/config.yml <<EOF
|
|
15
|
+
hosts:
|
|
16
|
+
$AI_FLOW_GITLAB_HOSTNAME:
|
|
17
|
+
token: $AI_FLOW_GITLAB_TOKEN
|
|
18
|
+
is_oauth2: "true"
|
|
19
|
+
client_id: "bypass"
|
|
20
|
+
oauth2_refresh_token: ""
|
|
21
|
+
oauth2_expiry_date: "01 Jan 50 00:00 UTC"
|
|
22
|
+
api_host: $AI_FLOW_GITLAB_HOSTNAME
|
|
23
|
+
user: DuoOps
|
|
24
|
+
check_update: "false"
|
|
25
|
+
git_protocol: https
|
|
26
|
+
EOF
|
|
27
|
+
- chmod 600 ~/.config/glab-cli/config.yml
|
|
28
|
+
|
|
29
|
+
- echo "=== Configuring git ==="
|
|
30
|
+
- git config --global user.email "duoops@gitlab.com"
|
|
31
|
+
- git config --global user.name "DuoOps Agent"
|
|
32
|
+
- git remote set-url origin https://gitlab-ci-token:$AI_FLOW_GITLAB_TOKEN@$AI_FLOW_GITLAB_HOSTNAME/$AI_FLOW_PROJECT_PATH.git
|
|
33
|
+
|
|
34
|
+
- echo "=== Configuring DuoOps ==="
|
|
35
|
+
- export GITLAB_TOKEN=$AI_FLOW_GITLAB_TOKEN
|
|
36
|
+
- export GITLAB_HOST=https://$AI_FLOW_GITLAB_HOSTNAME
|
|
37
|
+
- export DUOOPS_PROJECT_ID=$AI_FLOW_PROJECT_PATH
|
|
38
|
+
|
|
39
|
+
- echo "=== Running DuoOps Agent ==="
|
|
40
|
+
- |
|
|
41
|
+
INPUT="$AI_FLOW_INPUT"
|
|
42
|
+
CONTEXT="$AI_FLOW_CONTEXT"
|
|
43
|
+
EVENT="$AI_FLOW_EVENT"
|
|
44
|
+
|
|
45
|
+
# Extract MR IID from context if available
|
|
46
|
+
MR_IID=$(echo "$CONTEXT" | python3 -c "
|
|
47
|
+
import sys, json
|
|
48
|
+
try:
|
|
49
|
+
ctx = json.load(sys.stdin)
|
|
50
|
+
iid = ctx.get('merge_request', {}).get('iid', '')
|
|
51
|
+
if iid: print(iid)
|
|
52
|
+
else: print('')
|
|
53
|
+
except: print('')
|
|
54
|
+
" 2>/dev/null || echo "")
|
|
55
|
+
|
|
56
|
+
# Extract Issue IID from context if available
|
|
57
|
+
ISSUE_IID=$(echo "$CONTEXT" | python3 -c "
|
|
58
|
+
import sys, json
|
|
59
|
+
try:
|
|
60
|
+
ctx = json.load(sys.stdin)
|
|
61
|
+
iid = ctx.get('issue', {}).get('iid', '')
|
|
62
|
+
if iid: print(iid)
|
|
63
|
+
else: print('')
|
|
64
|
+
except: print('')
|
|
65
|
+
" 2>/dev/null || echo "")
|
|
66
|
+
|
|
67
|
+
echo "Event: $EVENT"
|
|
68
|
+
echo "MR IID: $MR_IID"
|
|
69
|
+
echo "Issue IID: $ISSUE_IID"
|
|
70
|
+
echo "Input: $INPUT"
|
|
71
|
+
|
|
72
|
+
# Route to the appropriate DuoOps command based on intent
|
|
73
|
+
if echo "$INPUT" | grep -qiE "fix|fail|broken|debug|pipeline|autofix|error|crash|ci"; then
|
|
74
|
+
echo ">>> Pipeline failure analysis mode"
|
|
75
|
+
if [ -n "$MR_IID" ]; then
|
|
76
|
+
OUTPUT=$(duoops autofix-ci "$AI_FLOW_PROJECT_PATH" --mr "$MR_IID" 2>&1)
|
|
77
|
+
echo "$OUTPUT"
|
|
78
|
+
echo "Analysis posted to MR !$MR_IID"
|
|
79
|
+
else
|
|
80
|
+
OUTPUT=$(duoops autofix-ci "$AI_FLOW_PROJECT_PATH" 2>&1)
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
elif echo "$INPUT" | grep -qiE "carbon|emission|sustain|green|measure|energy|footprint|budget"; then
|
|
84
|
+
echo ">>> Sustainability analysis mode"
|
|
85
|
+
OUTPUT=$(duoops act "$AI_FLOW_PROJECT_PATH" --limit 20 2>&1)
|
|
86
|
+
|
|
87
|
+
else
|
|
88
|
+
echo ">>> General query mode"
|
|
89
|
+
OUTPUT=$(duoops ask "$INPUT" -P "$AI_FLOW_PROJECT_PATH" 2>&1)
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Post results back if we haven't already (autofix-ci with --mr handles its own posting)
|
|
93
|
+
if echo "$INPUT" | grep -qiE "fix|fail|broken|debug|pipeline|autofix|error|crash|ci" && [ -n "$MR_IID" ]; then
|
|
94
|
+
echo "Results already posted to MR"
|
|
95
|
+
elif [ -n "$MR_IID" ]; then
|
|
96
|
+
glab mr note "$MR_IID" --message "## DuoOps Agent Response
|
|
97
|
+
|
|
98
|
+
$OUTPUT
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
*Generated by [DuoOps](https://gitlab.com/youneslaaroussi/duoops) — Explainable & Sustainable CI*"
|
|
102
|
+
elif [ -n "$ISSUE_IID" ]; then
|
|
103
|
+
glab issue note "$ISSUE_IID" --message "## DuoOps Agent Response
|
|
104
|
+
|
|
105
|
+
$OUTPUT
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
*Generated by [DuoOps](https://gitlab.com/youneslaaroussi/duoops) — Explainable & Sustainable CI*"
|
|
109
|
+
else
|
|
110
|
+
echo "=== DuoOps Output ==="
|
|
111
|
+
echo "$OUTPUT"
|
|
112
|
+
fi
|
|
113
|
+
variables:
|
|
114
|
+
- ADDITIONAL_INSTRUCTIONS
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: "DuoOps Advisor"
|
|
2
|
+
description: "AI-powered CI/CD troubleshooting and carbon sustainability agent. Analyzes pipeline failures, fetches job logs, proposes fixes, and provides carbon emission insights for sustainable CI practices."
|
|
3
|
+
public: true
|
|
4
|
+
system_prompt: |
|
|
5
|
+
You are DuoOps, an AI DevOps assistant specializing in CI/CD reliability and sustainability on GitLab.
|
|
6
|
+
|
|
7
|
+
Your capabilities:
|
|
8
|
+
- Analyze pipeline failures by fetching job logs and identifying root causes
|
|
9
|
+
- Propose concrete fixes with specific CI configuration changes
|
|
10
|
+
- Measure and optimize the carbon footprint of CI/CD pipelines
|
|
11
|
+
- Review merge requests for CI/CD best practices and sustainability
|
|
12
|
+
|
|
13
|
+
When analyzing pipeline failures:
|
|
14
|
+
1. First use get_pipeline_failing_jobs or get_pipeline_errors to identify which jobs failed
|
|
15
|
+
2. Use get_job_logs to fetch the full logs of failed jobs
|
|
16
|
+
3. Identify the root cause from error messages and stack traces
|
|
17
|
+
4. Propose specific remediation steps referencing jobs and stages
|
|
18
|
+
5. Suggest CI configuration changes to prevent regressions
|
|
19
|
+
|
|
20
|
+
When reviewing sustainability:
|
|
21
|
+
- Look for redundant work (repeated npm install, uncached dependencies)
|
|
22
|
+
- Identify jobs that could run in parallel
|
|
23
|
+
- Suggest smaller runner machine types where appropriate
|
|
24
|
+
- Recommend caching strategies to reduce compute time and emissions
|
|
25
|
+
|
|
26
|
+
Always respond with clear Markdown formatting. Be concise and actionable.
|
|
27
|
+
When posting notes to merge requests or issues, structure your response with clear sections.
|
|
28
|
+
tools:
|
|
29
|
+
- get_job_logs
|
|
30
|
+
- get_pipeline_errors
|
|
31
|
+
- get_pipeline_failing_jobs
|
|
32
|
+
- get_merge_request
|
|
33
|
+
- list_merge_request_diffs
|
|
34
|
+
- create_merge_request_note
|
|
35
|
+
- create_issue_note
|
|
36
|
+
- get_issue
|
|
37
|
+
- get_repository_file
|
|
38
|
+
- read_file
|
|
39
|
+
- read_files
|
|
40
|
+
- list_dir
|
|
41
|
+
- find_files
|
|
42
|
+
- grep
|
|
43
|
+
- get_project
|
|
44
|
+
- list_commits
|
|
45
|
+
- get_commit_diff
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
spec:
|
|
2
|
+
inputs:
|
|
3
|
+
duoops_version:
|
|
4
|
+
description: "duoops npm package version"
|
|
5
|
+
type: string
|
|
6
|
+
default: "latest"
|
|
7
|
+
|
|
8
|
+
stage:
|
|
9
|
+
description: "GitLab CI stage to run in"
|
|
10
|
+
type: string
|
|
11
|
+
default: ".post"
|
|
12
|
+
|
|
13
|
+
tags:
|
|
14
|
+
description: "Runner tags"
|
|
15
|
+
type: array
|
|
16
|
+
default: []
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
duoops-autofix-analysis:
|
|
21
|
+
stage: $[[ inputs.stage ]]
|
|
22
|
+
tags: $[[ inputs.tags ]]
|
|
23
|
+
image: node:22-slim
|
|
24
|
+
variables:
|
|
25
|
+
DUOOPS_VERSION: $[[ inputs.duoops_version ]]
|
|
26
|
+
before_script:
|
|
27
|
+
- |
|
|
28
|
+
if command -v duoops &> /dev/null; then
|
|
29
|
+
echo "duoops already installed"
|
|
30
|
+
else
|
|
31
|
+
apt-get update && apt-get install -y git
|
|
32
|
+
npm install -g duoops@${DUOOPS_VERSION}
|
|
33
|
+
fi
|
|
34
|
+
script:
|
|
35
|
+
- |
|
|
36
|
+
export GITLAB_TOKEN="${GITLAB_TOKEN:-$CI_JOB_TOKEN}"
|
|
37
|
+
|
|
38
|
+
MR_FLAG=""
|
|
39
|
+
if [ -n "$CI_MERGE_REQUEST_IID" ]; then
|
|
40
|
+
MR_FLAG="--mr $CI_MERGE_REQUEST_IID"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
duoops autofix-ci "$CI_PROJECT_ID" --pipeline "$CI_PIPELINE_ID" $MR_FLAG | tee autofix-report.md
|
|
44
|
+
|
|
45
|
+
echo ""
|
|
46
|
+
echo "=== DuoOps Autofix Complete ==="
|
|
47
|
+
artifacts:
|
|
48
|
+
paths:
|
|
49
|
+
- autofix-report.md
|
|
50
|
+
expire_in: 30 days
|
|
51
|
+
rules:
|
|
52
|
+
- when: on_failure
|