@potential-guide/check-environment 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +213 -2
- package/dist/bundles/directories/resources/check.json +19 -0
- package/dist/bundles/directories/resources/references/en/check.md +18 -0
- package/dist/bundles/directories/resources/references/en/problems/directory-unusable.md +10 -0
- package/dist/bundles/directories/resources/references/en/problems/not-directory.md +7 -0
- package/dist/bundles/directories/schemas/0.json +51 -0
- package/dist/bundles/directories/schemas/1.json +74 -0
- package/dist/bundles/directories/schemas/2.json +4 -0
- package/dist/bundles/directories/task.yaml +22 -0
- package/dist/bundles/directories/worker.cjs +4 -0
- package/dist/bundles/directories.car +0 -0
- package/dist/bundles/network/resources/check.json +21 -0
- package/dist/bundles/network/resources/references/en/check.md +21 -0
- package/dist/bundles/network/resources/references/en/problems/dns-unavailable.md +8 -0
- package/dist/bundles/network/resources/references/en/problems/http-status.md +9 -0
- package/dist/bundles/network/resources/references/en/problems/http-unavailable.md +9 -0
- package/dist/bundles/network/schemas/0.json +76 -0
- package/dist/bundles/network/schemas/1.json +78 -0
- package/dist/bundles/network/schemas/2.json +4 -0
- package/dist/bundles/network/task.yaml +23 -0
- package/dist/bundles/network/worker.cjs +2 -0
- package/dist/bundles/network.car +0 -0
- package/dist/bundles/references.json +17 -0
- package/dist/bundles/tools/resources/check.json +23 -0
- package/dist/bundles/tools/resources/references/en/check.md +21 -0
- package/dist/bundles/tools/resources/references/en/problems/tool-missing.md +8 -0
- package/dist/bundles/tools/resources/references/en/problems/tool-unusable.md +9 -0
- package/dist/bundles/tools/resources/references/en/problems/tool-version.md +8 -0
- package/dist/bundles/tools/resources/references/en/problems/version-unparseable.md +8 -0
- package/dist/bundles/tools/schemas/0.json +50 -0
- package/dist/bundles/tools/schemas/1.json +82 -0
- package/dist/bundles/tools/schemas/2.json +4 -0
- package/dist/bundles/tools/task.yaml +24 -0
- package/dist/bundles/tools/worker.cjs +2 -0
- package/dist/bundles/tools.car +0 -0
- package/dist/command.d.ts +8 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/command.js +105 -0
- package/dist/command.js.map +1 -0
- package/dist/contracts.d.ts +36 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +14 -0
- package/dist/contracts.js.map +1 -0
- package/dist/definitions/directories.d.ts +1 -0
- package/dist/definitions/directories.js +1 -0
- package/dist/definitions/network.d.ts +1 -0
- package/dist/definitions/network.js +1 -0
- package/dist/definitions/tools.d.ts +1 -0
- package/dist/definitions/tools.js +1 -0
- package/dist/directories.d.ts +16 -0
- package/dist/directories.d.ts.map +1 -0
- package/dist/directories.js +65 -0
- package/dist/directories.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +18 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +79 -0
- package/dist/network.js.map +1 -0
- package/dist/result.d.ts +20 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +23 -0
- package/dist/result.js.map +1 -0
- package/dist/tasks/directories/contract.d.ts +21 -0
- package/dist/tasks/directories/contract.d.ts.map +1 -0
- package/dist/tasks/directories/contract.js +11 -0
- package/dist/tasks/directories/contract.js.map +1 -0
- package/dist/tasks/directories/main.d.ts +2 -0
- package/dist/tasks/directories/main.d.ts.map +1 -0
- package/dist/tasks/directories/main.js +7 -0
- package/dist/tasks/directories/main.js.map +1 -0
- package/dist/tasks/inputs.d.ts +6 -0
- package/dist/tasks/inputs.d.ts.map +1 -0
- package/dist/tasks/inputs.js +5 -0
- package/dist/tasks/inputs.js.map +1 -0
- package/dist/tasks/network/contract.d.ts +24 -0
- package/dist/tasks/network/contract.d.ts.map +1 -0
- package/dist/tasks/network/contract.js +25 -0
- package/dist/tasks/network/contract.js.map +1 -0
- package/dist/tasks/network/main.d.ts +2 -0
- package/dist/tasks/network/main.d.ts.map +1 -0
- package/dist/tasks/network/main.js +7 -0
- package/dist/tasks/network/main.js.map +1 -0
- package/dist/tasks/tools/contract.d.ts +23 -0
- package/dist/tasks/tools/contract.d.ts.map +1 -0
- package/dist/tasks/tools/contract.js +13 -0
- package/dist/tasks/tools/contract.js.map +1 -0
- package/dist/tasks/tools/main.d.ts +2 -0
- package/dist/tasks/tools/main.d.ts.map +1 -0
- package/dist/tasks/tools/main.js +7 -0
- package/dist/tasks/tools/main.js.map +1 -0
- package/dist/tools.d.ts +19 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +49 -0
- package/dist/tools.js.map +1 -0
- package/package.json +50 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 shazhou-ww
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,214 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Environment check bundles
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Three reusable checks share a library and build into **three independent Task
|
|
4
|
+
Bundles**: `tools`, `directories`, and `network`. No Objective levels, repair
|
|
5
|
+
agents, package installation or long-running sampling are built into them.
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
pnpm --filter @potential-guide/check-environment... build
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package contains `dist/bundles/<kind>/task.yaml`, the self-contained
|
|
12
|
+
`worker.cjs`, and the distributable `dist/bundles/<kind>.car`.
|
|
13
|
+
`dist/bundles/references.json` records each relative `directory`, `car` and
|
|
14
|
+
standard UnixFS root `cid`. The CAR and directory describe the same file tree
|
|
15
|
+
and root CID; there is no parallel Bundle digest. Build output is ignored by Git.
|
|
16
|
+
|
|
17
|
+
## Standard author pipeline
|
|
18
|
+
|
|
19
|
+
The three `*.check.build.yaml` files are the only author declarations.
|
|
20
|
+
Their problemCodes generate the literal business types, runtime allowed-code
|
|
21
|
+
list, result Schema and Reference coverage matrix through
|
|
22
|
+
[`check-sdk/authoring`](../check-sdk/README.md).
|
|
23
|
+
The first stage snapshots source, generated code definitions and validated
|
|
24
|
+
Reference bytes into `dist/staging/<kind>/`, including a generated
|
|
25
|
+
`task.build.yaml` with explicit `runTask` entry and exact resource includes.
|
|
26
|
+
The second stage uses the ordinary Task Bundle `buildTask` API.
|
|
27
|
+
It reads only those fixed business inputs, not the live source or Reference
|
|
28
|
+
directories. No Check-specific compiler is added.
|
|
29
|
+
|
|
30
|
+
Before acceptance the orchestration compares CAR and directory CIDs and exact
|
|
31
|
+
resource bytes. All three candidates must succeed before `dist/bundles/` and
|
|
32
|
+
its final references.json are replaced together; failures retain accepted
|
|
33
|
+
artifacts and references. Staging and candidate outputs are not release inputs.
|
|
34
|
+
|
|
35
|
+
Each Bundle contains `schemas/`, `resources/check.json`, and only its own
|
|
36
|
+
`resources/references/en/check.md` plus `problems/<code>.md`. Read them through
|
|
37
|
+
`ipfs://<CID>/resources/references/en/check.md` or the resource index. Reference
|
|
38
|
+
frontmatter accepts only title and description (nonempty, description <=200
|
|
39
|
+
Unicode code points). Filename/language/problem coverage and exact bytes are
|
|
40
|
+
gated; Markdown bodies, sections and links are deliberately not validated.
|
|
41
|
+
Language completeness is declared per Check, with no automatic fallback.
|
|
42
|
+
|
|
43
|
+
## Migration and entry points
|
|
44
|
+
|
|
45
|
+
The three Task Bundles are the execution entry points. The retired Check Host
|
|
46
|
+
is not restored, and no old Host command, registration or compatibility forwarding
|
|
47
|
+
is provided. Import/pin the emitted CAR and submit its root CID to the Engine;
|
|
48
|
+
do not submit an npm package name or an old Checker digest as a Bundle.
|
|
49
|
+
|
|
50
|
+
The library exports (`checkTools`, `checkDirectories`, `checkNetwork`, version
|
|
51
|
+
helpers and the directory write probe) remain shared implementations, not legacy
|
|
52
|
+
task entry points. Existing Objective Workers still consume them without child
|
|
53
|
+
Task scheduling. Removing them would break those consumers rather than migrate
|
|
54
|
+
an old runner. This scope does not migrate `pnpm check`, repository/package
|
|
55
|
+
standardization, devbox-health or setup/self-check Objectives.
|
|
56
|
+
|
|
57
|
+
## Inputs
|
|
58
|
+
|
|
59
|
+
Tool versions (numeric dot-separated versions, not npm semver ranges):
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"tools": [
|
|
64
|
+
{"command": "node", "args": ["--version"], "minimumVersion": "22.13.0"}
|
|
65
|
+
],
|
|
66
|
+
"checkTimeoutMs": 5000
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Version output uses the same numeric version parser as devbox-health. Commands
|
|
71
|
+
run without a shell. On Windows, use a native executable or `node` with the
|
|
72
|
+
absolute JavaScript CLI entry in `args`, not `.cmd`/`.bat` shims. Tools remain
|
|
73
|
+
external prerequisites; the bundle does not install them. Tool launch, timeout,
|
|
74
|
+
output-limit and cleanup faults fail the Run rather than manufacture a gap.
|
|
75
|
+
|
|
76
|
+
Directories (absolute paths on the **execution machine**):
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"directories": [
|
|
81
|
+
{"path": "D:\\Code\\demo", "requirements": ["exists", "read"]},
|
|
82
|
+
{"path": "D:\\TaskScratch", "requirements": ["exists", "write"]}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
All entries must be existing directories, not final-component symbolic links.
|
|
88
|
+
`read` checks read/traversal access; `write` creates an exclusive temporary file,
|
|
89
|
+
reads it back and removes it, including on cancellation. No business directory
|
|
90
|
+
is created and no permissions are changed. Ownership and canonical-path
|
|
91
|
+
requirements remain Objective policy, not generic requirements.
|
|
92
|
+
|
|
93
|
+
DNS and HTTP:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"dns": ["localhost"],
|
|
98
|
+
"http": [
|
|
99
|
+
{"url": "http://127.0.0.1:8080/health", "expectedStatus": [200], "followRedirects": false}
|
|
100
|
+
],
|
|
101
|
+
"checkTimeoutMs": 4000
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
At least one DNS or HTTP check is required. Each endpoint has an independent
|
|
106
|
+
timeout. HTTP(S) URLs cannot embed credentials. Redirects are rejected unless
|
|
107
|
+
explicitly enabled. Responses are not downloaded; their bodies are cancelled.
|
|
108
|
+
DNS resolves in a bounded subprocess so cancellation cannot leave a native
|
|
109
|
+
lookup running inside the Worker. Timeout/cancellation is an execution fault,
|
|
110
|
+
not evidence that an endpoint is unavailable.
|
|
111
|
+
|
|
112
|
+
`checkTimeoutMs` is the per-command/endpoint business budget (default 5000ms).
|
|
113
|
+
The Task Request's separate `timeoutMs` controls task eligibility/lifetime and
|
|
114
|
+
is reserved by the Engine; it is not passed to the business callback.
|
|
115
|
+
|
|
116
|
+
Each list is bounded to 100 items. The message contract is TypeBox `Never`
|
|
117
|
+
(equivalent to JSON Schema `false`); the checks do not
|
|
118
|
+
accept business messages. Request/Run cancellation still works through the SDK.
|
|
119
|
+
|
|
120
|
+
## Results and submission
|
|
121
|
+
|
|
122
|
+
A passing check returns exactly:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{"pass": true}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A completed check with unmet requirements returns only actual problems:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"pass": false,
|
|
133
|
+
"problems": [
|
|
134
|
+
{"target": "node", "code": "tool-version", "actual": "20.0.0"}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Passing items are omitted. Each problem includes `target`, `code`, and nullable
|
|
140
|
+
`actual`. Failure results require a nonempty `problems` array; passing results
|
|
141
|
+
have no `problems` field. Neither variant includes `checks` or `ok`. Invocation
|
|
142
|
+
faults still fail the Run, not return a check result.
|
|
143
|
+
|
|
144
|
+
Per-item verbose diagnostics do not belong in Result. The Task SDK supports
|
|
145
|
+
separate bounded Run diagnostics through `context.log`; see
|
|
146
|
+
[Run logs](../../docs/task-run-logs.md). These checks currently emit no diagnostic
|
|
147
|
+
logs and do not repurpose progress or checkpoint as verbose output.
|
|
148
|
+
Console stderr is not a task-history logging channel. Log availability never
|
|
149
|
+
replaces the declared result, failure or physical release evidence.
|
|
150
|
+
|
|
151
|
+
Changing the business result contract changes the Bundle CID. Rebuild and
|
|
152
|
+
use the new reference from `references.json`; existing CID-addressed content
|
|
153
|
+
retain their original contracts.
|
|
154
|
+
|
|
155
|
+
After building, import and pin the emitted CAR using an external tool of your
|
|
156
|
+
choice and submit its root CID. For example, with a caller-managed local Kubo:
|
|
157
|
+
|
|
158
|
+
```powershell
|
|
159
|
+
ipfs dag import --pin-roots=true packages\check-environment\dist\bundles\tools.car
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The Engine must be started, authorized and able to fetch the pinned CID through
|
|
163
|
+
its configured IPFS client. Normal task observation is unchanged:
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
import {readFile} from "node:fs/promises";
|
|
167
|
+
import {join, resolve} from "node:path";
|
|
168
|
+
|
|
169
|
+
const directory = resolve("packages", "check-environment", "dist", "bundles");
|
|
170
|
+
const refs = JSON.parse(await readFile(join(directory, "references.json"), "utf8"));
|
|
171
|
+
await engine.submit({
|
|
172
|
+
requestId,
|
|
173
|
+
bundle: refs.tools.cid,
|
|
174
|
+
tools: [{command: process.execPath, args: ["--version"], minimumVersion: "22.13.0"}],
|
|
175
|
+
timeoutMs: 5000
|
|
176
|
+
}, caller);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Production delivery imports/pins the CAR on the caller-managed node and
|
|
180
|
+
publishes root CID references, optionally with gateway hints. Do not use
|
|
181
|
+
`latest` as executable identity. npm delivers the author/library package;
|
|
182
|
+
the generic Engine does not install it. This change adds local delivery
|
|
183
|
+
verification only, not publication workflows or release authorization.
|
|
184
|
+
|
|
185
|
+
## Regression verification
|
|
186
|
+
|
|
187
|
+
```powershell
|
|
188
|
+
pnpm --filter @potential-guide/check-environment... build
|
|
189
|
+
pnpm --filter @potential-guide/check-environment test
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The Engine regression imports the emitted CARs, verifies equality with their
|
|
193
|
+
directory trees and root CIDs, runs passing and unmet-requirement inputs for all
|
|
194
|
+
three bundles, and waits for physical release. Tool timeout/output overflow,
|
|
195
|
+
invalid directory paths and HTTP timeout must fail the Run without a fabricated
|
|
196
|
+
check result. Cancellation of an active HTTP request must stop and release the
|
|
197
|
+
Run without a result. Library tests additionally verify process cancellation,
|
|
198
|
+
write-probe cleanup, DNS, HTTP redirects and numeric version handling.
|
|
199
|
+
|
|
200
|
+
## Objective integration
|
|
201
|
+
|
|
202
|
+
The same write/readback probe is reused by devbox-health and pguide setup
|
|
203
|
+
(including self-check). Devbox-health also reuses version parsing/comparison.
|
|
204
|
+
Its ownership/canonical-path rules, stage deadlines, command evidence, problem
|
|
205
|
+
IDs and actual compiler/runtime smoke tests remain in the Objective.
|
|
206
|
+
|
|
207
|
+
These existing Objective Workers call shared library code, bundled into their
|
|
208
|
+
own artifacts; they do **not** submit child Tasks to an Engine. The independent
|
|
209
|
+
bundles use the same implementations and are separately exercised through the
|
|
210
|
+
real Engine in tests. Network checks currently have an independent consumer
|
|
211
|
+
surface; devbox-health keeps its original network execution/evidence path.
|
|
212
|
+
|
|
213
|
+
Workers are not security sandboxes. Admit tool commands, filesystem paths and
|
|
214
|
+
network targets only from authorized callers in a trusted execution environment.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "directories",
|
|
3
|
+
"languages": [
|
|
4
|
+
"en"
|
|
5
|
+
],
|
|
6
|
+
"problemCodes": [
|
|
7
|
+
"not-directory",
|
|
8
|
+
"directory-unusable"
|
|
9
|
+
],
|
|
10
|
+
"references": {
|
|
11
|
+
"en": {
|
|
12
|
+
"check": "resources/references/en/check.md",
|
|
13
|
+
"problems": {
|
|
14
|
+
"not-directory": "resources/references/en/problems/not-directory.md",
|
|
15
|
+
"directory-unusable": "resources/references/en/problems/directory-unusable.md"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Directory access
|
|
3
|
+
description: Check absolute directory paths and explicit exists, read or write requirements.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Directory access
|
|
7
|
+
|
|
8
|
+
Input `directories` lists absolute `path` and nonempty `requirements` from
|
|
9
|
+
`exists`, `read`, `write`. Symlinks are not accepted as directories.
|
|
10
|
+
Read uses host access checks. Write creates an exclusive probe file, reads it
|
|
11
|
+
back and deletes it in `finally`; it does not modify existing files.
|
|
12
|
+
The Engine host must have the requested permissions.
|
|
13
|
+
|
|
14
|
+
Results contain only failures: [not-directory](problems/not-directory.md) or
|
|
15
|
+
[directory-unusable](problems/directory-unusable.md). `target` is the absolute
|
|
16
|
+
path; `actual` is the filesystem error code or null. Invalid input, unexpected
|
|
17
|
+
filesystem errors, readback mismatch and cleanup errors remain execution
|
|
18
|
+
faults. Cancellation propagates after probe cleanup. No repair is performed.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Directory unavailable
|
|
3
|
+
description: The directory is missing or requested access failed with a recognized filesystem error.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`target` is the absolute directory path; `actual` is ENOENT, ENOTDIR, EACCES,
|
|
7
|
+
EPERM, EROFS, ENOSPC or EDQUOT. Check path, permissions, filesystem capacity and
|
|
8
|
+
mount state. Resolve the cause separately; no repair is attempted.
|
|
9
|
+
Unexpected errors and cleanup failures are faults, not this problem.
|
|
10
|
+
See [directory checks](../check.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Not a directory
|
|
3
|
+
description: The path exists but is not an ordinary directory or is a symbolic link.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`target` is the requested absolute path; `actual` is null. Select an ordinary
|
|
7
|
+
directory rather than a file or symlink. See [directory checks](../check.md).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"additionalProperties": false,
|
|
3
|
+
"type": "object",
|
|
4
|
+
"required": [
|
|
5
|
+
"directories"
|
|
6
|
+
],
|
|
7
|
+
"properties": {
|
|
8
|
+
"directories": {
|
|
9
|
+
"minItems": 1,
|
|
10
|
+
"maxItems": 100,
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": {
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": [
|
|
16
|
+
"path",
|
|
17
|
+
"requirements"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"path": {
|
|
21
|
+
"minLength": 1,
|
|
22
|
+
"maxLength": 4096,
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
"requirements": {
|
|
26
|
+
"minItems": 1,
|
|
27
|
+
"uniqueItems": true,
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {
|
|
30
|
+
"anyOf": [
|
|
31
|
+
{
|
|
32
|
+
"const": "exists",
|
|
33
|
+
"type": "string"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"const": "read",
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"const": "write",
|
|
41
|
+
"type": "string"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"$id": "https://task.invalid/schema/input"
|
|
51
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"anyOf": [
|
|
3
|
+
{
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"pass"
|
|
8
|
+
],
|
|
9
|
+
"properties": {
|
|
10
|
+
"pass": {
|
|
11
|
+
"const": true,
|
|
12
|
+
"type": "boolean"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": [
|
|
20
|
+
"pass",
|
|
21
|
+
"problems"
|
|
22
|
+
],
|
|
23
|
+
"properties": {
|
|
24
|
+
"pass": {
|
|
25
|
+
"const": false,
|
|
26
|
+
"type": "boolean"
|
|
27
|
+
},
|
|
28
|
+
"problems": {
|
|
29
|
+
"minItems": 1,
|
|
30
|
+
"type": "array",
|
|
31
|
+
"items": {
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": [
|
|
35
|
+
"target",
|
|
36
|
+
"code",
|
|
37
|
+
"actual"
|
|
38
|
+
],
|
|
39
|
+
"properties": {
|
|
40
|
+
"target": {
|
|
41
|
+
"minLength": 1,
|
|
42
|
+
"maxLength": 4096,
|
|
43
|
+
"type": "string"
|
|
44
|
+
},
|
|
45
|
+
"code": {
|
|
46
|
+
"anyOf": [
|
|
47
|
+
{
|
|
48
|
+
"const": "not-directory",
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"const": "directory-unusable",
|
|
53
|
+
"type": "string"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"actual": {
|
|
58
|
+
"anyOf": [
|
|
59
|
+
{
|
|
60
|
+
"type": "string"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "null"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"$id": "https://task.invalid/schema/result"
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
$schema: https://shazhou-ww.github.io/potential-guide/schemas/artifacts/task/v1.json
|
|
2
|
+
name: Environment directories check
|
|
3
|
+
runtime:
|
|
4
|
+
type: node
|
|
5
|
+
minVersion: 22.13.0
|
|
6
|
+
entry: worker.cjs
|
|
7
|
+
inputSchema:
|
|
8
|
+
$ref: schemas/0.json
|
|
9
|
+
resultSchema:
|
|
10
|
+
$ref: schemas/1.json
|
|
11
|
+
messageSchema:
|
|
12
|
+
$ref: schemas/2.json
|
|
13
|
+
include:
|
|
14
|
+
- resources/check.json
|
|
15
|
+
- resources/references/en/check.md
|
|
16
|
+
- resources/references/en/problems/directory-unusable.md
|
|
17
|
+
- resources/references/en/problems/not-directory.md
|
|
18
|
+
- schemas/0.json
|
|
19
|
+
- schemas/1.json
|
|
20
|
+
- schemas/2.json
|
|
21
|
+
- task.yaml
|
|
22
|
+
- worker.cjs
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const __artifactURL = require("node:url").pathToFileURL(__filename).href;
|
|
2
|
+
"use strict";function G(o){if(new Set(o).size!==o.length||o.some(n=>!/^[a-z][a-z0-9-]*$/.test(n)||n.length>4096))throw new Error("Check problem codes must be unique stable names of at most 4096 characters.")}function de(o,n){G(o);let i=new Set(o),r=n.map(a=>{if(!i.has(a.code))throw new Error(`Undeclared Check problem code: ${a.code}`);if(typeof a.target!="string"||!a.target.length||[...a.target].length>4096||a.actual!==null&&typeof a.actual!="string"||Object.keys(a).some(m=>!["target","code","actual"].includes(m)))throw new Error("Invalid Check problem.");return{target:a.target,code:a.code,actual:a.actual}});return r.length?{pass:!1,problems:r}:{pass:!0}}function Q(o,n){return async(i,r)=>{r.signal.throwIfAborted();let a=await n(i,r);return r.signal.throwIfAborted(),de(o,a)}}var F=require("node:worker_threads");var u=class extends Error{code;details;constructor(n,i,r){super(i),this.code=n,this.details=r,this.name="TaskError"}};function b(o,n){throw new u(o,n)}function N(o,n=65536){let i=new Set,r=0;function a(s,k){if((++r>n||k>64)&&b("invalid_payload","JSON structure exceeds limit."),s===null||typeof s=="string"||typeof s=="boolean"||typeof s=="number"&&Number.isFinite(s))return s;(typeof s!="object"||s===null)&&b("invalid_payload","Only finite JSON values are accepted."),i.has(s)&&b("invalid_payload","Cyclic JSON is not accepted.");let E=Array.isArray(s);Object.getPrototypeOf(s)!==(E?Array.prototype:Object.prototype)&&b("invalid_payload","Custom prototypes are not accepted."),i.add(s);let f=Object.getOwnPropertyDescriptors(s),w=Reflect.ownKeys(f),T={},C=[];E&&w.length!==s.length+1&&b("invalid_payload","Sparse or decorated arrays are not accepted.");for(let I of w){typeof I!="string"&&b("invalid_payload","Symbol keys are not accepted.");let x=f[I];(!x||!("value"in x))&&b("invalid_payload","Accessors are not accepted."),!(E&&I==="length")&&(x.enumerable||b("invalid_payload","Hidden properties are not accepted."),E?(I!==String(C.length)&&b("invalid_payload","Invalid array keys."),C.push(a(x.value,k+1))):Object.defineProperty(T,I,{value:a(x.value,k+1),enumerable:!0,writable:!0,configurable:!0}))}return i.delete(s),E?C:T}let m=a(o,0);return new TextEncoder().encode(JSON.stringify(m)).byteLength>n&&b("payload_too_large","JSON payload exceeds byte limit."),m}function A(o){return Array.isArray(o)?`[${o.map(A).join(",")}]`:o&&typeof o=="object"?`{${Object.keys(o).sort().map(n=>`${JSON.stringify(n)}:${A(o[n]??null)}`).join(",")}}`:JSON.stringify(o)}var R=class extends u{result;constructor(n,i,r){super(n,i),this.result=r}};var B="https://shazhou-ww.github.io/potential-guide/schemas/artifacts/task/v1.json";var K={declaration:JSON.parse('{"$schema":"https://shazhou-ww.github.io/potential-guide/schemas/artifacts/task/v1.json","name":"Environment directories check","runtime":{"type":"node","minVersion":"22.13.0"},"entry":"worker.cjs","inputSchema":{"additionalProperties":false,"type":"object","required":["directories"],"properties":{"directories":{"minItems":1,"maxItems":100,"type":"array","items":{"additionalProperties":false,"type":"object","required":["path","requirements"],"properties":{"path":{"minLength":1,"maxLength":4096,"type":"string"},"requirements":{"minItems":1,"uniqueItems":true,"type":"array","items":{"anyOf":[{"const":"exists","type":"string"},{"const":"read","type":"string"},{"const":"write","type":"string"}]}}}}}}},"resultSchema":{"anyOf":[{"additionalProperties":false,"type":"object","required":["pass"],"properties":{"pass":{"const":true,"type":"boolean"}}},{"additionalProperties":false,"type":"object","required":["pass","problems"],"properties":{"pass":{"const":false,"type":"boolean"},"problems":{"minItems":1,"type":"array","items":{"additionalProperties":false,"type":"object","required":["target","code","actual"],"properties":{"target":{"minLength":1,"maxLength":4096,"type":"string"},"code":{"anyOf":[{"const":"not-directory","type":"string"},{"const":"directory-unusable","type":"string"}]},"actual":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}]},"messageSchema":{"not":{}},"include":["resources/check.json","resources/references/en/check.md","resources/references/en/problems/directory-unusable.md","resources/references/en/problems/not-directory.md","schemas/0.json","schemas/1.json","schemas/2.json","task.yaml","worker.cjs"]}')};var W=class{capacity;queue=[];waiter;closed=!1;constructor(n){this.capacity=n}push(n){if(this.closed||this.queue.length>=this.capacity)throw new Error("Task inbox unavailable.");this.waiter?(this.waiter({done:!1,value:n}),this.waiter=void 0):this.queue.push(n)}close(){this.closed=!0,this.queue=[],this.waiter?.({done:!0,value:void 0}),this.waiter=void 0}[Symbol.asyncIterator](){return{next:()=>{let n=this.queue.shift();return n?Promise.resolve({done:!1,value:n}):this.closed?Promise.resolve({done:!0,value:void 0}):this.waiter?Promise.reject(new Error("Task inbox has one consumer.")):new Promise(i=>{this.waiter=i})}}}};function Y(o){if(!K)throw new Error("Task SDK requires a build-locked Bundle binding.");let{declaration:n}=K,i=F.parentPort;if(!i||n.$schema!==B)throw new Error("Unsupported Task Worker.");let r=F.workerData;if(typeof r.generation!="string"||!r.generation||!Number.isSafeInteger(r.maxInFlight)||r.maxInFlight<=0||!Number.isSafeInteger(r.maxPayloadBytes)||r.maxPayloadBytes<=0||typeof r.bundle?.cid!="string"||!r.bundle.cid||r.bundle.cid.length>256)throw new Error("Invalid trusted Task Worker configuration.");if(!r.bundle||A(N(r.bundle.declaration,1048576))!==A(N(n,1048576)))throw new Error("Task Worker declaration differs from verified Bundle.");let a=new AbortController,m=new W(r.maxInFlight),s=new Map,k=new Map,E=0,f,w=!1,T=0,C=!1,I=Promise.resolve(),x,oe=0,q=new Map;function ie(t,p={}){let d=++oe,e=p.signal?AbortSignal.any([a.signal,p.signal]):a.signal,c=()=>{},g=()=>{c(),q.get(d)?.reject(new DOMException("Bundle read canceled.","AbortError")),q.delete(d),i.postMessage({generation:r.generation,runId:f,kind:"bundle-cancel",readId:d})};return new ReadableStream({start(h){let l=()=>{g(),h.error(e.reason)};if(c=()=>e.removeEventListener("abort",l),e.aborted){h.error(e.reason);return}e.addEventListener("abort",l,{once:!0})},async pull(h){try{if(w)throw new u("run_finished","Bundle stream is closed.");e.throwIfAborted();let l=await new Promise((O,ce)=>{q.set(d,{resolve:O,reject:ce}),i.postMessage({generation:r.generation,runId:f,kind:"bundle-pull",readId:d,path:t})});if(l.done)c(),h.close();else if(l.chunk instanceof Uint8Array)h.enqueue(l.chunk);else throw new u("invalid_bundle","Invalid Bundle stream chunk.")}catch(l){g(),h.error(l)}},cancel:g},{highWaterMark:0})}let D=0,S=new Map,P=0,z=0,$=!1,_=!1,X=!1,y;function ae(){if(y||_||w||a.signal.aborted||P<=z)return;let t=++D,p=P,d=setTimeout(()=>{_=!0},1e3);y={id:t,count:p,timer:d};try{i.postMessage({generation:r.generation,runId:f,kind:"log-drop",logId:t,count:p})}catch{clearTimeout(d),y=void 0,_=!0}}function Z(){$||y||_||w||a.signal.aborted||($=!0,queueMicrotask(()=>{$=!1,ae()}))}function v(){if(!(X||P<=z)){X=!0;try{i.postMessage({generation:r.generation,runId:f,kind:"log-drop",logId:++D,count:P,final:!0})}catch{}}}function U(){y&&clearTimeout(y.timer),y=void 0,_=!0;for(let t of S.values())clearTimeout(t.timer),t.resolve({state:"unavailable",reason:"channel"});S.clear()}async function se(t){if(w||a.signal.aborted)return{state:"unavailable",reason:"channel"};if(S.size>=Math.min(r.maxInFlight,16))return P=Math.min(Number.MAX_SAFE_INTEGER,P+1),Z(),{state:"dropped",reason:"capacity"};let p=D+1;return new Promise((d,e)=>{let c=setTimeout(()=>{S.delete(p),d({state:"unavailable",reason:"timeout"})},1e3);S.set(p,{resolve:d,reject:e,timer:c});try{i.postMessage({generation:r.generation,runId:f,kind:"log",logId:p,value:t}),D=p}catch(g){clearTimeout(c),S.delete(p),g instanceof Error&&g.name==="DataCloneError"?e(new u("invalid_payload","Task payload cannot be cloned for IPC.")):d({state:"unavailable",reason:"channel"})}})}function M(t,p){if(k.size>=r.maxInFlight)return Promise.reject(new Error("Task channel capacity exceeded."));let d=E+1,e=new Promise((c,g)=>{k.set(d,{resolve:c,reject:g})});try{i.postMessage({generation:r.generation,runId:f,id:d,kind:t,value:p}),E=d}catch(c){k.get(d)?.reject(c instanceof Error&&c.name==="DataCloneError"?new u("invalid_payload","Task payload cannot be cloned for IPC."):c instanceof Error?c:new Error("Task send failed.")),k.delete(d)}return e}function J(t,p){if(w)return Promise.reject(new u("run_finished","Task is finishing."));if(T>=r.maxInFlight)return Promise.reject(new Error("Task channel capacity exceeded."));T++;let d=I.then(()=>M(t,p)).catch(e=>{throw x??=e,e}).finally(()=>{T--});return I=d,d.catch(()=>{}),d}i.postMessage({generation:r.generation,kind:"ready",value:{$schema:n.$schema,cid:r.bundle.cid,inputSchema:n.inputSchema,resultSchema:n.resultSchema,messageSchema:n.messageSchema}}),i.on("message",t=>{if(!t||t.generation!==r.generation)return;if(t.kind==="bundle-data"){let e=q.get(t.readId);if(!e)return;q.delete(t.readId),t.error?e.reject(new u(t.error.code,t.error.message)):e.resolve(t);return}if(t.kind==="log-drop-ack"){if(t.runId!==f||!y||t.logId!==y.id)return;let e=y;clearTimeout(e.timer),y=void 0,t.error||t.value?.state==="unavailable"?_=!0:(z=e.count,_=!1,Z());return}if(t.kind==="log-ack"){if(t.runId!==f)return;let e=S.get(t.logId);if(!e)return;S.delete(t.logId),clearTimeout(e.timer),t.error?e.reject(new u(t.error.code,t.error.message)):e.resolve(t.value);return}if(t.kind==="ack"){let e=k.get(t.id);if(!e)return;k.delete(t.id),t.error?e.reject(new u(t.error.code,t.error.message)):e.resolve();return}if(t.kind==="ping"){i.postMessage({generation:r.generation,kind:"pong"});return}if(t.runId!==f&&t.kind!=="run")return;if(t.kind==="cancel"){i.postMessage({generation:r.generation,runId:f,kind:"stopping"}),a.abort(),U(),m.close();for(let e of s.values())e.abort();return}if(t.kind==="withdraw"){let e=s.get(t.taskId)??new AbortController;s.set(t.taskId,e),e.abort();return}if(t.kind==="message"){let e=t.value,c=s.get(e.taskId)??new AbortController;s.set(e.taskId,c);let g=!1,h=(l,O)=>g||c.signal.aborted?Promise.reject(new Error("Message unavailable.")):(g=!0,J("message-confirm",{taskId:e.taskId,messageId:e.messageId,state:l,...O===void 0?{}:{reason:O}}));m.push(Object.freeze({messageId:e.messageId,taskId:e.taskId,caller:Object.freeze(e.caller),signal:c.signal,payload:e.payload,accept:()=>h("accepted"),reject:l=>h("rejected",l)}));return}if(t.kind!=="run"||f!==void 0)return;f=t.runId;let p=Object.freeze({runId:t.runId,...t.identity?{generation:t.identity.generation,startedAt:t.identity.startedAt}:{},scope:t.scope,signal:a.signal,messages:m,bundle:Object.freeze({read:ie}),progress:e=>J("progress",e),checkpoint:e=>J("checkpoint",e),log:se}),d=async e=>{let c=e;C||=c instanceof u&&["reclamation_failed","reclamation_unknown"].includes(c.code);let g=c instanceof R?c.result:void 0,h=(l,O)=>({code:l instanceof u?l.code.slice(0,128):"run_failed",message:l instanceof Error?l.message.slice(0,512):"Task callback failed.",...O===void 0?{}:{result:O}});try{await M("failed",h(c,g))}catch(l){await M("failed",h(l))}};(async()=>{try{let e=await o(t.input,p);if(w=!0,v(),await I,x!==void 0)throw x;await M("complete",e)}catch(e){w=!0,v(),await I.catch(()=>{}),C||=e instanceof u&&["reclamation_failed","reclamation_unknown"].includes(e.code);let c=x??e;if(a.signal.aborted&&c instanceof Error&&(c.name==="AbortError"||c instanceof u&&["ABORT_ERR","run_canceled"].includes(c.code)))return;await d(c)}})().then(()=>{m.close(),a.abort(),U();for(let e of s.values())e.abort();i.postMessage({generation:r.generation,runId:f,kind:C?"uncertain":"released"}),i.close()},()=>{m.close(),a.abort(),U();for(let e of s.values())e.abort();i.postMessage({generation:r.generation,runId:f,kind:"uncertain"}),i.close()})})}var V=Object.freeze(["not-directory","directory-unusable"]);var re=require("node:crypto"),H=require("node:fs"),j=require("node:fs/promises"),L=require("node:path");function ee(o){return o.filter(n=>!n.ok).map(({target:n,code:i,actual:r})=>{if(i==="pass")throw new Error("A failing check requires a problem code.");return{target:n,code:i,actual:r}})}function te(o){return o instanceof Error&&"code"in o&&typeof o.code=="string"?o.code:void 0}async function le(o,n){n?.throwIfAborted();let i=(0,L.join)(o,`.pguide-check-${(0,re.randomUUID)()}`),r=!1;try{if(await(0,j.writeFile)(i,`directory-check
|
|
3
|
+
`,{flag:"wx",mode:384}),r=!0,n?.throwIfAborted(),await(0,j.readFile)(i,"utf8")!==`directory-check
|
|
4
|
+
`)throw new Error("Directory write probe readback mismatch.")}finally{r&&await(0,j.rm)(i)}}async function ne(o,n={}){if(!o.directories.length||o.directories.length>100)throw new Error("Specify between 1 and 100 directories.");for(let r of o.directories)if(!(0,L.isAbsolute)(r.path)||r.path.includes("\0")||!r.requirements.length||r.requirements.some(a=>!["exists","read","write"].includes(a)))throw new Error("Directories require absolute paths and explicit access requirements.");let i=[];for(let r of o.directories){n.signal?.throwIfAborted();let a="pass",m=null;try{let s=await(0,j.lstat)(r.path);!s.isDirectory()||s.isSymbolicLink()?a="not-directory":(r.requirements.includes("read")&&await(0,j.access)(r.path,H.constants.R_OK|H.constants.X_OK),r.requirements.includes("write")&&await le(r.path,n.signal))}catch(s){if(n.signal?.throwIfAborted(),m=te(s)??null,m===null||!["ENOENT","ENOTDIR","EACCES","EPERM","EROFS","ENOSPC","EDQUOT"].includes(m))throw s;a="directory-unusable"}i.push({target:r.path,ok:a==="pass",code:a,actual:m})}return ee(i)}var ue=Q(V,ne);Y(ue);
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "network",
|
|
3
|
+
"languages": [
|
|
4
|
+
"en"
|
|
5
|
+
],
|
|
6
|
+
"problemCodes": [
|
|
7
|
+
"dns-unavailable",
|
|
8
|
+
"http-status",
|
|
9
|
+
"http-unavailable"
|
|
10
|
+
],
|
|
11
|
+
"references": {
|
|
12
|
+
"en": {
|
|
13
|
+
"check": "resources/references/en/check.md",
|
|
14
|
+
"problems": {
|
|
15
|
+
"dns-unavailable": "resources/references/en/problems/dns-unavailable.md",
|
|
16
|
+
"http-status": "resources/references/en/problems/http-status.md",
|
|
17
|
+
"http-unavailable": "resources/references/en/problems/http-unavailable.md"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Network reachability
|
|
3
|
+
description: Check explicit DNS names and HTTP status expectations with bounded resource lifetimes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Network reachability
|
|
7
|
+
|
|
8
|
+
Input contains `dns` names and `http` entries with `url`, `expectedStatus`,
|
|
9
|
+
`followRedirects`; at least one check is required. Optional `checkTimeoutMs`
|
|
10
|
+
defaults to 5000 (100–300000). The host requires DNS/network access and Node
|
|
11
|
+
subprocess support. HTTP(S) URLs cannot contain credentials.
|
|
12
|
+
DNS runs in a bounded subprocess; HTTP response bodies are cancelled.
|
|
13
|
+
|
|
14
|
+
Problems: [DNS unavailable](problems/dns-unavailable.md),
|
|
15
|
+
[unexpected HTTP status](problems/http-status.md),
|
|
16
|
+
[HTTP unavailable](problems/http-unavailable.md).
|
|
17
|
+
Results contain only failures; `target` is the DNS name or URL, `actual` is
|
|
18
|
+
the DNS error/address, status string or fetch diagnostic.
|
|
19
|
+
Timeouts, invalid inputs, unexpected subprocess faults and cleanup failures
|
|
20
|
+
remain execution faults. Cancellation propagates and physical resources are
|
|
21
|
+
released. This is a point-in-time probe, not a network repair or guarantee.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: DNS unavailable
|
|
3
|
+
description: DNS lookup failed with a recognized availability error.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`target` is the DNS name; `actual` is ENOTFOUND, EAI_AGAIN, EAI_FAIL or ETIMEOUT.
|
|
7
|
+
Check spelling, host DNS configuration and connectivity. Subprocess deadlines
|
|
8
|
+
and unexpected resolver errors remain faults. See [network checks](../check.md).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Unexpected HTTP status
|
|
3
|
+
description: The HTTP response did not satisfy status and redirect expectations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`target` is the requested URL; `actual` is the decimal response status.
|
|
7
|
+
Check expectedStatus and followRedirects, endpoint health and authentication.
|
|
8
|
+
A redirect fails when following redirects is disabled, even when listed as an
|
|
9
|
+
expected status. See [network checks](../check.md).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: HTTP unavailable
|
|
3
|
+
description: Fetch failed with a network TypeError rather than a timeout or cancellation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`target` is the URL; `actual` is the fetch diagnostic string. Check host
|
|
7
|
+
connectivity, TLS trust and endpoint availability without treating the diagnostic
|
|
8
|
+
as a stable machine code. Timeout and cancellation remain faults/cancellation.
|
|
9
|
+
See [network checks](../check.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"additionalProperties": false,
|
|
3
|
+
"anyOf": [
|
|
4
|
+
{
|
|
5
|
+
"properties": {
|
|
6
|
+
"dns": {
|
|
7
|
+
"type": "array",
|
|
8
|
+
"minItems": 1
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"properties": {
|
|
14
|
+
"http": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": [
|
|
23
|
+
"dns",
|
|
24
|
+
"http"
|
|
25
|
+
],
|
|
26
|
+
"properties": {
|
|
27
|
+
"checkTimeoutMs": {
|
|
28
|
+
"minimum": 100,
|
|
29
|
+
"maximum": 300000,
|
|
30
|
+
"type": "integer"
|
|
31
|
+
},
|
|
32
|
+
"dns": {
|
|
33
|
+
"maxItems": 100,
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"maxLength": 253,
|
|
37
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*$",
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"http": {
|
|
42
|
+
"maxItems": 100,
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": {
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": [
|
|
48
|
+
"url",
|
|
49
|
+
"expectedStatus",
|
|
50
|
+
"followRedirects"
|
|
51
|
+
],
|
|
52
|
+
"properties": {
|
|
53
|
+
"url": {
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"maxLength": 4096,
|
|
56
|
+
"type": "string"
|
|
57
|
+
},
|
|
58
|
+
"expectedStatus": {
|
|
59
|
+
"minItems": 1,
|
|
60
|
+
"uniqueItems": true,
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": {
|
|
63
|
+
"minimum": 100,
|
|
64
|
+
"maximum": 599,
|
|
65
|
+
"type": "integer"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"followRedirects": {
|
|
69
|
+
"type": "boolean"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"$id": "https://task.invalid/schema/input"
|
|
76
|
+
}
|