@openclaw/lobster 2026.9.5 → 2026.9.6
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/node_modules/@clawdbot/lobster/README.md +20 -133
- package/node_modules/@clawdbot/lobster/VISION.md +15 -238
- package/node_modules/@clawdbot/lobster/bin/clawd.invoke.js +2 -21
- package/node_modules/@clawdbot/lobster/bin/invoke.js +19 -0
- package/node_modules/@clawdbot/lobster/bin/lobster.js +2 -21
- package/node_modules/@clawdbot/lobster/bin/openclaw.invoke.js +2 -24
- package/node_modules/@clawdbot/lobster/dist/src/cli.js +32 -48
- package/node_modules/@clawdbot/lobster/dist/src/cli.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js +81 -0
- package/node_modules/@clawdbot/lobster/dist/src/command_tokens.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js +1 -12
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/dedupe.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js +2 -14
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/group_by.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js +1 -337
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/llm_invoke.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js +2 -13
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/map.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js +4 -21
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/sort.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/state.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js +2 -13
- package/node_modules/@clawdbot/lobster/dist/src/commands/stdlib/template.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js +0 -18
- package/node_modules/@clawdbot/lobster/dist/src/commands/workflows/workflows_run.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js +304 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/llm_accounting.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js +11 -0
- package/node_modules/@clawdbot/lobster/dist/src/core/value_path.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/parser.js +2 -67
- package/node_modules/@clawdbot/lobster/dist/src/parser.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/index.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js +5 -64
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/pr-monitor.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js +45 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/read_pr.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js +45 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/snapshot.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js +9 -47
- package/node_modules/@clawdbot/lobster/dist/src/recipes/github/stages/pr-view.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js +18 -135
- package/node_modules/@clawdbot/lobster/dist/src/sdk/Lobster.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/index.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js +1 -2
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/approve.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js +2 -8
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/diff.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js +15 -81
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/exec.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js +3 -9
- package/node_modules/@clawdbot/lobster/dist/src/sdk/primitives/state.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js +0 -12
- package/node_modules/@clawdbot/lobster/dist/src/sdk/runtime.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js +6 -0
- package/node_modules/@clawdbot/lobster/dist/src/sdk/state_env.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js +192 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/atomic_file.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/state/store.js +2 -188
- package/node_modules/@clawdbot/lobster/dist/src/state/store.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js +262 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/dry_run.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js +345 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/expressions.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js +21 -1036
- package/node_modules/@clawdbot/lobster/dist/src/workflows/file.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js +8 -77
- package/node_modules/@clawdbot/lobster/dist/src/workflows/github_pr_monitor.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js +1 -7
- package/node_modules/@clawdbot/lobster/dist/src/workflows/graph.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js +347 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/load.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js +2 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/types.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js +93 -0
- package/node_modules/@clawdbot/lobster/dist/src/workflows/values.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cancellation.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js +53 -0
- package/node_modules/@clawdbot/lobster/dist/test/cli_run_errors.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js +82 -0
- package/node_modules/@clawdbot/lobster/dist/test/command_arguments.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js +49 -19
- package/node_modules/@clawdbot/lobster/dist/test/cost_tracker.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js +17 -0
- package/node_modules/@clawdbot/lobster/dist/test/helpers/node_shell_fixture.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js +2 -0
- package/node_modules/@clawdbot/lobster/dist/test/llm_invoke.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js +65 -0
- package/node_modules/@clawdbot/lobster/dist/test/pipeline_resume_state.test.js.map +1 -0
- package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js +5 -3
- package/node_modules/@clawdbot/lobster/dist/test/process_output_limit.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js +72 -25
- package/node_modules/@clawdbot/lobster/dist/test/sdk_exec.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js +1 -1
- package/node_modules/@clawdbot/lobster/dist/test/workflow_graph.test.js.map +1 -1
- package/node_modules/@clawdbot/lobster/package.json +6 -8
- package/package.json +5 -5
- package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js +0 -9
- package/node_modules/@clawdbot/lobster/dist/src/sdk/token.js.map +0 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
for (const kind of ["missing", "directory", "extension"]) {
|
|
8
|
+
test(`tool run reports ${kind} workflow target errors as JSON`, async () => {
|
|
9
|
+
const dir = await mkdtemp(join(tmpdir(), "lobster-target-error-"));
|
|
10
|
+
try {
|
|
11
|
+
const target = kind === "directory"
|
|
12
|
+
? dir
|
|
13
|
+
: join(dir, kind === "extension" ? "workflow.txt" : "missing.lobster");
|
|
14
|
+
if (kind === "extension")
|
|
15
|
+
await writeFile(target, JSON.stringify({ steps: [{ id: "ok", run: "echo ok" }] }));
|
|
16
|
+
const result = spawnSync(process.execPath, ["bin/lobster.js", "run", "--mode", "tool", "--file", target], { encoding: "utf8" });
|
|
17
|
+
assert.equal(result.status, 2);
|
|
18
|
+
const envelope = JSON.parse(result.stdout);
|
|
19
|
+
assert.equal(envelope.protocolVersion, 1);
|
|
20
|
+
assert.equal(envelope.ok, false);
|
|
21
|
+
assert.equal(envelope.error.type, "parse_error");
|
|
22
|
+
assert.match(envelope.error.message, kind === "missing" ? /ENOENT/ : kind === "directory" ? /not a file/ : /must end in/);
|
|
23
|
+
assert.equal(result.stderr, "");
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await rm(dir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
test("human run reports missing workflow without an uncaught stack", async () => {
|
|
31
|
+
const dir = await mkdtemp(join(tmpdir(), "lobster-human-error-"));
|
|
32
|
+
try {
|
|
33
|
+
const result = spawnSync(process.execPath, ["bin/lobster.js", "run", "--file", join(dir, "missing.json")], { encoding: "utf8" });
|
|
34
|
+
assert.equal(result.status, 2);
|
|
35
|
+
assert.equal(result.stdout, "");
|
|
36
|
+
assert.match(result.stderr, /^Parse error: .*ENOENT/);
|
|
37
|
+
assert.doesNotMatch(result.stderr, /at async|node:internal/);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await rm(dir, { recursive: true, force: true });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
for (const flag of ["--help", "-h"]) {
|
|
44
|
+
test(`graph ${flag} shows graph-specific options`, () => {
|
|
45
|
+
const result = spawnSync(process.execPath, ["bin/lobster.js", "graph", flag], {
|
|
46
|
+
encoding: "utf8",
|
|
47
|
+
});
|
|
48
|
+
assert.equal(result.status, 0);
|
|
49
|
+
assert.match(result.stdout, /--format\s+Output format: mermaid \(default\), dot, ascii/);
|
|
50
|
+
assert.equal(result.stderr, "");
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cli_run_errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli_run_errors.test.js","sourceRoot":"","sources":["../../test/cli_run_errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { createServer } from "node:http";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { parsePipeline } from "../src/parser.js";
|
|
7
|
+
import { Lobster, exec } from "../src/sdk/index.js";
|
|
8
|
+
const runFile = promisify(execFile);
|
|
9
|
+
const argvScript = "console.log(JSON.stringify(process.argv.slice(1)))";
|
|
10
|
+
const argvCommand = `${JSON.stringify(process.execPath)} -e '${argvScript}' "" tail`;
|
|
11
|
+
test("pipeline parsing preserves empty positional arguments", () => {
|
|
12
|
+
assert.deepEqual(parsePipeline(`exec node "" ' ' '' tail`)[0].args._, [
|
|
13
|
+
"node",
|
|
14
|
+
"",
|
|
15
|
+
" ",
|
|
16
|
+
"",
|
|
17
|
+
"tail",
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
test("pipeline parsing preserves an empty quoted option value", () => {
|
|
21
|
+
const args = parsePipeline(`custom --value "" tail`)[0].args;
|
|
22
|
+
assert.equal(args["value"], "");
|
|
23
|
+
assert.deepEqual(args._, ["tail"]);
|
|
24
|
+
});
|
|
25
|
+
test("built CLI passes explicit empty arguments to a child process", async () => {
|
|
26
|
+
const { stdout } = await runFile(process.execPath, [
|
|
27
|
+
"bin/lobster.js",
|
|
28
|
+
"run",
|
|
29
|
+
"--mode",
|
|
30
|
+
"tool",
|
|
31
|
+
`exec --json ${argvCommand}`,
|
|
32
|
+
]);
|
|
33
|
+
assert.deepEqual(JSON.parse(stdout).output, ["", "tail"]);
|
|
34
|
+
});
|
|
35
|
+
test("SDK exec passes explicit empty arguments to a child process", async () => {
|
|
36
|
+
const result = await new Lobster().pipe(exec(argvCommand)).run();
|
|
37
|
+
assert.equal(result.ok, true, JSON.stringify(result));
|
|
38
|
+
assert.deepEqual(result.output, ["", "tail"]);
|
|
39
|
+
});
|
|
40
|
+
for (const alias of ["openclaw.invoke", "clawd.invoke"]) {
|
|
41
|
+
test(`${alias} shim preserves punctuation and literal pipeline characters`, async () => {
|
|
42
|
+
const expected = {
|
|
43
|
+
text: 'O\'Malley says "hello"\nnext line',
|
|
44
|
+
path: "C:\\folder\\",
|
|
45
|
+
literal: "$HOME `id` | head",
|
|
46
|
+
empty: "",
|
|
47
|
+
};
|
|
48
|
+
const requests = [];
|
|
49
|
+
const server = createServer(async (req, res) => {
|
|
50
|
+
let body = "";
|
|
51
|
+
for await (const chunk of req)
|
|
52
|
+
body += chunk;
|
|
53
|
+
const payload = JSON.parse(body);
|
|
54
|
+
requests.push(payload.args);
|
|
55
|
+
res.setHeader("content-type", "application/json");
|
|
56
|
+
res.end(JSON.stringify({ ok: true, result: [payload.args] }));
|
|
57
|
+
});
|
|
58
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
59
|
+
try {
|
|
60
|
+
const address = server.address();
|
|
61
|
+
assert.ok(address && typeof address === "object");
|
|
62
|
+
const { stdout } = await runFile(process.execPath, [
|
|
63
|
+
`bin/${alias}.js`,
|
|
64
|
+
"--url",
|
|
65
|
+
`http://127.0.0.1:${address.port}`,
|
|
66
|
+
"--tool",
|
|
67
|
+
"proof",
|
|
68
|
+
"--action",
|
|
69
|
+
"read",
|
|
70
|
+
"--args-json",
|
|
71
|
+
JSON.stringify(expected),
|
|
72
|
+
], { env: { ...process.env, OPENCLAW_TOKEN: "", CLAWD_TOKEN: "" } });
|
|
73
|
+
assert.deepEqual(JSON.parse(stdout), [expected]);
|
|
74
|
+
assert.deepEqual(requests, [expected]);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
server.closeAllConnections();
|
|
78
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=command_arguments.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command_arguments.test.js","sourceRoot":"","sources":["../../test/command_arguments.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import { PassThrough } from "node:stream";
|
|
8
8
|
import { createDefaultRegistry } from "../src/commands/registry.js";
|
|
9
9
|
import { CostTracker } from "../src/core/cost_tracker.js";
|
|
10
|
-
import { createLlmSpendLedger, llmProvenanceOf, restoreLlmProvenance, } from "../src/
|
|
10
|
+
import { createLlmSpendLedger, llmProvenanceOf, restoreLlmProvenance, } from "../src/core/llm_accounting.js";
|
|
11
11
|
import { runWorkflowFile } from "../src/workflows/file.js";
|
|
12
12
|
import { decodeToken } from "../src/token.js";
|
|
13
13
|
test("CostTracker records usage and computes totals", () => {
|
|
@@ -121,19 +121,25 @@ async function runWorkflow(workflow, envOverride, llmAdapters) {
|
|
|
121
121
|
stderr.on("data", (d) => {
|
|
122
122
|
stderrOutput += String(d);
|
|
123
123
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
124
|
+
try {
|
|
125
|
+
const result = await runWorkflowFile({
|
|
126
|
+
filePath,
|
|
127
|
+
ctx: {
|
|
128
|
+
stdin: process.stdin,
|
|
129
|
+
stdout: process.stdout,
|
|
130
|
+
stderr,
|
|
131
|
+
env: { ...process.env, LOBSTER_STATE_DIR: stateDir, ...envOverride },
|
|
132
|
+
mode: "tool",
|
|
133
|
+
registry: createDefaultRegistry(),
|
|
134
|
+
llmAdapters,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return { result, stderrOutput };
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
stderr.destroy();
|
|
141
|
+
await fsp.rm(tmpDir, { recursive: true, force: true });
|
|
142
|
+
}
|
|
137
143
|
}
|
|
138
144
|
test("workflow result includes _meta.cost when usage is present", async () => {
|
|
139
145
|
const { result } = await runWorkflow({
|
|
@@ -920,11 +926,6 @@ test("workflow cost tracking bills both live calls a retried step's replays stan
|
|
|
920
926
|
await fsp.rm(cacheDir, { recursive: true, force: true });
|
|
921
927
|
}
|
|
922
928
|
});
|
|
923
|
-
// A gate pauses a run rather than resetting what it spent. The resume is a different run — a
|
|
924
|
-
// different process, even — so the call made before the gate is only in the paused run's
|
|
925
|
-
// record; if a later step repeats the prompt, it replays and correctly bills nothing. Without
|
|
926
|
-
// carrying that record forward, the provider call before the gate would appear in no total at
|
|
927
|
-
// all, and a `cost_limit` could be walked past one gate at a time.
|
|
928
929
|
// A gate pauses a run, it does not reset what the run has spent. The resume is a separate run
|
|
929
930
|
// with its own accounting, so a call made before the gate lives only in the paused run's
|
|
930
931
|
// record: if a later step repeats the prompt it replays, correctly bills nothing, and the
|
|
@@ -1960,4 +1961,33 @@ for (const loop of [false, true]) {
|
|
|
1960
1961
|
assert.equal(calls, 1, "a downstream failure must not hide paid usage from the retry budget");
|
|
1961
1962
|
});
|
|
1962
1963
|
}
|
|
1964
|
+
for (const rendered of [false, true]) {
|
|
1965
|
+
for (const passthrough of ["head --n 1", "pick usage"]) {
|
|
1966
|
+
test(`uninterrupted workflow does not rebill ${rendered ? "rendered" : "live"} output through ${passthrough}`, async () => {
|
|
1967
|
+
const provider = await startFakeProvider();
|
|
1968
|
+
const cacheDir = await fsp.mkdtemp(path.join(os.tmpdir(), "lobster-cost-step-reference-"));
|
|
1969
|
+
try {
|
|
1970
|
+
const { result } = await runWorkflow({
|
|
1971
|
+
cost_limit: { max_usd: 0.01, action: "stop" },
|
|
1972
|
+
steps: [
|
|
1973
|
+
{
|
|
1974
|
+
id: "live",
|
|
1975
|
+
pipeline: `llm.invoke --model gpt-4o --prompt Summarize${rendered ? " | json" : ""}`,
|
|
1976
|
+
},
|
|
1977
|
+
{ id: "copy", pipeline: passthrough, stdin: "$live.json" },
|
|
1978
|
+
{ id: "again", pipeline: "head --n 1", stdin: "$copy.json" },
|
|
1979
|
+
],
|
|
1980
|
+
}, { OPENCLAW_URL: provider.url, LOBSTER_CACHE_DIR: cacheDir });
|
|
1981
|
+
assert.equal(provider.requests(), 1);
|
|
1982
|
+
assert.equal(result._meta?.cost?.totalInputTokens, 1000);
|
|
1983
|
+
assert.equal(result._meta?.cost?.totalOutputTokens, 500);
|
|
1984
|
+
assert.deepEqual(result._meta?.cost?.byStep.map((entry) => entry.stepId), ["live"]);
|
|
1985
|
+
}
|
|
1986
|
+
finally {
|
|
1987
|
+
await provider.close();
|
|
1988
|
+
await fsp.rm(cacheDir, { recursive: true, force: true });
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1963
1993
|
//# sourceMappingURL=cost_tracker.test.js.map
|