restless-sdk 0.1.0 → 0.1.1
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 +4 -4
- data/CONFORMANCE.md +5 -5
- data/LICENSE +18 -0
- data/README.md +19 -5
- data/exe/restless-conformance +1 -1
- data/install.md +4 -3
- data/lib/restless/capture.rb +9 -4
- data/lib/restless/conformance.rb +18 -1
- data/lib/restless/injection.rb +31 -16
- data/lib/restless/rack.rb +15 -3
- data/lib/restless/settings.rb +1 -1
- data/lib/restless/version.rb +2 -2
- data/spec/VECTORS_VERSION +1 -1
- data/spec/vectors/debug-injection.json +219 -0
- data/spec/vectors/fingerprint.json +1 -1
- data/spec/vectors/har.json +1 -1
- data/spec/vectors/mask.json +1 -1
- data/spec/vectors/recovery-slug.json +1 -1
- data/spec/vectors/redact.json +1 -1
- data/spec/vectors/request-id.json +1 -1
- metadata +8 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a346b73df699fa4b309117f63c42f284a971d392fff96ac0321510c9b5c06327
|
|
4
|
+
data.tar.gz: a57d20173e1a974bc5fde6b9c7dd6c20e4239f4f7c70cd572863f97628d9ddfc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 34762d9a43d3294b0448c75a683a158d5ce241d88961177bfbcc215969bf9b4a6f6e0f362cfdb31973e8d05de827a8c56d243ab224132b0f3f3a96c4f7ba0c1b
|
|
7
|
+
data.tar.gz: bbc49f5ffc1a24e563a4cc609cc6f3a95d43b3f3d4090c78826b83c68cdb4ec5d4aab36cfd7b795ce033ed2e5796d56f0a89c3196d40b49b6227affa0ee13487
|
data/CONFORMANCE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
| | |
|
|
4
4
|
|---|---|
|
|
5
|
-
| **Spec version** | 1.0.
|
|
5
|
+
| **Spec version** | 1.0.1 |
|
|
6
6
|
| **Level** | L2 (core + batching, caches, injection, safety) |
|
|
7
7
|
| **Reference** | `restlesshq/node` (`@restlessai/sdk`) |
|
|
8
8
|
| **Driver** | `ruby exe/restless-conformance` |
|
|
@@ -12,7 +12,7 @@ Declared in `lib/restless/version.rb` (META-001).
|
|
|
12
12
|
## Verifying
|
|
13
13
|
|
|
14
14
|
The harness and vectors live in the reference SDK, so the commands below
|
|
15
|
-
assume it is checked out as a sibling (`../node
|
|
15
|
+
assume it is checked out as a sibling (`../node`), which is how
|
|
16
16
|
`setup.sh` in the install repo arranges things. The vectors in `spec/` here
|
|
17
17
|
are a pinned copy, so `ruby -Ilib -Itest test/all.rb` alone works without it.
|
|
18
18
|
|
|
@@ -22,11 +22,11 @@ are a pinned copy, so `ruby -Ilib -Itest test/all.rb` alone works without it.
|
|
|
22
22
|
ruby -Ilib -Itest test/all.rb
|
|
23
23
|
|
|
24
24
|
# the shared cross-language harness
|
|
25
|
-
node ../node
|
|
25
|
+
node ../node/spec/harness/run-vectors.mjs -- ruby exe/restless-conformance
|
|
26
26
|
|
|
27
27
|
# differential fuzz against the reference implementation
|
|
28
|
-
node ../node
|
|
29
|
-
--ref "node ../node
|
|
28
|
+
node ../node/spec/harness/fuzz.mjs \
|
|
29
|
+
--ref "node ../node/spec/driver/.build/node.js" \
|
|
30
30
|
--test "ruby exe/restless-conformance" \
|
|
31
31
|
--iterations 8000 --seed 24301
|
|
32
32
|
```
|
data/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Copyright © 2026 Restless
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the “Software”), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, OUT OF OR IN CONNECTION WITH THE
|
|
18
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/restless-init-dark.svg">
|
|
3
|
+
<img width="100%" src="docs/restless-init.svg" alt="Restless">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
6
|
+
Run in your codebase to get started:
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npx restless init
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This scans your project, figures out your framework, generates an OpenAPI spec, and automatically wires the SDK into your server.
|
|
13
|
+
|
|
1
14
|
# restless-sdk
|
|
2
15
|
|
|
3
16
|
Capture your API traffic and send it to [Restless](https://restless.ai).
|
|
@@ -5,7 +18,9 @@ Capture your API traffic and send it to [Restless](https://restless.ai).
|
|
|
5
18
|
One Rack middleware covers **Rails**, **Sinatra**, **Hanami**, **Grape**,
|
|
6
19
|
**Roda** and anything else that speaks Rack. Ruby 2.6+. No dependencies.
|
|
7
20
|
|
|
8
|
-
##
|
|
21
|
+
## Manual installation
|
|
22
|
+
|
|
23
|
+
If you'd rather wire it up by hand:
|
|
9
24
|
|
|
10
25
|
```sh
|
|
11
26
|
gem install restless-sdk
|
|
@@ -68,8 +83,8 @@ and body your app produced rather than an inner layer's.
|
|
|
68
83
|
and friends are redacted before anything leaves your process. Bodies with
|
|
69
84
|
nothing to redact are passed through byte for byte, so your payloads are
|
|
70
85
|
never re-serialized on the way out.
|
|
71
|
-
- **Error triage.**
|
|
72
|
-
and a `debug` block in the JSON body. If someone attaches a "next steps"
|
|
86
|
+
- **Error triage.** Every response gets `x-log-url` and `x-debug` headers,
|
|
87
|
+
and 4xx/5xx responses also get a `debug` block in the JSON body. If someone attaches a "next steps"
|
|
73
88
|
message to an error in the dashboard, the SDK injects it as
|
|
74
89
|
`debug.recovery` - read synchronously from an in-process cache, never
|
|
75
90
|
blocking the response on a network call. A 5xx that groups by its throw
|
|
@@ -144,5 +159,4 @@ differential fuzzer run against the reference implementation. See
|
|
|
144
159
|
[CONFORMANCE.md](./CONFORMANCE.md).
|
|
145
160
|
|
|
146
161
|
## License
|
|
147
|
-
|
|
148
|
-
ISC
|
|
162
|
+
MIT
|
data/exe/restless-conformance
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# The Ruby conformance driver. See
|
|
4
|
+
# The Ruby conformance driver. See sdk/node/spec/driver/PROTOCOL.md.
|
|
5
5
|
#
|
|
6
6
|
# Dev-only: it is a thin shell over Restless::Conformance, which
|
|
7
7
|
# test/test_vectors.rb also uses, so the vectors cannot describe behaviour the
|
data/install.md
CHANGED
|
@@ -191,7 +191,7 @@ api_key: CLIENT.mask(request.header("Authorization") || "anonymous")
|
|
|
191
191
|
|
|
192
192
|
## 7. `.restless/settings.json`
|
|
193
193
|
|
|
194
|
-
Read at startup, walking up from the working directory. Created and owned by the `
|
|
194
|
+
Read at startup, walking up from the working directory. Created and owned by the `restless` CLI (`npx restless init`). Every Restless SDK reads the same file with the same camelCase keys, so a polyglot repo needs only one.
|
|
195
195
|
|
|
196
196
|
```json
|
|
197
197
|
{
|
|
@@ -247,7 +247,8 @@ Bodies are capped at **256 KiB** (UTF-8 bytes) and truncated with `[...TRUNCATED
|
|
|
247
247
|
|
|
248
248
|
- Request IDs are v4 UUIDs, never time-based, so they leak no ordering.
|
|
249
249
|
- Every response gets `x-restless-id`. `x-request-id` is set only if the caller did not send one, and an incoming value is never reused as ours.
|
|
250
|
-
- On status
|
|
250
|
+
- On **every** status the SDK adds `x-log-url` and `x-debug` headers; on status **>= 400** it also merges a `debug` key into a JSON body. There is no user-configurable hook for this.
|
|
251
|
+
- `x-log-url` points at your project's public docs host, which the server tells the SDK on each upload. Until the first upload round-trips it is omitted rather than guessed: a URL that 404s is worse than no URL. The ingest host is never used for it.
|
|
251
252
|
|
|
252
253
|
## 10. Blocking
|
|
253
254
|
|
|
@@ -295,5 +296,5 @@ Batching is fixed: 10 requests per batch, a 5000 ms flush interval, a 1000-entry
|
|
|
295
296
|
1. `restless-sdk` appears in the `Gemfile`, and `bundle list` finds it.
|
|
296
297
|
2. `CLIENT.rack` is mounted in `config.ru` or `config/application.rb`, as far out as possible.
|
|
297
298
|
3. A `CLIENT.setup` block exists and reads its header via `request.header(...)`.
|
|
298
|
-
4. `.restless/settings.json` exists (created by `npx
|
|
299
|
+
4. `.restless/settings.json` exists (created by `npx restless init`).
|
|
299
300
|
5. Starting the server and curling any endpoint returns an `x-restless-id` response header.
|
data/lib/restless/capture.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Restless
|
|
|
25
25
|
@redact = redact || {}
|
|
26
26
|
@enrich_cache = EnrichCache.new
|
|
27
27
|
@recovery_cache = RecoveryCache.new
|
|
28
|
-
@
|
|
28
|
+
@portal_url = nil
|
|
29
29
|
@docs_mutex = Mutex.new
|
|
30
30
|
@callback = nil
|
|
31
31
|
@uploader = Uploader.new(
|
|
@@ -43,8 +43,11 @@ module Restless
|
|
|
43
43
|
|
|
44
44
|
# INJECT-006. The latest server-resolved docs origin, or nil when no batch
|
|
45
45
|
# has round-tripped yet.
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
# INJECT-006. The server-published portal origin every injected URL is
|
|
47
|
+
# built on. Nil before the first upload round-trip, and then nothing is
|
|
48
|
+
# emitted rather than a guess.
|
|
49
|
+
def portal_url
|
|
50
|
+
@docs_mutex.synchronize { @portal_url }
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
def flush
|
|
@@ -61,7 +64,9 @@ module Restless
|
|
|
61
64
|
docs = body["docsUrl"]
|
|
62
65
|
if docs.is_a?(String) && !docs.empty?
|
|
63
66
|
# Origin only; strip trailing slashes so the server can be lax.
|
|
64
|
-
|
|
67
|
+
# The wire key stays `docsUrl`: every already-deployed SDK reads it,
|
|
68
|
+
# so renaming would strand them all with no portal origin (WIRE-023).
|
|
69
|
+
@docs_mutex.synchronize { @portal_url = docs.sub(%r{/+\z}, "") }
|
|
65
70
|
end
|
|
66
71
|
|
|
67
72
|
messages = body["recoveryMessages"].is_a?(Hash) ? body["recoveryMessages"] : {}
|
data/lib/restless/conformance.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Restless
|
|
|
16
16
|
# through this one file, so it is impossible for the vectors to describe
|
|
17
17
|
# behaviour the driver does not exhibit.
|
|
18
18
|
#
|
|
19
|
-
# See
|
|
19
|
+
# See sdk/node/spec/driver/PROTOCOL.md. Internal: never part of the public
|
|
20
20
|
# API, never shipped behaviour a customer depends on.
|
|
21
21
|
module Conformance
|
|
22
22
|
# An input this implementation's language cannot represent or parse. The
|
|
@@ -79,6 +79,8 @@ module Restless
|
|
|
79
79
|
# --- injection (section 10) ---
|
|
80
80
|
when "recoverySlug"
|
|
81
81
|
Injection.recovery_slug(str_or_nil(input["method"]), str_or_nil(input["path"]))
|
|
82
|
+
when "debugInjection"
|
|
83
|
+
debug_injection(input)
|
|
82
84
|
|
|
83
85
|
# --- HAR (section 7) ---
|
|
84
86
|
when "harEntry"
|
|
@@ -89,6 +91,21 @@ module Restless
|
|
|
89
91
|
end
|
|
90
92
|
end
|
|
91
93
|
|
|
94
|
+
# The observable surface only: the headers, and the `debug` object the
|
|
95
|
+
# adapter would merge (nil when there is nothing to merge).
|
|
96
|
+
def debug_injection(input)
|
|
97
|
+
built = Injection.build(
|
|
98
|
+
status: input["status"].to_i,
|
|
99
|
+
request_id: str(input["requestId"]),
|
|
100
|
+
prefix: str_or_nil(input["prefix"]),
|
|
101
|
+
recovery: str_or_nil(input["recovery"]),
|
|
102
|
+
method: str_or_nil(input["method"]),
|
|
103
|
+
path: str_or_nil(input["path"]),
|
|
104
|
+
portal_url: str_or_nil(input["portalUrl"])
|
|
105
|
+
)
|
|
106
|
+
{ "headers" => built[:headers], "debug" => built[:debug] }
|
|
107
|
+
end
|
|
108
|
+
|
|
92
109
|
def fingerprint(input)
|
|
93
110
|
stack = stack_text(input["stackTrace"])
|
|
94
111
|
if !stack.empty? && v8_dialect?(stack)
|
data/lib/restless/injection.rb
CHANGED
|
@@ -31,19 +31,37 @@ module Restless
|
|
|
31
31
|
flat.empty? ? m : "#{m}-#{flat}"
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# INJECT-002. The debug response headers, which ship on every status.
|
|
35
|
+
#
|
|
36
|
+
# `x-log-url` is omitted with no portal origin; `x-debug` carries no URL,
|
|
37
|
+
# so it always ships.
|
|
38
|
+
def headers(request_id:, prefix: nil, portal_url: nil)
|
|
39
|
+
display = RequestId.format_request_id(request_id, prefix)
|
|
40
|
+
out = { "x-debug" => "npx api debug #{display}" }
|
|
41
|
+
out["x-log-url"] = "#{portal_url}/logs/#{request_id}" unless portal_url.nil? || portal_url.empty?
|
|
42
|
+
out
|
|
43
|
+
end
|
|
44
|
+
|
|
34
45
|
# INJECT-001..004, INJECT-006. Returns the headers to set plus the `debug`
|
|
35
|
-
# object to merge into a JSON body
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
# object to merge into a JSON body (nil when there is nothing to merge).
|
|
47
|
+
#
|
|
48
|
+
# `portal_url` is the project's public portal origin, published by the
|
|
49
|
+
# server. It is NOT the ingest base URL, which serves `/v1/*` and would
|
|
50
|
+
# 404 both paths, and there is deliberately no fallback to it: with no
|
|
51
|
+
# portal origin we emit `x-debug` alone. A caller cannot tell a broken URL
|
|
52
|
+
# from a missing one, and one fetched 404 teaches an agent to stop
|
|
53
|
+
# following the link.
|
|
54
|
+
def build(status:, request_id:, prefix: nil, recovery: nil,
|
|
55
|
+
method: nil, path: nil, portal_url: nil)
|
|
56
|
+
hdrs = headers(request_id: request_id, prefix: prefix, portal_url: portal_url)
|
|
39
57
|
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
log_url = "
|
|
46
|
-
debug_cmd = "
|
|
58
|
+
# INJECT-001. The body object is 4xx/5xx only: a successful body is the
|
|
59
|
+
# caller's data, not ours to reshape. With no portal origin there is no
|
|
60
|
+
# URL to put in one either (INJECT-006).
|
|
61
|
+
return { headers: hdrs, debug: nil } if status < 400 || portal_url.nil? || portal_url.empty?
|
|
62
|
+
|
|
63
|
+
log_url = hdrs["x-log-url"]
|
|
64
|
+
debug_cmd = hdrs["x-debug"]
|
|
47
65
|
|
|
48
66
|
# Per-request "dig-in" URL the calling agent (often an AI) can fetch for
|
|
49
67
|
# concrete next steps. Deliberately LEGIBLE: it ends in `<slug>.md` so it
|
|
@@ -52,7 +70,7 @@ module Restless
|
|
|
52
70
|
# can correlate the follow-up without any new tracking token.
|
|
53
71
|
slug = recovery_slug(method, path)
|
|
54
72
|
dig_in = "For the accepted parameters and next steps, " \
|
|
55
|
-
"fetch #{
|
|
73
|
+
"fetch #{portal_url}/p/#{request_id}/#{slug}.md"
|
|
56
74
|
# INJECT-004: a cached recovery message precedes the dig-in line,
|
|
57
75
|
# separated by a blank line.
|
|
58
76
|
recovery_text =
|
|
@@ -63,10 +81,7 @@ module Restless
|
|
|
63
81
|
end
|
|
64
82
|
|
|
65
83
|
{
|
|
66
|
-
headers:
|
|
67
|
-
"x-log-url" => log_url, # INJECT-002
|
|
68
|
-
"x-debug" => debug_cmd
|
|
69
|
-
},
|
|
84
|
+
headers: hdrs,
|
|
70
85
|
debug: {
|
|
71
86
|
"log" => log_url,
|
|
72
87
|
"cli" => debug_cmd,
|
data/lib/restless/rack.rb
CHANGED
|
@@ -242,6 +242,18 @@ module Restless
|
|
|
242
242
|
options[:request_id_prefix], options[:has_api_key]
|
|
243
243
|
))
|
|
244
244
|
|
|
245
|
+
# INJECT-002. The headers ship on every status; only the body work below
|
|
246
|
+
# is 4xx/5xx, and fingerprinting a success would be wasted.
|
|
247
|
+
if inject && status.to_i < 400
|
|
248
|
+
headers.merge!(safely({}) do
|
|
249
|
+
Injection.headers(
|
|
250
|
+
request_id: our_id,
|
|
251
|
+
prefix: options[:request_id_prefix],
|
|
252
|
+
portal_url: @engine.portal_url
|
|
253
|
+
)
|
|
254
|
+
end)
|
|
255
|
+
end
|
|
256
|
+
|
|
245
257
|
if inject && status.to_i >= 400
|
|
246
258
|
# INJECT-009: the fingerprint is computed against the customer's RAW
|
|
247
259
|
# response, snapshotted before any injected header or body field.
|
|
@@ -257,18 +269,18 @@ module Restless
|
|
|
257
269
|
Injection.build(
|
|
258
270
|
status: status.to_i,
|
|
259
271
|
request_id: our_id,
|
|
260
|
-
base_url: options[:base_url],
|
|
261
272
|
prefix: options[:request_id_prefix],
|
|
262
273
|
recovery: recovery,
|
|
263
274
|
method: env["REQUEST_METHOD"],
|
|
264
275
|
path: captured["routePattern"],
|
|
265
|
-
|
|
276
|
+
portal_url: @engine.portal_url
|
|
266
277
|
)
|
|
267
278
|
end
|
|
268
279
|
|
|
269
280
|
if injection
|
|
270
281
|
headers.merge!(injection[:headers]) # INJECT-002
|
|
271
|
-
rewritten =
|
|
282
|
+
rewritten = injection[:debug] &&
|
|
283
|
+
Injection.apply_body(body_text, headers["content-type"],
|
|
272
284
|
injection[:debug]) # INJECT-003
|
|
273
285
|
if rewritten && !rewritten.equal?(body_text) && rewritten != body_text
|
|
274
286
|
body = [rewritten]
|
data/lib/restless/settings.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "json"
|
|
|
4
4
|
|
|
5
5
|
module Restless
|
|
6
6
|
# CONTRACT.md section 12. `.restless/settings.json` is created and owned by
|
|
7
|
-
# the `
|
|
7
|
+
# the `restless` CLI (`npx restless init`); the SDK consumes exactly two fields of it
|
|
8
8
|
# at runtime (CONFIG-015).
|
|
9
9
|
module Settings
|
|
10
10
|
class ConfigError < StandardError; end
|
data/lib/restless/version.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Restless
|
|
|
6
6
|
# META-001: the spec version an SDK implements must be recorded in a
|
|
7
7
|
# machine-readable form alongside its conformance level.
|
|
8
8
|
|
|
9
|
-
VERSION = "0.1.
|
|
9
|
+
VERSION = "0.1.1"
|
|
10
10
|
|
|
11
11
|
# WIRE-016: distinct per implementation, so the ingest can attribute a
|
|
12
12
|
# payload to a language.
|
|
@@ -17,7 +17,7 @@ module Restless
|
|
|
17
17
|
# REDACT-010 requires full Unicode name folding
|
|
18
18
|
# to require full Unicode lowercase. Keep this in step with
|
|
19
19
|
# spec/VECTORS_VERSION.
|
|
20
|
-
SPEC_VERSION = "1.0.
|
|
20
|
+
SPEC_VERSION = "1.0.1"
|
|
21
21
|
|
|
22
22
|
# CONTRACT.md 1.1: L1 is the pure functions, L2 adds batching, caches,
|
|
23
23
|
# injection and the safety guarantees.
|
data/spec/VECTORS_VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.1
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$generated": "GENERATED FILE - do not edit by hand. Inputs live in spec/cases.ts; run `npm run spec:vectors` to regenerate.",
|
|
3
|
+
"$contract": "spec/CONTRACT.md",
|
|
4
|
+
"specVersion": "1.0.1",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "inject/2xx-headers-only",
|
|
8
|
+
"requirement": "INJECT-001",
|
|
9
|
+
"op": "debugInjection",
|
|
10
|
+
"input": {
|
|
11
|
+
"status": 200,
|
|
12
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
13
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
14
|
+
"method": "GET",
|
|
15
|
+
"path": "/car/{id}"
|
|
16
|
+
},
|
|
17
|
+
"expected": {
|
|
18
|
+
"headers": {
|
|
19
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
20
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
21
|
+
},
|
|
22
|
+
"debug": null
|
|
23
|
+
},
|
|
24
|
+
"note": "Headers on every status; the body object stays 4xx/5xx only."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "inject/2xx-301",
|
|
28
|
+
"requirement": "INJECT-001",
|
|
29
|
+
"op": "debugInjection",
|
|
30
|
+
"input": {
|
|
31
|
+
"status": 301,
|
|
32
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
33
|
+
"portalUrl": "https://acme.restlessdocs.com"
|
|
34
|
+
},
|
|
35
|
+
"expected": {
|
|
36
|
+
"headers": {
|
|
37
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
38
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
39
|
+
},
|
|
40
|
+
"debug": null
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "inject/4xx-full",
|
|
45
|
+
"requirement": "INJECT-002",
|
|
46
|
+
"op": "debugInjection",
|
|
47
|
+
"input": {
|
|
48
|
+
"status": 404,
|
|
49
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
50
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
51
|
+
"method": "GET",
|
|
52
|
+
"path": "/car/{id}"
|
|
53
|
+
},
|
|
54
|
+
"expected": {
|
|
55
|
+
"headers": {
|
|
56
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
57
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
58
|
+
},
|
|
59
|
+
"debug": {
|
|
60
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
61
|
+
"cli": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
62
|
+
"recovery": "For the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/get-car-id.md"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"note": "Both URLs share the portal origin."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "inject/5xx-full",
|
|
69
|
+
"requirement": "INJECT-002",
|
|
70
|
+
"op": "debugInjection",
|
|
71
|
+
"input": {
|
|
72
|
+
"status": 500,
|
|
73
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
74
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
75
|
+
"method": "POST",
|
|
76
|
+
"path": "/orders"
|
|
77
|
+
},
|
|
78
|
+
"expected": {
|
|
79
|
+
"headers": {
|
|
80
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
81
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
82
|
+
},
|
|
83
|
+
"debug": {
|
|
84
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
85
|
+
"cli": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
86
|
+
"recovery": "For the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/post-orders.md"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "inject/no-portal-2xx",
|
|
92
|
+
"requirement": "INJECT-006",
|
|
93
|
+
"op": "debugInjection",
|
|
94
|
+
"input": {
|
|
95
|
+
"status": 200,
|
|
96
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
97
|
+
},
|
|
98
|
+
"expected": {
|
|
99
|
+
"headers": {
|
|
100
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
101
|
+
},
|
|
102
|
+
"debug": null
|
|
103
|
+
},
|
|
104
|
+
"note": "No portal origin: x-debug only, and never a URL on the ingest host."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "inject/no-portal-4xx",
|
|
108
|
+
"requirement": "INJECT-006",
|
|
109
|
+
"op": "debugInjection",
|
|
110
|
+
"input": {
|
|
111
|
+
"status": 404,
|
|
112
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
113
|
+
"method": "GET",
|
|
114
|
+
"path": "/car/{id}"
|
|
115
|
+
},
|
|
116
|
+
"expected": {
|
|
117
|
+
"headers": {
|
|
118
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
119
|
+
},
|
|
120
|
+
"debug": null
|
|
121
|
+
},
|
|
122
|
+
"note": "No portal origin means no debug object at all, so no dig-in line."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "inject/prefix",
|
|
126
|
+
"requirement": "INJECT-002",
|
|
127
|
+
"op": "debugInjection",
|
|
128
|
+
"input": {
|
|
129
|
+
"status": 404,
|
|
130
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
131
|
+
"prefix": "TST",
|
|
132
|
+
"portalUrl": "https://acme.restlessdocs.com"
|
|
133
|
+
},
|
|
134
|
+
"expected": {
|
|
135
|
+
"headers": {
|
|
136
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
137
|
+
"x-debug": "npx api debug TST-9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
138
|
+
},
|
|
139
|
+
"debug": {
|
|
140
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
141
|
+
"cli": "npx api debug TST-9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
142
|
+
"recovery": "For the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/unknown.md"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"note": "x-log-url carries the RAW id; only the CLI hint is prefixed."
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"id": "inject/authored-recovery",
|
|
149
|
+
"requirement": "INJECT-004",
|
|
150
|
+
"op": "debugInjection",
|
|
151
|
+
"input": {
|
|
152
|
+
"status": 402,
|
|
153
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
154
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
155
|
+
"recovery": "Try another card.",
|
|
156
|
+
"method": "POST",
|
|
157
|
+
"path": "/charge"
|
|
158
|
+
},
|
|
159
|
+
"expected": {
|
|
160
|
+
"headers": {
|
|
161
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
162
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
163
|
+
},
|
|
164
|
+
"debug": {
|
|
165
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
166
|
+
"cli": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
167
|
+
"recovery": "Try another card.\n\nFor the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/post-charge.md"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"note": "The authored hint precedes the dig-in line, separated by a blank line."
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "inject/unknown-slug",
|
|
174
|
+
"requirement": "INJECT-005",
|
|
175
|
+
"op": "debugInjection",
|
|
176
|
+
"input": {
|
|
177
|
+
"status": 404,
|
|
178
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
179
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
180
|
+
"method": "GET"
|
|
181
|
+
},
|
|
182
|
+
"expected": {
|
|
183
|
+
"headers": {
|
|
184
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
185
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
186
|
+
},
|
|
187
|
+
"debug": {
|
|
188
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
189
|
+
"cli": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
190
|
+
"recovery": "For the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/unknown.md"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"id": "inject/trailing-slash-origin",
|
|
196
|
+
"requirement": "WIRE-023",
|
|
197
|
+
"op": "debugInjection",
|
|
198
|
+
"input": {
|
|
199
|
+
"status": 404,
|
|
200
|
+
"requestId": "9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
201
|
+
"portalUrl": "https://acme.restlessdocs.com",
|
|
202
|
+
"method": "GET",
|
|
203
|
+
"path": "/x"
|
|
204
|
+
},
|
|
205
|
+
"expected": {
|
|
206
|
+
"headers": {
|
|
207
|
+
"x-log-url": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
208
|
+
"x-debug": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f"
|
|
209
|
+
},
|
|
210
|
+
"debug": {
|
|
211
|
+
"log": "https://acme.restlessdocs.com/logs/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
212
|
+
"cli": "npx api debug 9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f",
|
|
213
|
+
"recovery": "For the accepted parameters and next steps, fetch https://acme.restlessdocs.com/p/9f18a0e2-1c3d-4b5a-8e7f-0a1b2c3d4e5f/get-x.md"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"note": "The origin is stripped of trailing slashes on ingest (WIRE-023), so the SDK appends directly."
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$generated": "GENERATED FILE - do not edit by hand. Inputs live in spec/cases.ts; run `npm run spec:vectors` to regenerate.",
|
|
3
3
|
"$contract": "spec/CONTRACT.md",
|
|
4
|
-
"specVersion": "1.0.
|
|
4
|
+
"specVersion": "1.0.1",
|
|
5
5
|
"cases": [
|
|
6
6
|
{
|
|
7
7
|
"id": "fp/404-parameterized",
|
data/spec/vectors/har.json
CHANGED
data/spec/vectors/mask.json
CHANGED
data/spec/vectors/redact.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$generated": "GENERATED FILE - do not edit by hand. Inputs live in spec/cases.ts; run `npm run spec:vectors` to regenerate.",
|
|
3
3
|
"$contract": "spec/CONTRACT.md",
|
|
4
|
-
"specVersion": "1.0.
|
|
4
|
+
"specVersion": "1.0.1",
|
|
5
5
|
"cases": [
|
|
6
6
|
{
|
|
7
7
|
"id": "reqid/format-with-prefix",
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: restless-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Restless
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: |
|
|
14
13
|
Rack middleware that captures API traffic, redacts secrets before anything
|
|
@@ -16,14 +15,14 @@ description: |
|
|
|
16
15
|
recovered from, and uploads the result to Restless. Works with Rails,
|
|
17
16
|
Sinatra, Hanami, Grape, Roda and anything else that speaks Rack.
|
|
18
17
|
|
|
19
|
-
Implements version 1.0.
|
|
18
|
+
Implements version 1.0.1 of the Restless SDK Contract at
|
|
20
19
|
level L2.
|
|
21
|
-
email:
|
|
22
20
|
executables: []
|
|
23
21
|
extensions: []
|
|
24
22
|
extra_rdoc_files: []
|
|
25
23
|
files:
|
|
26
24
|
- CONFORMANCE.md
|
|
25
|
+
- LICENSE
|
|
27
26
|
- README.md
|
|
28
27
|
- exe/restless-conformance
|
|
29
28
|
- install.md
|
|
@@ -46,6 +45,7 @@ files:
|
|
|
46
45
|
- lib/restless/uploader.rb
|
|
47
46
|
- lib/restless/version.rb
|
|
48
47
|
- spec/VECTORS_VERSION
|
|
48
|
+
- spec/vectors/debug-injection.json
|
|
49
49
|
- spec/vectors/fingerprint.json
|
|
50
50
|
- spec/vectors/har.json
|
|
51
51
|
- spec/vectors/mask.json
|
|
@@ -54,13 +54,12 @@ files:
|
|
|
54
54
|
- spec/vectors/request-id.json
|
|
55
55
|
homepage: https://restless.ai
|
|
56
56
|
licenses:
|
|
57
|
-
-
|
|
57
|
+
- MIT
|
|
58
58
|
metadata:
|
|
59
59
|
source_code_uri: https://github.com/restlesshq/ruby
|
|
60
60
|
bug_tracker_uri: https://github.com/restlesshq/ruby/issues
|
|
61
|
-
restless_spec_version: 1.0.
|
|
61
|
+
restless_spec_version: 1.0.1
|
|
62
62
|
restless_conformance_level: L2
|
|
63
|
-
post_install_message:
|
|
64
63
|
rdoc_options: []
|
|
65
64
|
require_paths:
|
|
66
65
|
- lib
|
|
@@ -75,8 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
74
|
- !ruby/object:Gem::Version
|
|
76
75
|
version: '0'
|
|
77
76
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
79
|
-
signing_key:
|
|
77
|
+
rubygems_version: 4.0.16
|
|
80
78
|
specification_version: 4
|
|
81
79
|
summary: Capture your API traffic and send it to Restless.
|
|
82
80
|
test_files: []
|