зеркало из https://github.com/mozilla/mig.git
Really working on that formatting
This commit is contained in:
Родитель
19b904edbc
Коммит
88453e0275
26
doc/api.rst
26
doc/api.rst
|
@ -64,19 +64,19 @@ POST /api/v1/heartbeat
|
|||
- `pid`: An integer representing the agent's process ID
|
||||
- `queueLoc`: The name of the queue the agent is reading from and writing to
|
||||
- `startTime:` The time at which the agent sent the heartbeat formatted like "2009-11-10T23:00:00Z"
|
||||
- `environment`:
|
||||
- `init`: A string containing the name of the host's init system, such as systemd
|
||||
- `ident`: A string containing the name of the host's OS identifier
|
||||
- `os`: A string containing the name of the host's OS family (linux, darwin, windows, ...)
|
||||
- `arch`: A string containing the host's architecture, such as "x86_64"
|
||||
- `proxied`: A boolean that is true if the agent uses a proxy
|
||||
- `proxy`: A string containing the address of the proxy used by the agent if any
|
||||
- `addresses`: An array of strings containing IP addresses associated with network interfaces on the host
|
||||
- `publicIP`: A string containing the IP address of the agent's host from the API's perspective
|
||||
- `modules`: An array of strings containing names of modules enabled by the agent
|
||||
- `tags`:
|
||||
- `name`: A string name for the tag
|
||||
- `value`: A string value for the tag
|
||||
- `environment`: An object
|
||||
- `environment.init`: A string containing the name of the host's init system, such as systemd
|
||||
- `environment.ident`: A string containing the name of the host's OS identifier
|
||||
- `environment.os`: A string containing the name of the host's OS family (linux, darwin, windows, ...)
|
||||
- `environment.arch`: A string containing the host's architecture, such as "x86_64"
|
||||
- `environment.proxied`: A boolean that is true if the agent uses a proxy
|
||||
- `environment.proxy`: A string containing the address of the proxy used by the agent if any
|
||||
- `environment.addresses`: An array of strings containing IP addresses associated with network interfaces on the host
|
||||
- `environment.publicIP`: A string containing the IP address of the agent's host from the API's perspective
|
||||
- `environment.modules`: An array of strings containing names of modules enabled by the agent
|
||||
- `tags`: An array of objects
|
||||
- `tags[i].name`: A string name for the tag
|
||||
- `tags[i].value`: A string value for the tag
|
||||
* Response Code:
|
||||
- `200`: The heartbeat was accepted and recorded successfully
|
||||
- `400`: The body of the request was incorrectly formatted or missing data
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
# Client Daemon API Types
|
||||
|
||||
*Note*: In the case that a type is suffixed by `?`, meanining that it
|
||||
can be `undefined`, or `type | null`, the value can be left out. This
|
||||
notation is used to describe configuration parameters that are optional
|
||||
and have defaults.
|
||||
|
||||
In this example, `value` and `other` can be left out, so the following are
|
||||
all valid values of type `Example`.
|
||||
|
||||
```typescript
|
||||
type Example = {
|
||||
value: string | null,
|
||||
other: number?,
|
||||
required: string
|
||||
}
|
||||
|
||||
const ex1: Example = {
|
||||
value: "test",
|
||||
other: 32,
|
||||
required: "no"
|
||||
}
|
||||
|
||||
const ex2: Example = {
|
||||
value: null,
|
||||
required: "hello"
|
||||
}
|
||||
|
||||
const ex3: Example = {
|
||||
required: "test"
|
||||
}
|
||||
```
|
||||
|
||||
## API Types
|
||||
|
||||
```typescript
|
||||
type Parameter = {
|
||||
name: string,
|
||||
type: string,
|
||||
description: string,
|
||||
example: any,
|
||||
}
|
||||
|
||||
type Endpoint = {
|
||||
method: string,
|
||||
route: string,
|
||||
parameters: Array<Parameter>,
|
||||
response: Array<Parameter>,
|
||||
statusCodes: {
|
||||
[code: number]: string,
|
||||
}
|
||||
}
|
||||
|
||||
type RawSQL = string
|
||||
|
||||
type RegExp = string
|
||||
|
||||
// Multiple target queries can be specified and will be joined with `AND`.
|
||||
type Target = Array<TargetQuery>
|
||||
|
||||
type TargetQuery
|
||||
= TargetWithSQL
|
||||
| TargetAll
|
||||
| TargetByAgentDetails
|
||||
| TargetByHostDetails
|
||||
| TargetByTag
|
||||
|
||||
type Module
|
||||
= AgentDestroyModule
|
||||
| AuditModule
|
||||
| DispatchModule
|
||||
| PingModule
|
||||
| ScribeModule
|
||||
| TimeDriftModule
|
||||
| PkgModule
|
||||
| FileModule
|
||||
| FSWatchModule
|
||||
| MemoryModule
|
||||
| NetStatModule
|
||||
| SSHKeyModule
|
||||
| YaraModule
|
||||
|
||||
// Contains whatever result information is produced by agents that have run an action.
|
||||
type Result = {
|
||||
errors: Array<string>,
|
||||
foundanything: boolean,
|
||||
success: boolean,
|
||||
elements: Array<object>,
|
||||
statistics: Array<object>
|
||||
}
|
||||
|
||||
// This type is a fallback for investigators to write the arbitrary SQL queries they may have used
|
||||
// for advanced targeting.
|
||||
type TargetWithSQL = {
|
||||
sql: RawSQL
|
||||
}
|
||||
|
||||
// Regardless of whether `all` is `true` or `false`, this type specifies all online agents as targets.
|
||||
type TargetAll = {
|
||||
all: bool
|
||||
}
|
||||
|
||||
// Enables targeting agents by details specific to a given agent.
|
||||
// At least one field must be present for this target query to be considered valid.
|
||||
type TargetByAgentDetails = {
|
||||
id: number?,
|
||||
name: string?,
|
||||
queueLocation: string?,
|
||||
version: string?,
|
||||
pid: number?,
|
||||
status: string?
|
||||
}
|
||||
|
||||
// Enables targeting agents by details specific to the agent's host environment.
|
||||
// At least one field must be present for this target query to be considered valid.
|
||||
// See the [cheatsheet](https://github.com/mozilla/mig/blob/master/doc/cheatsheet.rst#environments)
|
||||
// for more information about this.
|
||||
type TargetByHostDetails = {
|
||||
ident: string?,
|
||||
os: string?,
|
||||
arch: string?,
|
||||
publicIP: string?
|
||||
}
|
||||
|
||||
// Enables targeting agents by one of their tags.
|
||||
type TargetByTag = {
|
||||
tagName: string,
|
||||
value: string
|
||||
}
|
||||
|
||||
type AgentDestroyModule = {
|
||||
pid: number,
|
||||
version: string
|
||||
}
|
||||
|
||||
// No parameters
|
||||
type AuditModule = {
|
||||
}
|
||||
|
||||
// No parameters
|
||||
type DispatchModule = {
|
||||
}
|
||||
|
||||
type PingModule = {
|
||||
destination: string,
|
||||
protocol: 'tcp' | 'udp' | 'icmp',
|
||||
destinationPort: number?,
|
||||
count: number?,
|
||||
timeout: number?
|
||||
}
|
||||
|
||||
type ScribeModule = {
|
||||
path: string,
|
||||
onlyTrueDocTests: bool?,
|
||||
humanReadableOutput: bool?,
|
||||
jsonOutput: bool?
|
||||
}
|
||||
|
||||
type TimeDriftModule = {
|
||||
drift: int?
|
||||
}
|
||||
|
||||
type PkgModule = {
|
||||
packageName: RegExp,
|
||||
packageVersion: string | null
|
||||
}
|
||||
|
||||
type FileModule = {
|
||||
options: null | {
|
||||
maxDepth: number?,
|
||||
matchAll: bool?,
|
||||
matchAny: bool?,
|
||||
matchEntireFile: bool?,
|
||||
mismatchingContent: string?,
|
||||
limit: number?,
|
||||
includeFileSha256: bool?,
|
||||
decompressFiles: bool?,
|
||||
maxErrors: number?
|
||||
},
|
||||
search: {
|
||||
path: string,
|
||||
name: string?,
|
||||
content: string?,
|
||||
minSizeBytes: number?,
|
||||
maxSizeBytes: number?,
|
||||
modifiedSinceMinutes: number?,
|
||||
modifiedAfterMinutes: number?,
|
||||
mode: string?,
|
||||
md5: string?,
|
||||
sha1: string?,
|
||||
sha2: string?,
|
||||
sha3: string?,
|
||||
}
|
||||
}
|
||||
|
||||
// No parameters
|
||||
type FSWatchModule = {
|
||||
}
|
||||
|
||||
type MemoryModule = {
|
||||
options: null | {
|
||||
offset: number?,
|
||||
maxLength: number?,
|
||||
logFailures: bool?,
|
||||
matchAll: bool?
|
||||
},
|
||||
search: {
|
||||
names: []string,
|
||||
libraries: []string,
|
||||
bytes: []string,
|
||||
contents: []string
|
||||
}
|
||||
}
|
||||
|
||||
type NetStatModule = {
|
||||
localMACAddress: string?,
|
||||
neighborMACAddress: string?,
|
||||
localIPAddress: string?,
|
||||
neighborIPAddress: string?,
|
||||
remoteConnectedIPAddress: string?,
|
||||
listeningPort: number?,
|
||||
resolveNamespaces: bool?
|
||||
}
|
||||
|
||||
type SSHKeyModule = {
|
||||
path: string,
|
||||
maxDepth: number?
|
||||
}
|
||||
|
||||
type YaraModule = {
|
||||
yaraRules: string,
|
||||
filePaths: []string
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Target
|
||||
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
tagName: "operator",
|
||||
value: "IT"
|
||||
},
|
||||
{
|
||||
os: "linux"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
os: "linux"
|
||||
}
|
||||
{
|
||||
name: "buildbot"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
sql: "id IN (SELECT agentid FROM commands, json_array_elements(commands.results) AS r WHERE commands.actionid = 12345 AND r#>>'{foundanything}' = 'true')"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
all: true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Module
|
||||
|
||||
Ping module
|
||||
|
||||
```typescript
|
||||
{
|
||||
destination: "127.0.0.1",
|
||||
destinationPort: 8080,
|
||||
protocol: "tcp"
|
||||
}
|
||||
```
|
||||
|
||||
File module
|
||||
|
||||
```typescript
|
||||
{
|
||||
options: {
|
||||
maxDepth: 1,
|
||||
limit: 100
|
||||
},
|
||||
search: {
|
||||
path: "/etc/passwd",
|
||||
modifiedSinceMinutes: 2880
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
NetStat module
|
||||
|
||||
```typescript
|
||||
{
|
||||
remoteConnectedIPAddress: "1.2.3.4"
|
||||
}
|
||||
```
|
|
@ -0,0 +1,313 @@
|
|||
# Client Daemon API
|
||||
|
||||
## Conventions and Notation
|
||||
|
||||
1. All endpoints using the `GET` and `DELETE` methods accept input in URL parameters.
|
||||
2. All other endpoints accept parameters encoded as JSON in the body of the request.
|
||||
3. Data types are expressed using TypeScript's notation for [basic](https://www.typescriptlang.org/docs/handbook/basic-types.html) and [composite](https://www.typescriptlang.org/docs/handbook/advanced-types.html) types.
|
||||
4. We use `Option<type>` as a synonym for `type | null` because the latter disrupts the Markdown formatting of tables.
|
||||
5. Complex data types like `Target` and `Module` may be defined in [doc/client/api-types.md](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api-types.md).
|
||||
6. URLs may contain positional parameter for identifiers, which are always strings. E.g. in `/actions/:id/status`, `:id` is a positional parameter.
|
||||
|
||||
|
||||
## Endpoint Table of Contents
|
||||
|
||||
* [Retrieve a signable token for authentication](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#retrieve-a-signable-token-for-authentication)
|
||||
* [Provide a signed authentication token](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#provide-a-signed-authentication-token)
|
||||
* [Retrieve top-level API documentation](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#endpoint-documentation)
|
||||
* [Retrieve documentation for a module](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#retrieve-documentation-for-a-module)
|
||||
* [Create an action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#create-an-action)
|
||||
* [Retrieve an action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#retrieve-an-action)
|
||||
* [Retrieve an action for signing](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#retrieve-an-action-for-signing)
|
||||
* [Provide a signature for an action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#provide-a-signture-for-an-action)
|
||||
* [Dispatch an action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#dispatch-an-action)
|
||||
* [Retrieve results for an action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#retrieve-results-for-an-action)
|
||||
* [Check the status of a dispatched action](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api.md#check-the-status-of-a-dispatched-action)
|
||||
|
||||
## Endpoint Documentation
|
||||
|
||||
|
||||
### Retrieve a signable token for authentication
|
||||
|
||||
```
|
||||
GET /v1/authentication/pgp
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| challenge | string | The base of an authentication token for which a detached signature should be computed | No example |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that a challenge was successfully generated.
|
||||
|
||||
### Provide a signed authentication token
|
||||
|
||||
```
|
||||
POST /v1/authentication/pgp
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| challenge | string | The base of an authentication token | No example |
|
||||
| signature | string | The detached signature over the challenge encoded as base64 | No example |
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Optional<string>` | If the signature or challenge is improperly formatted, an error will be returned | null |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the challenge and signature have been accepted.
|
||||
* `400` indicates that either the challenge or the signature are improperly formatted.
|
||||
|
||||
### Retrieve top-level API documentation
|
||||
|
||||
```
|
||||
GET /v1/documentation
|
||||
```
|
||||
|
||||
Retrieve a JSON document describing all of the endpoints supported by the API.
|
||||
|
||||
#### Parameters
|
||||
|
||||
None
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| currentVersion | string | The most recent version of the API. | "v2" |
|
||||
| versions | `[]{ version: string, endpoints: []Endpoint }` | An array of objects containing descriptions of endpoints exposed by each version of the API | See below |
|
||||
|
||||
##### Example Response
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the request was handled successfully.
|
||||
|
||||
### Retrieve documentation for a module
|
||||
|
||||
```
|
||||
GET /v1/module/:name/documentation
|
||||
```
|
||||
|
||||
Retrieve a JSON document describing the configuration accepted by a module
|
||||
and what it does.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The `name` positional parameter in the URL must be the name of the module to retrieve
|
||||
documentation for as a string. E.g. `pkg`, `file`, etc.
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| module | string | The name of the module | "pkg" |
|
||||
| version | string | The semantic version of the mdoule | "v1.2.3" |
|
||||
| description | string | An explanation of what the module does | "Find packages on the host" |
|
||||
| configuration | Module | An object describing the configuration parameters | See below |
|
||||
|
||||
##### Example Response
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the response was served successfully.
|
||||
* `400` indicates that the module requested does not exist.
|
||||
|
||||
### Create an action
|
||||
|
||||
```
|
||||
POST /v1/actions/create
|
||||
```
|
||||
|
||||
This endpoint can be invoked to create an action.
|
||||
The action will not be dispatched to the MIG API to be executed by agents right away.
|
||||
Instead, the action will be retained by the daemon, and its ID will be returned.
|
||||
This allows investigators to review and modify actions before dispatching them.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| module | string | The name of the module the action should invoke. | "pkg" |
|
||||
| expireAfter | number | The number of seconds after which the action should expire. | 300 |
|
||||
| target | Target | A description of the agents to have run the action. | [Target examples](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api-types.md#target) |
|
||||
| moduleConfig | Module | An object providing configuration values for the module specified. | [Module examples](https://github.com/mozilla/mig/blob/client-daemon/doc/client/api-types.md#module) |
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Option<string>` | If the action cannot be created, an error will be returned. | "Invalid module configuration." |
|
||||
| action | string | An identifier for the newly-created action. | "abc123...def" |
|
||||
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the action was created successfully.
|
||||
* `400` indicates that some data provided as a parameter was incorrectly formatted or otherwise invalid.
|
||||
|
||||
#### Example Request
|
||||
|
||||
#### Example Response
|
||||
|
||||
### Retrieve an action
|
||||
|
||||
```
|
||||
GET /v1/actions/:id
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
The `id` positional argument should be the ID of an action, as returned by
|
||||
the "create an action" endpoint.
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Optional<string>` | If the action does not exist, an error will be returned | null |
|
||||
| action | `Optional<object>` | An action structure encoded as JSON | See below |
|
||||
|
||||
[Action structure](https://github.com/mozilla/mig/blob/master/action.go#L30)
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the action was retrieved successfully
|
||||
* `400` indicates that the action does not exist
|
||||
|
||||
### Retrieve an action for signing
|
||||
|
||||
```
|
||||
GET /v1/actions/:id/signing
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
The `id` positional argument should be the ID of an action, as returned by the "create an action" endpoint.
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Option<string>` | If the action requested does not exist, an error will be returned. | null |
|
||||
| action | string | An action formatted as a string that can be signed by an investigator. | No example |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the ID provided was valid and a signable action has been retrieved.
|
||||
* `400` indicates that the ID provided was invalid.
|
||||
|
||||
### Provide a signature for an action
|
||||
|
||||
```
|
||||
PUT /v1/actions/:id/sign
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
The `id` positional argument is expected to be a string identifier for an action created by the client daemon.
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| signature | string | A base64-encoded signature of the action provided | No example |
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Option<string>` | If the signature is not valid base64 or the action does not exist, an error will be returned | "Signature is not valid base64" |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the ID corresponds to a valid action and that the signature provided has been appended to it.
|
||||
* `400` indicates that either the signature provided is invalid or that the ID does not correspond to a valid action.
|
||||
|
||||
### Dispatch an action
|
||||
|
||||
```
|
||||
PUT /v1/actions/:id/dispatch
|
||||
```
|
||||
|
||||
After an action has been created, this endpoint can be invoked to dispatch that action to the MIG API.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The positional argument `id` is expected to be the identifier of an action managed by the client daemon.
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Option<string>` | If the action cannot be dispatched, an error will be returned. | "Action has expired." |
|
||||
| status | string | The new status of the action. | "dispatched" |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that the action was dispatched to the MIG API successfully.
|
||||
* `400` indicates that the action identifier provided was determined to be invalid.
|
||||
* `500` indicates that the action could not be dispatched due to an internal failure.
|
||||
|
||||
#### Example Request
|
||||
|
||||
#### Example Response
|
||||
|
||||
### Retrieve results for an action
|
||||
|
||||
```
|
||||
GET /v1/results
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| action | string | The identifier of an action being managed by the daemon | abc123 |
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Optional<string>` | An error message if results cannot be retrieved | "No results" |
|
||||
| results | `[]Result` | An array of results produced by agents | See example response |
|
||||
|
||||
##### Status Codes
|
||||
|
||||
* `200` indicates that results have successfully been retrieved.
|
||||
* `400` indicates that the action specified does not exist.
|
||||
* `500` indicates that communication with the MIG API failed.
|
||||
|
||||
### Check the status of a dispatched action
|
||||
|
||||
```
|
||||
GET /v1/actions/:id/status
|
||||
```
|
||||
|
||||
Retrieve the status of an action.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The only parameter is the `:id` positional parameter in the endpoint URL.
|
||||
|
||||
#### Response
|
||||
|
||||
| Name | Type | Description | Example |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| error | `Option<string>` | If the action's status cannot be retrieved, an error will be returned. | null |
|
||||
| status | string | The status of the action. | "in-progress" |
|
||||
| agentsTargeted | number | The number agents that the action will be scheduled for | 32 |
|
||||
| sent | number | The number of agents to whom the action has been sent | 10 |
|
||||
| done | number | The number of agents that have finished running the action | 3 |
|
||||
| succeeded | number | The number of agents that successfully finished running the action | 2 |
|
Загрузка…
Ссылка в новой задаче