melaya 0.1.4 → 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.
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Pipelines API — overview dashboard, pipeline run listing, traces, and
5
+ # cron-based scheduling.
6
+ #
7
+ # Maps to:
8
+ # /api/v1/private/overview/* — dashboard + run listing
9
+ # /api/v1/private/runs/:id/traces/* — distributed traces
10
+ # /api/v1/private/pipeline-schedule — cron scheduling
11
+ # /api/v1/version — server version (public)
12
+ #
13
+ # @example
14
+ # runs = melaya.pipelines.list(project: "my-project", limit: 20)
15
+ # melaya.pipelines.upsert_schedule("my-project", "nightly-report",
16
+ # cron: "0 2 * * *")
17
+ class PipelinesAPI
18
+ def initialize(http)
19
+ @http = http
20
+ end
21
+
22
+ # ── Overview ───────────────────────────────────────────────────────────────
23
+
24
+ # GET /api/v1/private/overview
25
+ # Dashboard overview: usage stats, active strategies, recent runs.
26
+ def overview
27
+ @http.get("/api/v1/private/overview")
28
+ end
29
+
30
+ # GET /api/v1/private/overview/model-prices
31
+ # Get pricing data for available AI models.
32
+ def model_prices
33
+ @http.get("/api/v1/private/overview/model-prices")
34
+ end
35
+
36
+ # GET /api/v1/private/overview/chart
37
+ # Get chart data for overview dashboard (cost/usage over time).
38
+ def chart_data(params = {})
39
+ @http.get("/api/v1/private/overview/chart", params)
40
+ end
41
+
42
+ # GET /api/v1/private/overview/cost-breakdown
43
+ # Get cost breakdown by model/provider.
44
+ def cost_breakdown(params = {})
45
+ @http.get("/api/v1/private/overview/cost-breakdown", params)
46
+ end
47
+
48
+ # GET /api/v1/private/overview/pipeline-count
49
+ # Count of pipeline runs grouped by status.
50
+ def count
51
+ @http.get("/api/v1/private/overview/pipeline-count")
52
+ end
53
+
54
+ # GET /api/v1/private/overview/pipelines
55
+ # Paginated list of pipeline runs.
56
+ # @param project [String, nil]
57
+ # @param pipeline_name [String, nil]
58
+ # @param status [String, nil]
59
+ # @param limit [Integer, nil]
60
+ # @param offset [Integer, nil]
61
+ def list(project: nil, pipeline_name: nil, status: nil, limit: nil, offset: nil)
62
+ @http.get("/api/v1/private/overview/pipelines",
63
+ compact("project" => project, "pipelineName" => pipeline_name,
64
+ "status" => status, "limit" => limit,
65
+ "offset" => offset))
66
+ end
67
+
68
+ # GET /api/v1/private/overview/pipelines/recent
69
+ # Most recent pipeline runs for a dashboard widget.
70
+ def recent
71
+ @http.get("/api/v1/private/overview/pipelines/recent")
72
+ end
73
+
74
+ # ── Traces ─────────────────────────────────────────────────────────────────
75
+
76
+ # GET /api/v1/private/runs/:runId/traces
77
+ # List traces for a run (paginated).
78
+ #
79
+ # Returns a paginated envelope, NOT a flat array:
80
+ # {
81
+ # "data" => {
82
+ # "list" => Array<Hash>, # trace summaries
83
+ # "total" => Integer, # total matching traces
84
+ # "page" => Integer,
85
+ # "pageSize" => Integer
86
+ # }
87
+ # }
88
+ #
89
+ # @param run_id [String]
90
+ # @param params [Hash] optional pagination / filter params (page:, pageSize:, ...)
91
+ # @return [Hash] paginated envelope as described above
92
+ def traces(run_id, params = {})
93
+ @http.get("/api/v1/private/runs/#{enc(run_id)}/traces", params)
94
+ end
95
+
96
+ # GET /api/v1/private/runs/:runId/traces/:traceId
97
+ # Get a single trace by ID.
98
+ def trace(run_id, trace_id)
99
+ @http.get("/api/v1/private/runs/#{enc(run_id)}/traces/#{enc(trace_id)}")
100
+ end
101
+
102
+ # GET /api/v1/private/runs/:runId/traces/:traceId/stats
103
+ # Get statistics for a specific trace.
104
+ def trace_stats(run_id, trace_id)
105
+ @http.get("/api/v1/private/runs/#{enc(run_id)}/traces/#{enc(trace_id)}/stats")
106
+ end
107
+
108
+ # DELETE /api/v1/private/runs/:runId/traces
109
+ # Delete all traces (and their spans) for a run by runId.
110
+ # No request body required — the runId in the path identifies the target.
111
+ #
112
+ # @param run_id [String]
113
+ # @return [Hash] { "deletedSpans" => Integer, "requestedTraces" => Integer (optional) }
114
+ def delete_traces(run_id)
115
+ @http.delete("/api/v1/private/runs/#{enc(run_id)}/traces")
116
+ end
117
+
118
+ # ── Schedule ───────────────────────────────────────────────────────────────
119
+
120
+ # GET /api/v1/private/pipeline-schedule
121
+ # List all pipeline schedules accessible to the caller.
122
+ def list_schedules
123
+ @http.get("/api/v1/private/pipeline-schedule")
124
+ end
125
+
126
+ # GET /api/v1/private/pipeline-schedule/:project/:pipelineName
127
+ # Get schedule status for a pipeline.
128
+ # @param project [String]
129
+ # @param pipeline_name [String]
130
+ def get_schedule(project, pipeline_name)
131
+ @http.get("/api/v1/private/pipeline-schedule/#{enc(project)}/#{enc(pipeline_name)}")
132
+ end
133
+
134
+ # PUT /api/v1/private/pipeline-schedule/:project/:pipelineName
135
+ # Create or update a pipeline schedule (cron expression + optional config).
136
+ # @param project [String]
137
+ # @param pipeline_name [String]
138
+ # @param cron [String] cron expression e.g. "0 2 * * *"
139
+ # @param config [Hash, nil] optional pipeline config overrides
140
+ def upsert_schedule(project, pipeline_name, cron:, config: nil)
141
+ body = compact("cron" => cron, "config" => config)
142
+ @http.put("/api/v1/private/pipeline-schedule/#{enc(project)}/#{enc(pipeline_name)}", body)
143
+ end
144
+
145
+ # POST /api/v1/private/pipeline-schedule/:project/:pipelineName/pause
146
+ # Pause a pipeline schedule.
147
+ def pause_schedule(project, pipeline_name)
148
+ @http.post("/api/v1/private/pipeline-schedule/#{enc(project)}/#{enc(pipeline_name)}/pause")
149
+ end
150
+
151
+ # POST /api/v1/private/pipeline-schedule/:project/:pipelineName/resume
152
+ # Resume a paused pipeline schedule.
153
+ def resume_schedule(project, pipeline_name)
154
+ @http.post("/api/v1/private/pipeline-schedule/#{enc(project)}/#{enc(pipeline_name)}/resume")
155
+ end
156
+
157
+ # ── Pipeline lifecycle (CRUD + run + outputs + AI build) ──────────────────
158
+
159
+ # GET /api/v1/private/pipelines
160
+ # List all pipeline configs accessible to the caller.
161
+ # Returns { "pipelines" => [...] }.
162
+ def list_pipelines
163
+ @http.get("/api/v1/private/pipelines")
164
+ end
165
+
166
+ # POST /api/v1/private/pipelines
167
+ # Create a new pipeline config.
168
+ # @param name [String] pipeline name
169
+ # @param project [String] owning project
170
+ # @param description [String, nil]
171
+ # @param config [Hash] additional config keys merged into the request body
172
+ # @return [Hash] created pipeline config
173
+ def create(name:, project:, description: nil, **config)
174
+ body = compact(
175
+ "name" => name,
176
+ "project" => project,
177
+ "description" => description
178
+ ).merge(stringify_keys(config))
179
+ @http.post("/api/v1/private/pipelines", body)
180
+ end
181
+
182
+ # GET /api/v1/private/pipelines/:name
183
+ # Fetch a single pipeline config by name.
184
+ # @param name [String] pipeline name
185
+ # @param project [String, nil] owning project (disambiguates when multiple projects share a name)
186
+ # @return [Hash] pipeline config
187
+ def get(name, project: nil)
188
+ params = compact("project" => project)
189
+ @http.get("/api/v1/private/pipelines/#{enc(name)}", params)
190
+ end
191
+
192
+ # PUT /api/v1/private/pipelines/:name
193
+ # Replace a pipeline's config.
194
+ # @param name [String] pipeline name
195
+ # @param config [Hash] full pipeline config payload
196
+ # @param project [String, nil] owning project
197
+ # @return [Hash] updated pipeline config
198
+ def update(name, config:, project: nil)
199
+ body = compact("config" => config, "project" => project)
200
+ @http.put("/api/v1/private/pipelines/#{enc(name)}", body)
201
+ end
202
+
203
+ # DELETE /api/v1/private/pipelines/:name
204
+ # Delete a pipeline config.
205
+ # @param name [String] pipeline name
206
+ # @param project [String, nil] owning project
207
+ # @return [Hash] empty hash on success
208
+ def delete_pipeline(name, project: nil)
209
+ params = compact("project" => project)
210
+ @http.delete("/api/v1/private/pipelines/#{enc(name)}", params)
211
+ end
212
+
213
+ # POST /api/v1/private/pipelines/:name/run
214
+ # Enqueue a pipeline run.
215
+ # @param name [String] pipeline name
216
+ # @param project [String, nil]
217
+ # @param execution_target [String, nil] runner target identifier
218
+ # @param studio_url [String, nil] override studio URL
219
+ # @param env_overrides [Hash, nil] environment variable overrides
220
+ # @return [Hash] { "run_id" => String, "queued" => Boolean }
221
+ def run(name, project: nil, execution_target: nil, studio_url: nil, env_overrides: nil)
222
+ body = compact(
223
+ "project" => project,
224
+ "executionTarget" => execution_target,
225
+ "studio_url" => studio_url,
226
+ "env_overrides" => env_overrides
227
+ )
228
+ @http.post("/api/v1/private/pipelines/#{enc(name)}/run", body.empty? ? nil : body)
229
+ end
230
+
231
+ # GET /api/v1/private/pipelines/:name/runs
232
+ # List all run IDs for a pipeline.
233
+ # @param name [String] pipeline name
234
+ # @return [Hash] { "run_ids" => Array<String> }
235
+ def run_ids(name)
236
+ @http.get("/api/v1/private/pipelines/#{enc(name)}/runs")
237
+ end
238
+
239
+ # GET /api/v1/private/pipelines/:name/runs/:run_id
240
+ # Get the status of a specific pipeline run.
241
+ # @param name [String] pipeline name
242
+ # @param run_id [String] run identifier
243
+ # @return [Hash] { "runId", "status", "createdAt", "executionTarget", "cost" }
244
+ def run_status(name, run_id)
245
+ @http.get("/api/v1/private/pipelines/#{enc(name)}/runs/#{enc(run_id)}")
246
+ end
247
+
248
+ # DELETE /api/v1/private/pipelines/:name/runs/:run_id
249
+ # Cancel an in-progress pipeline run.
250
+ # @param name [String] pipeline name
251
+ # @param run_id [String] run identifier
252
+ # @return [Hash] empty hash on success
253
+ def cancel_run(name, run_id)
254
+ @http.delete("/api/v1/private/pipelines/#{enc(name)}/runs/#{enc(run_id)}")
255
+ end
256
+
257
+ # GET /api/v1/private/pipelines/:name/outputs
258
+ # List all output artifacts produced by a pipeline.
259
+ # @param name [String] pipeline name
260
+ # @return artifacts listing
261
+ def outputs(name)
262
+ @http.get("/api/v1/private/pipelines/#{enc(name)}/outputs")
263
+ end
264
+
265
+ # GET /api/v1/private/pipelines/:name/outputs/:path
266
+ # Fetch a specific output artifact. Each segment of +path+ is individually
267
+ # URL-encoded so slashes in segment values are preserved as separators.
268
+ # @param name [String] pipeline name
269
+ # @param path [String] artifact path, e.g. "reports/2024-01/summary.json"
270
+ # @param download [Boolean] if true, adds +?download=1+ to trigger a download response
271
+ # @return artifact content or download redirect
272
+ def output(name, path, download: false)
273
+ encoded_path = path.to_s.split("/").map { |seg| enc(seg) }.join("/")
274
+ params = download ? { "download" => 1 } : {}
275
+ @http.get("/api/v1/private/pipelines/#{enc(name)}/outputs/#{encoded_path}", params)
276
+ end
277
+
278
+ # POST /api/v1/private/pipelines/preview-code
279
+ # Generate a code preview for a pipeline config without saving it.
280
+ # @param config [Hash] pipeline config to preview
281
+ # @return preview payload
282
+ def preview_code(config)
283
+ @http.post("/api/v1/private/pipelines/preview-code", config)
284
+ end
285
+
286
+ # GET /api/v1/private/pipelines/tools
287
+ # Fetch the registry of tools available to pipeline steps.
288
+ # @return [Hash] tool registry
289
+ def tools
290
+ @http.get("/api/v1/private/pipelines/tools")
291
+ end
292
+
293
+ # GET /api/v1/private/pipelines/subagents
294
+ # Fetch the registry of sub-agent definitions available to pipelines.
295
+ # @return [Hash] sub-agent registry
296
+ def subagents
297
+ @http.get("/api/v1/private/pipelines/subagents")
298
+ end
299
+
300
+ # POST /api/v1/private/templates/:template_id/instantiate
301
+ # Instantiate a platform template into a new pipeline config.
302
+ # @param template_id [String] the template to instantiate
303
+ # @param name [String] name for the resulting pipeline
304
+ # @param project [String] target project
305
+ # @param overrides [Hash, nil] optional config overrides applied on top of the template defaults
306
+ # @return [Hash] { "pipeline" => config }
307
+ def instantiate_template(template_id, name:, project:, overrides: nil)
308
+ body = compact("name" => name, "project" => project, "overrides" => overrides)
309
+ @http.post("/api/v1/private/templates/#{enc(template_id)}/instantiate", body)
310
+ end
311
+
312
+ # POST /api/v1/private/ai/build-pipeline/sync
313
+ # Generate a pipeline config from a natural-language brief using AI.
314
+ # @param brief [Hash] free-form brief payload sent to the AI builder
315
+ # @return [Hash] generated pipeline config
316
+ def build_with_ai(brief)
317
+ @http.post("/api/v1/private/ai/build-pipeline/sync", brief)
318
+ end
319
+
320
+ # ── Misc ───────────────────────────────────────────────────────────────────
321
+
322
+ # GET /api/v1/version (public)
323
+ # Get current server version string.
324
+ def server_version
325
+ @http.get("/api/v1/version")
326
+ end
327
+
328
+ private
329
+
330
+ def enc(s)
331
+ URI.encode_www_form_component(s.to_s)
332
+ end
333
+
334
+ def compact(hash)
335
+ hash.reject { |_, v| v.nil? }
336
+ end
337
+
338
+ def stringify_keys(hash)
339
+ hash.transform_keys(&:to_s)
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Projects API — create and list agent projects.
5
+ #
6
+ # Projects are the top-level namespace for pipelines, connectors, and team
7
+ # membership in the Melaya platform.
8
+ #
9
+ # Maps to /api/v1/private/projects/*.
10
+ #
11
+ # @example
12
+ # projects = melaya.projects.list
13
+ # project = melaya.projects.create(name: "my-project", description: "...")
14
+ # melaya.projects.rename(old_name: "my-project", new_name: "renamed-project")
15
+ class ProjectsAPI
16
+ def initialize(http)
17
+ @http = http
18
+ end
19
+
20
+ # GET /api/v1/private/projects
21
+ # List all projects the authenticated user can access
22
+ # (owned projects + projects they are a member of).
23
+ def list
24
+ @http.get("/api/v1/private/projects")
25
+ end
26
+
27
+ # POST /api/v1/private/projects
28
+ # Create a new agent project.
29
+ # @param name [String]
30
+ # @param description [String, nil]
31
+ def create(name:, description: nil)
32
+ body = compact("name" => name, "description" => description)
33
+ @http.post("/api/v1/private/projects", body)
34
+ end
35
+
36
+ # PATCH /api/v1/private/projects/rename
37
+ # Rename a project (body: oldName, newName).
38
+ # @param old_name [String]
39
+ # @param new_name [String]
40
+ def rename(old_name:, new_name:)
41
+ @http.patch("/api/v1/private/projects/rename",
42
+ "oldName" => old_name,
43
+ "newName" => new_name)
44
+ end
45
+
46
+ # GET /api/v1/private/projects/runner
47
+ # Get projects list (runner-facing, authed).
48
+ def runner_projects
49
+ @http.get("/api/v1/private/projects/runner")
50
+ end
51
+
52
+ private
53
+
54
+ def compact(hash)
55
+ hash.reject { |_, v| v.nil? }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Runner API — mint, list, and revoke runner tokens.
5
+ #
6
+ # Runner tokens (mel_run_ prefix) authenticate the Melaya runner CLI process
7
+ # that executes agent pipelines on your infrastructure.
8
+ # The plaintext token is returned only once on creation — store it securely.
9
+ #
10
+ # Maps to /api/v1/private/runner/tokens/*.
11
+ #
12
+ # @example
13
+ # result = melaya.runner.create_token(label: "prod-server-1")
14
+ # token = result["token"] # store securely — shown only once
15
+ #
16
+ # tokens = melaya.runner.list_tokens
17
+ # melaya.runner.revoke_token(tokens.first["id"])
18
+ class RunnerAPI
19
+ def initialize(http)
20
+ @http = http
21
+ end
22
+
23
+ # POST /api/v1/private/runner/tokens
24
+ # Mint a new mel_run_ runner token.
25
+ # @param label [String, nil] human-readable label for the token
26
+ def create_token(label: nil)
27
+ body = label ? { "label" => label } : nil
28
+ @http.post("/api/v1/private/runner/tokens", body)
29
+ end
30
+
31
+ # GET /api/v1/private/runner/tokens
32
+ # List all runner tokens for the caller (masked, with last_seen).
33
+ def list_tokens
34
+ @http.get("/api/v1/private/runner/tokens")
35
+ end
36
+
37
+ # DELETE /api/v1/private/runner/tokens/:tokenId
38
+ # Revoke a runner token by ID.
39
+ # @param token_id [String]
40
+ def revoke_token(token_id)
41
+ @http.delete("/api/v1/private/runner/tokens/#{URI.encode_www_form_component(token_id.to_s)}")
42
+ end
43
+ end
44
+ end
@@ -151,5 +151,20 @@ module Melaya
151
151
  def ai_opt_runs(strategy_id)
152
152
  @http.get("/api/v1/strategies/#{strategy_id}/ai-opt/runs")
153
153
  end
154
+
155
+ # ── restRoutes.ts strategies endpoints ────────────────────────────────────
156
+
157
+ # GET /api/v1/private/strategies/team
158
+ # List strategies visible to the caller's team project.
159
+ def list_team
160
+ @http.get("/api/v1/private/strategies/team")
161
+ end
162
+
163
+ # POST /api/v1/private/strategies/summaries/bulk
164
+ # Bulk-fetch lightweight summaries for a list of strategy IDs.
165
+ # @param strategy_ids [Array<String>]
166
+ def summaries_bulk(strategy_ids)
167
+ @http.post("/api/v1/private/strategies/summaries/bulk", "strategyIds" => strategy_ids)
168
+ end
154
169
  end
155
170
  end
data/lib/melaya/stream.rb CHANGED
@@ -42,8 +42,9 @@ module Melaya
42
42
  end
43
43
 
44
44
  def initialize(url, verify_ssl: true)
45
+ raise ArgumentError, "Melaya: TLS certificate verification cannot be disabled." unless verify_ssl
45
46
  @uri = URI.parse(url)
46
- @verify_ssl = verify_ssl
47
+ @verify_ssl = true
47
48
  @socket = nil
48
49
  @closed = false
49
50
  @buf = String.new("", encoding: "BINARY")
@@ -54,7 +55,7 @@ module Melaya
54
55
 
55
56
  @socket = if @uri.scheme == "wss"
56
57
  ctx = OpenSSL::SSL::SSLContext.new
57
- ctx.verify_mode = @verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
58
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
58
59
  ssl = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
59
60
  ssl.hostname = @uri.host
60
61
  ssl.connect
@@ -259,10 +260,11 @@ module Melaya
259
260
  DEFAULT_WS_URL = "wss://wss.melaya.org"
260
261
 
261
262
  def initialize(api_key, ws_url, http, verify_ssl: true)
263
+ raise ArgumentError, "Melaya: TLS certificate verification cannot be disabled." unless verify_ssl
262
264
  @api_key = api_key
263
265
  @ws_url = ws_url.to_s.chomp("/")
264
266
  @http = http
265
- @verify_ssl = verify_ssl
267
+ @verify_ssl = true
266
268
  end
267
269
 
268
270
  # Live ticker frames.
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melaya
4
+ # Team API — manage project team membership, roles, and invitations.
5
+ #
6
+ # Maps to:
7
+ # /api/v1/private/projects/:project/members/*
8
+ # /api/v1/private/team/*
9
+ # /api/v1/private/projects/:project/pipelines/:pipeline/visibility
10
+ #
11
+ # @example
12
+ # members = melaya.team.list_members("my-project")
13
+ # melaya.team.invite("my-project", username: "alice")
14
+ # link = melaya.team.create_invite_link("my-project")
15
+ class TeamAPI
16
+ def initialize(http)
17
+ @http = http
18
+ end
19
+
20
+ # GET /api/v1/private/projects/:project/members
21
+ # List members of a project team.
22
+ # @param project [String] project name
23
+ def list_members(project)
24
+ @http.get("/api/v1/private/projects/#{enc(project)}/members")
25
+ end
26
+
27
+ # POST /api/v1/private/projects/:project/members/invite
28
+ # Invite a user to a project team by username.
29
+ # @param project [String]
30
+ # @param username [String]
31
+ def invite(project, username:)
32
+ @http.post("/api/v1/private/projects/#{enc(project)}/members/invite",
33
+ "username" => username)
34
+ end
35
+
36
+ # POST /api/v1/private/projects/:project/invite-link
37
+ # Create a shareable invite link for a project.
38
+ # Returns the URL to send to new team members.
39
+ # @param project [String]
40
+ def create_invite_link(project)
41
+ @http.post("/api/v1/private/projects/#{enc(project)}/invite-link")
42
+ end
43
+
44
+ # POST /api/v1/private/team/invite/accept
45
+ # Accept a project invite using the token from an invite link.
46
+ # @param token [String]
47
+ def accept_invite(token)
48
+ @http.post("/api/v1/private/team/invite/accept", "token" => token)
49
+ end
50
+
51
+ # PATCH /api/v1/private/projects/:project/members/:userId
52
+ # Update a team member's role in a project.
53
+ # @param project [String]
54
+ # @param user_id [String]
55
+ # @param role [String] one of "owner", "editor", "viewer"
56
+ def update_member_role(project, user_id, role:)
57
+ @http.patch(
58
+ "/api/v1/private/projects/#{enc(project)}/members/#{enc(user_id)}",
59
+ "role" => role
60
+ )
61
+ end
62
+
63
+ # DELETE /api/v1/private/projects/:project/members/:userId
64
+ # Remove a member from a project team.
65
+ # @param project [String]
66
+ # @param user_id [String]
67
+ def remove_member(project, user_id)
68
+ @http.delete("/api/v1/private/projects/#{enc(project)}/members/#{enc(user_id)}")
69
+ end
70
+
71
+ # ── Pipeline visibility ────────────────────────────────────────────────────
72
+
73
+ # GET /api/v1/private/projects/:project/pipelines/:pipeline/visibility
74
+ # Get visibility settings for a pipeline within a project.
75
+ # @param project [String]
76
+ # @param pipeline [String]
77
+ def get_pipeline_visibility(project, pipeline)
78
+ @http.get(
79
+ "/api/v1/private/projects/#{enc(project)}/pipelines/#{enc(pipeline)}/visibility"
80
+ )
81
+ end
82
+
83
+ # PUT /api/v1/private/projects/:project/pipelines/:pipeline/visibility
84
+ # Set pipeline visibility within a project.
85
+ # @param project [String]
86
+ # @param pipeline [String]
87
+ # @param body [Hash]
88
+ def set_pipeline_visibility(project, pipeline, body = {})
89
+ @http.put(
90
+ "/api/v1/private/projects/#{enc(project)}/pipelines/#{enc(pipeline)}/visibility",
91
+ body
92
+ )
93
+ end
94
+
95
+ private
96
+
97
+ def enc(s)
98
+ URI.encode_www_form_component(s.to_s)
99
+ end
100
+ end
101
+ end
102
+