@gakr-gakr/lobster 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/README.md +74 -0
- package/SKILL.md +97 -0
- package/autobot.plugin.json +21 -0
- package/index.ts +24 -0
- package/package.json +38 -0
- package/runtime-api.ts +12 -0
- package/src/lobster-ajv-cache.ts +142 -0
- package/src/lobster-core.d.ts +60 -0
- package/src/lobster-runner.ts +395 -0
- package/src/lobster-taskflow.ts +279 -0
- package/src/lobster-tool.ts +320 -0
- package/src/taskflow-test-helpers.ts +48 -0
- package/tsconfig.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Lobster (plugin)
|
|
2
|
+
|
|
3
|
+
Adds the `lobster` agent tool as an **optional** plugin tool.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
- Lobster is a standalone workflow shell (typed JSON-first pipelines + approvals/resume).
|
|
8
|
+
- This plugin integrates Lobster with AutoBot _without core changes_.
|
|
9
|
+
|
|
10
|
+
## Enable
|
|
11
|
+
|
|
12
|
+
Because this tool can trigger side effects (via workflows), it is registered with `optional: true`.
|
|
13
|
+
|
|
14
|
+
Enable it in an agent allowlist:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"agents": {
|
|
19
|
+
"list": [
|
|
20
|
+
{
|
|
21
|
+
"id": "main",
|
|
22
|
+
"tools": {
|
|
23
|
+
"allow": [
|
|
24
|
+
"lobster" // plugin id (enables all tools from this plugin)
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Using `autobot.invoke` (Lobster → AutoBot tools)
|
|
34
|
+
|
|
35
|
+
Some Lobster pipelines may include a `autobot.invoke` step to call back into AutoBot tools/plugins (for example: `gog` for Google Workspace, `gh` for GitHub, `message.send`, etc.).
|
|
36
|
+
|
|
37
|
+
For this to work, the AutoBot Gateway must expose the tool bridge endpoint and the target tool must be allowed by policy:
|
|
38
|
+
|
|
39
|
+
- AutoBot provides an HTTP endpoint: `POST /tools/invoke`.
|
|
40
|
+
- The request is gated by **gateway auth** (e.g. `Authorization: Bearer …` when token auth is enabled).
|
|
41
|
+
- The invoked tool is gated by **tool policy** (global + per-agent + provider + group policy). If the tool is not allowed, AutoBot returns `404 Tool not available`.
|
|
42
|
+
|
|
43
|
+
### Allowlisting recommended
|
|
44
|
+
|
|
45
|
+
To avoid letting workflows call arbitrary tools, set a tight allowlist on the agent that will be used by `autobot.invoke`.
|
|
46
|
+
|
|
47
|
+
Example (allow only a small set of tools):
|
|
48
|
+
|
|
49
|
+
```jsonc
|
|
50
|
+
{
|
|
51
|
+
"agents": {
|
|
52
|
+
"list": [
|
|
53
|
+
{
|
|
54
|
+
"id": "main",
|
|
55
|
+
"tools": {
|
|
56
|
+
"allow": ["lobster", "web_fetch", "web_search", "gog", "gh"],
|
|
57
|
+
"deny": ["gateway"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
|
|
67
|
+
- If `tools.allow` is omitted or empty, it behaves like "allow everything (except denied)". For a real allowlist, set a **non-empty** `allow`.
|
|
68
|
+
- Tool names depend on which plugins you have installed/enabled.
|
|
69
|
+
|
|
70
|
+
## Security
|
|
71
|
+
|
|
72
|
+
- Runs Lobster in process via the published `@clawdbot/lobster/core` runtime.
|
|
73
|
+
- Does not manage OAuth/tokens.
|
|
74
|
+
- Uses timeouts, stdout caps, and strict JSON envelope parsing.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Lobster
|
|
2
|
+
|
|
3
|
+
Lobster executes multi-step workflows with approval checkpoints. Use it when:
|
|
4
|
+
|
|
5
|
+
- User wants a repeatable automation (triage, monitor, sync)
|
|
6
|
+
- Actions need human approval before executing (send, post, delete)
|
|
7
|
+
- Multiple tool calls should run as one deterministic operation
|
|
8
|
+
|
|
9
|
+
## When to use Lobster
|
|
10
|
+
|
|
11
|
+
| User intent | Use Lobster? |
|
|
12
|
+
| ------------------------------------------------------ | --------------------------------------------- |
|
|
13
|
+
| "Triage my email" | Yes — multi-step, may send replies |
|
|
14
|
+
| "Send a message" | No — single action, use message tool directly |
|
|
15
|
+
| "Check my email every morning and ask before replying" | Yes — scheduled workflow with approval |
|
|
16
|
+
| "What's the weather?" | No — simple query |
|
|
17
|
+
| "Monitor this PR and notify me of changes" | Yes — stateful, recurring |
|
|
18
|
+
|
|
19
|
+
## Basic usage
|
|
20
|
+
|
|
21
|
+
### Run a pipeline
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"action": "run",
|
|
26
|
+
"pipeline": "gog.gmail.search --query 'newer_than:1d' --max 20 | email.triage"
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Returns structured result:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"protocolVersion": 1,
|
|
35
|
+
"ok": true,
|
|
36
|
+
"status": "ok",
|
|
37
|
+
"output": [{ "summary": {...}, "items": [...] }],
|
|
38
|
+
"requiresApproval": null
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Handle approval
|
|
43
|
+
|
|
44
|
+
If the workflow needs approval:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"status": "needs_approval",
|
|
49
|
+
"output": [],
|
|
50
|
+
"requiresApproval": {
|
|
51
|
+
"prompt": "Send 3 draft replies?",
|
|
52
|
+
"items": [...],
|
|
53
|
+
"resumeToken": "..."
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Present the prompt to the user. If they approve:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"action": "resume",
|
|
63
|
+
"token": "<resumeToken>",
|
|
64
|
+
"approve": true
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Example workflows
|
|
69
|
+
|
|
70
|
+
### Email triage
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
gog.gmail.search --query 'newer_than:1d' --max 20 | email.triage
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Fetches recent emails, classifies into buckets (needs_reply, needs_action, fyi).
|
|
77
|
+
|
|
78
|
+
### Email triage with approval gate
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
gog.gmail.search --query 'newer_than:1d' | email.triage | approve --prompt 'Process these?'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Same as above, but halts for approval before returning.
|
|
85
|
+
|
|
86
|
+
## Key behaviors
|
|
87
|
+
|
|
88
|
+
- **Deterministic**: Same input → same output (no LLM variance in pipeline execution)
|
|
89
|
+
- **Approval gates**: `approve` command halts execution, returns token
|
|
90
|
+
- **Resumable**: Use `resume` action with token to continue
|
|
91
|
+
- **Structured output**: Always returns JSON envelope with `protocolVersion`
|
|
92
|
+
|
|
93
|
+
## Don't use Lobster for
|
|
94
|
+
|
|
95
|
+
- Simple single-action requests (just use the tool directly)
|
|
96
|
+
- Queries that need LLM interpretation mid-flow
|
|
97
|
+
- One-off tasks that won't be repeated
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "lobster",
|
|
3
|
+
"activation": {
|
|
4
|
+
"onStartup": true
|
|
5
|
+
},
|
|
6
|
+
"name": "Lobster",
|
|
7
|
+
"description": "Typed workflow tool with resumable approvals.",
|
|
8
|
+
"contracts": {
|
|
9
|
+
"tools": ["lobster"]
|
|
10
|
+
},
|
|
11
|
+
"toolMetadata": {
|
|
12
|
+
"lobster": {
|
|
13
|
+
"optional": true
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"configSchema": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { definePluginEntry } from "autobot/plugin-sdk/plugin-entry";
|
|
2
|
+
import type { AnyAgentTool, AutoBotPluginApi, AutoBotPluginToolFactory } from "./runtime-api.js";
|
|
3
|
+
import { createLobsterTool } from "./src/lobster-tool.js";
|
|
4
|
+
|
|
5
|
+
export default definePluginEntry({
|
|
6
|
+
id: "lobster",
|
|
7
|
+
name: "Lobster",
|
|
8
|
+
description: "Optional local shell helper tools",
|
|
9
|
+
register(api: AutoBotPluginApi) {
|
|
10
|
+
api.registerTool(
|
|
11
|
+
((ctx) => {
|
|
12
|
+
if (ctx.sandboxed) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const taskFlow =
|
|
16
|
+
api.runtime?.tasks.managedFlows && ctx.sessionKey
|
|
17
|
+
? api.runtime.tasks.managedFlows.fromToolContext(ctx)
|
|
18
|
+
: undefined;
|
|
19
|
+
return createLobsterTool(api, { taskFlow }) as AnyAgentTool;
|
|
20
|
+
}) as AutoBotPluginToolFactory,
|
|
21
|
+
{ optional: true },
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gakr-gakr/lobster",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/autobot/autobot"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@clawdbot/lobster": "2026.4.6",
|
|
12
|
+
"ajv": "8.20.0",
|
|
13
|
+
"typebox": "1.1.38"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@gakr-gakr/plugin-sdk": "workspace:*"
|
|
17
|
+
},
|
|
18
|
+
"autobot": {
|
|
19
|
+
"extensions": [
|
|
20
|
+
"./index.ts"
|
|
21
|
+
],
|
|
22
|
+
"install": {
|
|
23
|
+
"npmSpec": "@gakr-gakr/lobster",
|
|
24
|
+
"defaultChoice": "npm",
|
|
25
|
+
"minHostVersion": ">=2026.4.25"
|
|
26
|
+
},
|
|
27
|
+
"compat": {
|
|
28
|
+
"pluginApi": ">=2026.5.19"
|
|
29
|
+
},
|
|
30
|
+
"build": {
|
|
31
|
+
"autobotVersion": "2026.5.19"
|
|
32
|
+
},
|
|
33
|
+
"release": {
|
|
34
|
+
"publishToClawHub": true,
|
|
35
|
+
"publishToNpm": true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/runtime-api.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { definePluginEntry } from "autobot/plugin-sdk/core";
|
|
2
|
+
export type {
|
|
3
|
+
AnyAgentTool,
|
|
4
|
+
AutoBotPluginApi,
|
|
5
|
+
AutoBotPluginToolContext,
|
|
6
|
+
AutoBotPluginToolFactory,
|
|
7
|
+
} from "autobot/plugin-sdk/core";
|
|
8
|
+
export {
|
|
9
|
+
applyWindowsSpawnProgramPolicy,
|
|
10
|
+
materializeWindowsSpawnProgram,
|
|
11
|
+
resolveWindowsSpawnProgramCandidate,
|
|
12
|
+
} from "autobot/plugin-sdk/windows-spawn";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import AjvPkg, { type AnySchema, type ValidateFunction } from "ajv";
|
|
3
|
+
|
|
4
|
+
const installedSymbol = Symbol.for("autobot.lobster.ajv-compile-cache.installed");
|
|
5
|
+
const cacheSymbol = Symbol.for("autobot.lobster.ajv-compile-cache.entries");
|
|
6
|
+
const maxEntries = 512;
|
|
7
|
+
|
|
8
|
+
type AjvInstance = import("ajv").default;
|
|
9
|
+
|
|
10
|
+
type CompileCacheEntry = {
|
|
11
|
+
schema: AnySchema;
|
|
12
|
+
validate: ValidateFunction;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const AjvCtor = AjvPkg as unknown as {
|
|
16
|
+
new (opts?: object): AjvInstance;
|
|
17
|
+
prototype: AjvInstance;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type AjvWithCompileCache = AjvInstance & {
|
|
21
|
+
[cacheSymbol]?: Map<string, CompileCacheEntry>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type AjvPrototypePatch = {
|
|
25
|
+
[installedSymbol]?: boolean;
|
|
26
|
+
compile: (schema: AnySchema) => ValidateFunction;
|
|
27
|
+
removeSchema: (schemaKeyRef?: Parameters<AjvInstance["removeSchema"]>[0]) => AjvInstance;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type JsonLike = null | boolean | number | string | JsonLike[] | { [key: string]: JsonLike };
|
|
31
|
+
|
|
32
|
+
function stableJsonStringify(value: unknown, seen = new WeakSet<object>()): string {
|
|
33
|
+
if (value === null || typeof value !== "object") {
|
|
34
|
+
return JSON.stringify(value);
|
|
35
|
+
}
|
|
36
|
+
if (seen.has(value)) {
|
|
37
|
+
throw new TypeError("Cannot cache cyclic JSON schema");
|
|
38
|
+
}
|
|
39
|
+
seen.add(value);
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
const items = value.map((entry) => stableJsonStringify(entry, seen));
|
|
42
|
+
seen.delete(value);
|
|
43
|
+
return `[${items.join(",")}]`;
|
|
44
|
+
}
|
|
45
|
+
const record = value as Record<string, unknown>;
|
|
46
|
+
const keys = Object.keys(record).toSorted();
|
|
47
|
+
const properties = keys
|
|
48
|
+
.filter((key) => record[key] !== undefined)
|
|
49
|
+
.map((key) => `${JSON.stringify(key)}:${stableJsonStringify(record[key], seen)}`);
|
|
50
|
+
seen.delete(value);
|
|
51
|
+
return `{${properties.join(",")}}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function compileCacheKey(schema: unknown): string | null {
|
|
55
|
+
try {
|
|
56
|
+
return createHash("sha256").update(stableJsonStringify(schema)).digest("hex");
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readCompileCache(instance: AjvWithCompileCache): Map<string, CompileCacheEntry> {
|
|
63
|
+
let cache = instance[cacheSymbol];
|
|
64
|
+
if (!cache) {
|
|
65
|
+
cache = new Map<string, CompileCacheEntry>();
|
|
66
|
+
Object.defineProperty(instance, cacheSymbol, {
|
|
67
|
+
value: cache,
|
|
68
|
+
configurable: true,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return cache;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function rememberCompiledValidator(params: {
|
|
75
|
+
cache: Map<string, CompileCacheEntry>;
|
|
76
|
+
instance: AjvWithCompileCache;
|
|
77
|
+
key: string;
|
|
78
|
+
removeSchema: AjvPrototypePatch["removeSchema"];
|
|
79
|
+
schema: AnySchema;
|
|
80
|
+
validate: ValidateFunction;
|
|
81
|
+
}) {
|
|
82
|
+
const { cache, instance, key, removeSchema, schema, validate } = params;
|
|
83
|
+
if (!cache.has(key) && cache.size >= maxEntries) {
|
|
84
|
+
const oldest = cache.keys().next().value;
|
|
85
|
+
if (oldest !== undefined) {
|
|
86
|
+
const evicted = cache.get(oldest);
|
|
87
|
+
cache.delete(oldest);
|
|
88
|
+
if (evicted) {
|
|
89
|
+
removeSchema.call(instance, evicted.schema);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
cache.set(key, { schema, validate });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function installLobsterAjvCompileCache() {
|
|
97
|
+
const proto = AjvCtor.prototype as unknown as AjvPrototypePatch;
|
|
98
|
+
if (proto[installedSymbol]) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const originalCompile = proto.compile;
|
|
103
|
+
const originalRemoveSchema = proto.removeSchema;
|
|
104
|
+
|
|
105
|
+
Object.defineProperty(proto, installedSymbol, {
|
|
106
|
+
value: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
proto.compile = function compileWithContentCache(
|
|
111
|
+
this: AjvWithCompileCache,
|
|
112
|
+
schema: AnySchema,
|
|
113
|
+
): ValidateFunction<JsonLike> {
|
|
114
|
+
const key = compileCacheKey(schema);
|
|
115
|
+
if (!key) {
|
|
116
|
+
return originalCompile.call(this, schema) as ValidateFunction<JsonLike>;
|
|
117
|
+
}
|
|
118
|
+
const cache = readCompileCache(this);
|
|
119
|
+
const cached = cache.get(key);
|
|
120
|
+
if (cached) {
|
|
121
|
+
return cached.validate as ValidateFunction<JsonLike>;
|
|
122
|
+
}
|
|
123
|
+
const validate = originalCompile.call(this, schema) as ValidateFunction<JsonLike>;
|
|
124
|
+
rememberCompiledValidator({
|
|
125
|
+
cache,
|
|
126
|
+
instance: this,
|
|
127
|
+
key,
|
|
128
|
+
removeSchema: originalRemoveSchema,
|
|
129
|
+
schema,
|
|
130
|
+
validate,
|
|
131
|
+
});
|
|
132
|
+
return validate;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
proto.removeSchema = function removeSchemaAndClearContentCache(
|
|
136
|
+
this: AjvWithCompileCache,
|
|
137
|
+
schemaKeyRef?: Parameters<AjvInstance["removeSchema"]>[0],
|
|
138
|
+
) {
|
|
139
|
+
this[cacheSymbol]?.clear();
|
|
140
|
+
return originalRemoveSchema.call(this, schemaKeyRef);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
declare module "@clawdbot/lobster/core" {
|
|
2
|
+
type LobsterApprovalRequest = {
|
|
3
|
+
type: "approval_request";
|
|
4
|
+
prompt: string;
|
|
5
|
+
items: unknown[];
|
|
6
|
+
resumeToken?: string;
|
|
7
|
+
approvalId?: string;
|
|
8
|
+
} | null;
|
|
9
|
+
|
|
10
|
+
type LobsterToolContext = {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
env?: Record<string, string | undefined>;
|
|
13
|
+
stdin?: NodeJS.ReadableStream;
|
|
14
|
+
stdout?: NodeJS.WritableStream;
|
|
15
|
+
stderr?: NodeJS.WritableStream;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
registry?: unknown;
|
|
18
|
+
llmAdapters?: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type LobsterToolEnvelope =
|
|
22
|
+
| {
|
|
23
|
+
protocolVersion: 1;
|
|
24
|
+
ok: true;
|
|
25
|
+
status: "ok" | "needs_approval" | "needs_input" | "cancelled";
|
|
26
|
+
output: unknown[];
|
|
27
|
+
requiresApproval: LobsterApprovalRequest;
|
|
28
|
+
requiresInput?: {
|
|
29
|
+
prompt: string;
|
|
30
|
+
schema?: unknown;
|
|
31
|
+
items?: unknown[];
|
|
32
|
+
resumeToken?: string;
|
|
33
|
+
approvalId?: string;
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
protocolVersion: 1;
|
|
38
|
+
ok: false;
|
|
39
|
+
error: {
|
|
40
|
+
type: string;
|
|
41
|
+
message: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function runToolRequest(params: {
|
|
46
|
+
pipeline?: string;
|
|
47
|
+
filePath?: string;
|
|
48
|
+
args?: Record<string, unknown>;
|
|
49
|
+
ctx?: LobsterToolContext;
|
|
50
|
+
}): Promise<LobsterToolEnvelope>;
|
|
51
|
+
|
|
52
|
+
export function resumeToolRequest(params: {
|
|
53
|
+
token?: string;
|
|
54
|
+
approvalId?: string;
|
|
55
|
+
approved?: boolean;
|
|
56
|
+
response?: unknown;
|
|
57
|
+
cancel?: boolean;
|
|
58
|
+
ctx?: LobsterToolContext;
|
|
59
|
+
}): Promise<LobsterToolEnvelope>;
|
|
60
|
+
}
|