stakwork-cli 0.1.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.
- checksums.yaml +7 -0
- data/bin/stakwork +9 -0
- data/lib/stakwork_cli/client.rb +84 -0
- data/lib/stakwork_cli/commands/auth.rb +41 -0
- data/lib/stakwork_cli/commands/customers.rb +37 -0
- data/lib/stakwork_cli/commands/projects.rb +76 -0
- data/lib/stakwork_cli/commands/prompts.rb +71 -0
- data/lib/stakwork_cli/commands/skills.rb +46 -0
- data/lib/stakwork_cli/commands/workflows.rb +104 -0
- data/lib/stakwork_cli/config.rb +43 -0
- data/lib/stakwork_cli/version.rb +3 -0
- data/lib/stakwork_cli.rb +48 -0
- metadata +68 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c532528e8fdf9e6ce51774140b9d9b35e30a5aa9d2ef178e2ea211fe5692b974
|
|
4
|
+
data.tar.gz: 0be1d458ffd97d73ac60f7256da4bb151a29c891b3432e092f325e608dd8263e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 83f75216f8be15134d037be12848ccaae6654ac8cb4c76a24445f42eb56e8839ceb7cb3f2d9d2b0fa8f00aec6d1575d261db328ac42232c0690ca4d9489845a6
|
|
7
|
+
data.tar.gz: b7bb3fbcb4d6219f9f64cb77011e70c693b86b03e0b2c091a873ec9e5e90632684ca2e0c8a551a31b6b6ab4090a38241384979ef0c380406bb853e696c95bc10
|
data/bin/stakwork
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module StakworkCli
|
|
6
|
+
class ApiError < StandardError
|
|
7
|
+
attr_reader :status, :body
|
|
8
|
+
|
|
9
|
+
def initialize(status, body)
|
|
10
|
+
@status = status
|
|
11
|
+
@body = body
|
|
12
|
+
super("API error #{status}: #{body}")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Client
|
|
17
|
+
def initialize(token:, base_url:)
|
|
18
|
+
@token = token
|
|
19
|
+
@base_url = base_url.chomp('/')
|
|
20
|
+
@uri = URI.parse(@base_url)
|
|
21
|
+
warn_if_insecure
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get(path, params: {})
|
|
25
|
+
uri = build_uri(path, params)
|
|
26
|
+
request = Net::HTTP::Get.new(uri)
|
|
27
|
+
perform(request, uri)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def post(path, body: {})
|
|
31
|
+
uri = build_uri(path)
|
|
32
|
+
request = Net::HTTP::Post.new(uri)
|
|
33
|
+
request.body = body.to_json
|
|
34
|
+
perform(request, uri)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def put(path, body: {})
|
|
38
|
+
uri = build_uri(path)
|
|
39
|
+
request = Net::HTTP::Put.new(uri)
|
|
40
|
+
request.body = body.to_json
|
|
41
|
+
perform(request, uri)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete(path)
|
|
45
|
+
uri = build_uri(path)
|
|
46
|
+
request = Net::HTTP::Delete.new(uri)
|
|
47
|
+
perform(request, uri)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def build_uri(path, params = {})
|
|
53
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
54
|
+
unless params.empty?
|
|
55
|
+
uri.query = URI.encode_www_form(params.reject { |_, v| v.nil? })
|
|
56
|
+
end
|
|
57
|
+
uri
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def perform(request, uri)
|
|
61
|
+
request['Content-Type'] = 'application/json'
|
|
62
|
+
request['Accept'] = 'application/json'
|
|
63
|
+
request['Authorization'] = "Token token=\"#{@token}\""
|
|
64
|
+
|
|
65
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
66
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
67
|
+
|
|
68
|
+
response = http.request(request)
|
|
69
|
+
|
|
70
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
71
|
+
raise ApiError.new(response.code.to_i, response.body)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
response.body
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def warn_if_insecure
|
|
78
|
+
return if @uri.scheme == 'https'
|
|
79
|
+
host = @uri.host.to_s
|
|
80
|
+
return if host == 'localhost' || host == 'lvh.me' || host.start_with?('127.')
|
|
81
|
+
$stderr.puts 'Warning: connecting over HTTP to a non-local host. Consider using HTTPS.'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
|
|
5
|
+
module StakworkCli
|
|
6
|
+
module Commands
|
|
7
|
+
class Auth < Thor
|
|
8
|
+
desc 'login', 'Save credentials to ~/.stakwork/config.yml'
|
|
9
|
+
option :token, required: true, desc: 'API token'
|
|
10
|
+
option :host, default: Config::DEFAULT_HOST, desc: 'API host URL'
|
|
11
|
+
def login
|
|
12
|
+
Config.save(token: options[:token], host: options[:host])
|
|
13
|
+
puts "✅ Credentials saved to ~/.stakwork/config.yml"
|
|
14
|
+
puts " Host: #{options[:host]}"
|
|
15
|
+
puts " Token: #{mask_token(options[:token])}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc 'whoami', 'Show current configured credentials'
|
|
19
|
+
option :token, desc: 'Inline token override'
|
|
20
|
+
option :host, desc: 'Inline host override'
|
|
21
|
+
option :pretty, type: :boolean, default: false, desc: 'Pretty-print output'
|
|
22
|
+
def whoami
|
|
23
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
24
|
+
result = {
|
|
25
|
+
host: cfg[:host],
|
|
26
|
+
token: mask_token(cfg[:token].to_s)
|
|
27
|
+
}
|
|
28
|
+
output = options[:pretty] ? JSON.pretty_generate(result) : result.to_json
|
|
29
|
+
puts output
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def mask_token(token)
|
|
35
|
+
return '(not set)' if token.nil? || token.empty?
|
|
36
|
+
return token if token.length <= 8
|
|
37
|
+
"#{token[0, 4]}...#{token[-4..]}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../client'
|
|
5
|
+
|
|
6
|
+
module StakworkCli
|
|
7
|
+
module Commands
|
|
8
|
+
class Customers < Thor
|
|
9
|
+
class_option :token, desc: 'API token (overrides config/env)'
|
|
10
|
+
class_option :host, desc: 'API host (overrides config/env)'
|
|
11
|
+
class_option :pretty, type: :boolean, default: false, desc: 'Pretty-print JSON output'
|
|
12
|
+
|
|
13
|
+
desc 'create', 'Create a new customer'
|
|
14
|
+
option :name, required: true, desc: 'Customer name'
|
|
15
|
+
def create
|
|
16
|
+
body = { customer: { name: options[:name] } }
|
|
17
|
+
result = client.post('/api/v1/customers', body: body)
|
|
18
|
+
print_output(result)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def client
|
|
24
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
25
|
+
Client.new(token: cfg[:token], base_url: cfg[:host])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def print_output(raw)
|
|
29
|
+
if options[:pretty]
|
|
30
|
+
puts JSON.pretty_generate(JSON.parse(raw))
|
|
31
|
+
else
|
|
32
|
+
puts raw
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../client'
|
|
5
|
+
|
|
6
|
+
module StakworkCli
|
|
7
|
+
module Commands
|
|
8
|
+
class Projects < Thor
|
|
9
|
+
class_option :token, desc: 'API token (overrides config/env)'
|
|
10
|
+
class_option :host, desc: 'API host (overrides config/env)'
|
|
11
|
+
class_option :pretty, type: :boolean, default: false, desc: 'Pretty-print JSON output'
|
|
12
|
+
|
|
13
|
+
desc 'status ID', 'Get the status of a project run'
|
|
14
|
+
def status(id)
|
|
15
|
+
result = client.get("/api/v1/projects/#{id}/status")
|
|
16
|
+
print_output(result)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc 'steps ID', 'Get the steps of a project run'
|
|
20
|
+
option :page, type: :numeric, desc: 'Page number'
|
|
21
|
+
option :'step-id', type: :numeric, desc: 'Filter by step ID'
|
|
22
|
+
def steps(id)
|
|
23
|
+
params = {}
|
|
24
|
+
params[:page] = options[:page] if options[:page]
|
|
25
|
+
params[:step_id] = options[:'step-id'] if options[:'step-id']
|
|
26
|
+
result = client.get("/api/v1/projects/#{id}/steps", params: params)
|
|
27
|
+
print_output(result)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
desc 'retry ID', 'Retry a failed project run'
|
|
31
|
+
def retry(id)
|
|
32
|
+
result = client.post("/api/v1/projects/#{id}/retry")
|
|
33
|
+
print_output(result)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc 'stop ID', 'Stop a running project'
|
|
37
|
+
def stop(id)
|
|
38
|
+
result = client.post("/api/v1/projects/#{id}/stop")
|
|
39
|
+
print_output(result)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc 'pause ID', 'Pause a running project'
|
|
43
|
+
option :reason, desc: 'Reason for pausing'
|
|
44
|
+
def pause(id)
|
|
45
|
+
body = {}
|
|
46
|
+
body[:reason] = options[:reason] if options[:reason]
|
|
47
|
+
result = client.post("/api/v1/projects/#{id}/pause", body: body)
|
|
48
|
+
print_output(result)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'resume ID', 'Resume a paused project'
|
|
52
|
+
option :instructions, desc: 'Instructions for resuming'
|
|
53
|
+
def resume(id)
|
|
54
|
+
body = {}
|
|
55
|
+
body[:instructions] = options[:instructions] if options[:instructions]
|
|
56
|
+
result = client.post("/api/v1/projects/#{id}/resume", body: body)
|
|
57
|
+
print_output(result)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def client
|
|
63
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
64
|
+
Client.new(token: cfg[:token], base_url: cfg[:host])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def print_output(raw)
|
|
68
|
+
if options[:pretty]
|
|
69
|
+
puts JSON.pretty_generate(JSON.parse(raw))
|
|
70
|
+
else
|
|
71
|
+
puts raw
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../client'
|
|
5
|
+
|
|
6
|
+
module StakworkCli
|
|
7
|
+
module Commands
|
|
8
|
+
class Prompts < Thor
|
|
9
|
+
class_option :token, desc: 'API token (overrides config/env)'
|
|
10
|
+
class_option :host, desc: 'API host (overrides config/env)'
|
|
11
|
+
class_option :pretty, type: :boolean, default: false, desc: 'Pretty-print JSON output'
|
|
12
|
+
|
|
13
|
+
desc 'list', 'List all prompts'
|
|
14
|
+
def list
|
|
15
|
+
result = client.get('/api/v1/prompts')
|
|
16
|
+
print_output(result)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc 'show ID', 'Show a prompt by ID'
|
|
20
|
+
def show(id)
|
|
21
|
+
result = client.get("/api/v1/prompts/#{id}")
|
|
22
|
+
print_output(result)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
desc 'create', 'Create a new prompt'
|
|
26
|
+
option :name, required: true, desc: 'Prompt name'
|
|
27
|
+
option :value, required: true, desc: 'Prompt value/body'
|
|
28
|
+
def create
|
|
29
|
+
body = { prompt: { name: options[:name], value: options[:value] } }
|
|
30
|
+
result = client.post('/api/v1/prompts', body: body)
|
|
31
|
+
print_output(result)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc 'update ID', 'Update an existing prompt'
|
|
35
|
+
option :value, required: true, desc: 'New prompt value/body'
|
|
36
|
+
def update(id)
|
|
37
|
+
body = { prompt: { value: options[:value] } }
|
|
38
|
+
result = client.put("/api/v1/prompts/#{id}", body: body)
|
|
39
|
+
print_output(result)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc 'delete ID', 'Delete a prompt'
|
|
43
|
+
def delete(id)
|
|
44
|
+
result = client.delete("/api/v1/prompts/#{id}")
|
|
45
|
+
print_output(result)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc 'publish ID', 'Publish a specific version of a prompt'
|
|
49
|
+
option :version, required: true, desc: 'Version ID to publish'
|
|
50
|
+
def publish(id)
|
|
51
|
+
result = client.post("/api/v1/prompts/#{id}/versions/#{options[:version]}/publish")
|
|
52
|
+
print_output(result)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def client
|
|
58
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
59
|
+
Client.new(token: cfg[:token], base_url: cfg[:host])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def print_output(raw)
|
|
63
|
+
if options[:pretty]
|
|
64
|
+
puts JSON.pretty_generate(JSON.parse(raw))
|
|
65
|
+
else
|
|
66
|
+
puts raw
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../client'
|
|
5
|
+
|
|
6
|
+
module StakworkCli
|
|
7
|
+
module Commands
|
|
8
|
+
class Skills < Thor
|
|
9
|
+
class_option :token, desc: 'API token (overrides config/env)'
|
|
10
|
+
class_option :host, desc: 'API host (overrides config/env)'
|
|
11
|
+
class_option :pretty, type: :boolean, default: false, desc: 'Pretty-print JSON output'
|
|
12
|
+
|
|
13
|
+
desc 'list', 'List available skills'
|
|
14
|
+
option :search, desc: 'Filter by search query'
|
|
15
|
+
option :page, type: :numeric, desc: 'Page number'
|
|
16
|
+
def list
|
|
17
|
+
params = {}
|
|
18
|
+
params[:search] = options[:search] if options[:search]
|
|
19
|
+
params[:page] = options[:page] if options[:page]
|
|
20
|
+
result = client.get('/api/v1/skills', params: params)
|
|
21
|
+
print_output(result)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'examples ID', 'Show input/output examples for a skill'
|
|
25
|
+
def examples(id)
|
|
26
|
+
result = client.get("/api/v1/skills/#{id}/examples")
|
|
27
|
+
print_output(result)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def client
|
|
33
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
34
|
+
Client.new(token: cfg[:token], base_url: cfg[:host])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def print_output(raw)
|
|
38
|
+
if options[:pretty]
|
|
39
|
+
puts JSON.pretty_generate(JSON.parse(raw))
|
|
40
|
+
else
|
|
41
|
+
puts raw
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../client'
|
|
5
|
+
|
|
6
|
+
module StakworkCli
|
|
7
|
+
module Commands
|
|
8
|
+
class Workflows < Thor
|
|
9
|
+
class_option :token, desc: 'API token (overrides config/env)'
|
|
10
|
+
class_option :host, desc: 'API host (overrides config/env)'
|
|
11
|
+
class_option :pretty, type: :boolean, default: false, desc: 'Pretty-print JSON output'
|
|
12
|
+
|
|
13
|
+
desc 'list', 'List workflows'
|
|
14
|
+
option :page, type: :numeric, default: 1, desc: 'Page number'
|
|
15
|
+
def list
|
|
16
|
+
result = client.get('/api/v1/workflows', params: { page: options[:page] })
|
|
17
|
+
print_output(result)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc 'search QUERY', 'Search workflows by name'
|
|
21
|
+
def search(query)
|
|
22
|
+
result = client.get('/api/v1/workflows/search', params: { search: query })
|
|
23
|
+
print_output(result)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc 'show ID', 'Show a workflow by ID'
|
|
27
|
+
def show(id)
|
|
28
|
+
result = client.get("/api/v1/workflows/#{id}")
|
|
29
|
+
print_output(result)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc 'create', 'Create a new workflow'
|
|
33
|
+
option :workflow, required: true, desc: 'JSON workflow definition'
|
|
34
|
+
option :'child-workflows', desc: 'JSON array of child workflow definitions'
|
|
35
|
+
def create
|
|
36
|
+
begin
|
|
37
|
+
workflow = JSON.parse(options[:workflow])
|
|
38
|
+
rescue JSON::ParserError => e
|
|
39
|
+
$stderr.puts "Error: --workflow is not valid JSON: #{e.message}"
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
body = { workflow: workflow }
|
|
44
|
+
|
|
45
|
+
if options[:'child-workflows']
|
|
46
|
+
begin
|
|
47
|
+
body[:child_workflows] = JSON.parse(options[:'child-workflows'])
|
|
48
|
+
rescue JSON::ParserError => e
|
|
49
|
+
$stderr.puts "Error: --child-workflows is not valid JSON: #{e.message}"
|
|
50
|
+
exit 1
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
result = client.post('/api/v1/workflows', body: body)
|
|
55
|
+
print_output(result)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
desc 'publish ID', 'Publish a workflow'
|
|
59
|
+
def publish(id)
|
|
60
|
+
result = client.post("/api/v1/workflows/#{id}/publish")
|
|
61
|
+
print_output(result)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc 'run ID', 'Trigger a workflow run'
|
|
65
|
+
option :name, required: true, desc: 'Name for this run'
|
|
66
|
+
option :params, default: '{}', desc: 'JSON workflow params'
|
|
67
|
+
def trigger(id)
|
|
68
|
+
begin
|
|
69
|
+
workflow_params = JSON.parse(options[:params])
|
|
70
|
+
rescue JSON::ParserError => e
|
|
71
|
+
$stderr.puts "Error: --params is not valid JSON: #{e.message}"
|
|
72
|
+
exit 1
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
body = {
|
|
76
|
+
project: {
|
|
77
|
+
workflow_id: id.to_i,
|
|
78
|
+
name: options[:name],
|
|
79
|
+
workflow_params: workflow_params
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
result = client.post('/api/v1/projects', body: body)
|
|
83
|
+
print_output(result)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
map 'run' => :trigger
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def client
|
|
91
|
+
cfg = Config.load(token: options[:token], host: options[:host])
|
|
92
|
+
Client.new(token: cfg[:token], base_url: cfg[:host])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def print_output(raw)
|
|
96
|
+
if options[:pretty]
|
|
97
|
+
puts JSON.pretty_generate(JSON.parse(raw))
|
|
98
|
+
else
|
|
99
|
+
puts raw
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module StakworkCli
|
|
5
|
+
module Config
|
|
6
|
+
CONFIG_DIR = File.expand_path('~/.stakwork')
|
|
7
|
+
CONFIG_PATH = File.join(CONFIG_DIR, 'config.yml')
|
|
8
|
+
DEFAULT_HOST = 'https://api.stakwork.com'
|
|
9
|
+
|
|
10
|
+
# Resolves token and host in precedence order:
|
|
11
|
+
# 1. Inline argument
|
|
12
|
+
# 2. Environment variable
|
|
13
|
+
# 3. ~/.stakwork/config.yml
|
|
14
|
+
# 4. Default host
|
|
15
|
+
def self.load(token: nil, host: nil)
|
|
16
|
+
file_config = load_file
|
|
17
|
+
|
|
18
|
+
resolved_token = token ||
|
|
19
|
+
ENV['STAKWORK_TOKEN'] ||
|
|
20
|
+
file_config['token']
|
|
21
|
+
|
|
22
|
+
resolved_host = host ||
|
|
23
|
+
ENV['STAKWORK_HOST'] ||
|
|
24
|
+
file_config['host'] ||
|
|
25
|
+
DEFAULT_HOST
|
|
26
|
+
|
|
27
|
+
{ token: resolved_token, host: resolved_host }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.save(token:, host:)
|
|
31
|
+
FileUtils.mkdir_p(CONFIG_DIR)
|
|
32
|
+
File.write(CONFIG_PATH, { 'token' => token, 'host' => host }.to_yaml)
|
|
33
|
+
File.chmod(0o600, CONFIG_PATH)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.load_file
|
|
37
|
+
return {} unless File.exist?(CONFIG_PATH)
|
|
38
|
+
YAML.safe_load(File.read(CONFIG_PATH)) || {}
|
|
39
|
+
rescue Psych::SyntaxError
|
|
40
|
+
{}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/stakwork_cli.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require_relative 'stakwork_cli/version'
|
|
3
|
+
require_relative 'stakwork_cli/config'
|
|
4
|
+
require_relative 'stakwork_cli/client'
|
|
5
|
+
require_relative 'stakwork_cli/commands/auth'
|
|
6
|
+
require_relative 'stakwork_cli/commands/workflows'
|
|
7
|
+
require_relative 'stakwork_cli/commands/projects'
|
|
8
|
+
require_relative 'stakwork_cli/commands/prompts'
|
|
9
|
+
require_relative 'stakwork_cli/commands/skills'
|
|
10
|
+
require_relative 'stakwork_cli/commands/customers'
|
|
11
|
+
|
|
12
|
+
module StakworkCli
|
|
13
|
+
class CLI < Thor
|
|
14
|
+
desc 'login', 'Save API credentials to ~/.stakwork/config.yml'
|
|
15
|
+
option :token, required: true, desc: 'API token'
|
|
16
|
+
option :host, default: Config::DEFAULT_HOST, desc: 'API host URL'
|
|
17
|
+
def login
|
|
18
|
+
Commands::Auth.new.invoke(:login, [], options.to_h.transform_keys(&:to_sym))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc 'whoami', 'Show current configured credentials'
|
|
22
|
+
option :token, desc: 'Inline token override'
|
|
23
|
+
option :host, desc: 'Inline host override'
|
|
24
|
+
option :pretty, type: :boolean, default: false, desc: 'Pretty-print output'
|
|
25
|
+
def whoami
|
|
26
|
+
Commands::Auth.new.invoke(:whoami, [], options.to_h.transform_keys(&:to_sym))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc 'workflows SUBCOMMAND', 'Manage workflows'
|
|
30
|
+
subcommand 'workflows', Commands::Workflows
|
|
31
|
+
|
|
32
|
+
desc 'projects SUBCOMMAND', 'Manage project runs'
|
|
33
|
+
subcommand 'projects', Commands::Projects
|
|
34
|
+
|
|
35
|
+
desc 'prompts SUBCOMMAND', 'Manage prompts'
|
|
36
|
+
subcommand 'prompts', Commands::Prompts
|
|
37
|
+
|
|
38
|
+
desc 'skills SUBCOMMAND', 'Browse skills'
|
|
39
|
+
subcommand 'skills', Commands::Skills
|
|
40
|
+
|
|
41
|
+
desc 'customers SUBCOMMAND', 'Manage customers'
|
|
42
|
+
subcommand 'customers', Commands::Customers
|
|
43
|
+
|
|
44
|
+
def self.exit_on_failure?
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: stakwork-cli
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Stakwork
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: thor
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.4'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.4'
|
|
27
|
+
description: A command-line interface for interacting with the Stakwork REST API.
|
|
28
|
+
Manage workflows, projects, prompts, and skills from the terminal.
|
|
29
|
+
email:
|
|
30
|
+
executables:
|
|
31
|
+
- stakwork
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- bin/stakwork
|
|
36
|
+
- lib/stakwork_cli.rb
|
|
37
|
+
- lib/stakwork_cli/client.rb
|
|
38
|
+
- lib/stakwork_cli/commands/auth.rb
|
|
39
|
+
- lib/stakwork_cli/commands/customers.rb
|
|
40
|
+
- lib/stakwork_cli/commands/projects.rb
|
|
41
|
+
- lib/stakwork_cli/commands/prompts.rb
|
|
42
|
+
- lib/stakwork_cli/commands/skills.rb
|
|
43
|
+
- lib/stakwork_cli/commands/workflows.rb
|
|
44
|
+
- lib/stakwork_cli/config.rb
|
|
45
|
+
- lib/stakwork_cli/version.rb
|
|
46
|
+
homepage:
|
|
47
|
+
licenses: []
|
|
48
|
+
metadata: {}
|
|
49
|
+
post_install_message:
|
|
50
|
+
rdoc_options: []
|
|
51
|
+
require_paths:
|
|
52
|
+
- lib
|
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '2.7'
|
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
requirements: []
|
|
64
|
+
rubygems_version: 3.2.33
|
|
65
|
+
signing_key:
|
|
66
|
+
specification_version: 4
|
|
67
|
+
summary: CLI for the Stakwork API
|
|
68
|
+
test_files: []
|