@vimhead.dev/norn-cli 0.1.0-tip.35614826665.1 → 0.1.0-tip.35724621678.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/README.md +41 -13
- package/assets/docs/README.md +16 -16
- package/assets/docs/agents.md +23 -15
- package/assets/docs/cli.md +10 -8
- package/assets/docs/composition.md +7 -7
- package/assets/docs/persistence.md +22 -22
- package/assets/docs/projects.md +6 -6
- package/assets/docs/recovery.md +10 -10
- package/assets/docs/schemas.md +42 -36
- package/assets/docs/workflows.md +39 -32
- package/assets/examples/agent-then-analysis/input.json +3 -3
- package/assets/examples/agent-then-analysis/norn.project.json +2 -2
- package/assets/examples/agent-then-analysis/plugin.ts +37 -9
- package/assets/examples/caller-owned-routing/README.md +6 -6
- package/assets/examples/caller-owned-routing/assessment.ts +18 -4
- package/assets/examples/caller-owned-routing/contracts.ts +7 -2
- package/assets/examples/caller-owned-routing/input.json +12 -12
- package/assets/examples/caller-owned-routing/norn.project.json +2 -2
- package/assets/examples/caller-owned-routing/revision.ts +7 -2
- package/assets/examples/caller-owned-routing/router.ts +14 -3
- package/assets/examples/caller-selected-continuation/caller.ts +38 -12
- package/assets/examples/caller-selected-continuation/input.json +7 -7
- package/assets/examples/caller-selected-continuation/norn.project.json +2 -2
- package/assets/examples/caller-selected-continuation/producer.ts +10 -3
- package/assets/examples/coordinating-multiple-agents/README.md +6 -6
- package/assets/examples/coordinating-multiple-agents/input.json +20 -8
- package/assets/examples/coordinating-multiple-agents/norn.project.json +2 -2
- package/assets/examples/coordinating-multiple-agents/plugin.ts +165 -46
- package/assets/examples/coordinating-multiple-agents/queue-tools.ts +30 -7
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +211 -63
- package/assets/examples/getting-started/norn.project.json +2 -2
- package/assets/examples/getting-started/plugin.ts +33 -13
- package/assets/examples/minimal-workflow/norn.project.json +2 -2
- package/assets/examples/minimal-workflow/plugin.ts +12 -3
- package/assets/examples/shared-state/README.md +6 -6
- package/assets/examples/shared-state/input.json +1 -1
- package/assets/examples/shared-state/norn.project.json +2 -2
- package/assets/examples/shared-state/plugin.ts +44 -15
- package/assets/examples/shared-state/shared-state.ts +46 -13
- package/assets/examples/shared-state/state-tools.ts +68 -16
- package/assets/examples/worktree-development-loop/README.md +12 -12
- package/assets/examples/worktree-development-loop/norn.project.json +7 -7
- package/assets/examples/worktree-development-loop/plugin.ts +7 -1
- package/assets/examples/worktree-development-loop/scope.ts +4 -1
- package/assets/examples/worktree-development-loop/shared/commands.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -4
- package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +11 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +40 -10
- package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/planning/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +33 -9
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -8
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/build-info.ts +4 -1
- package/assets/packages/cli/src/bun/cli.ts +21 -5
- package/assets/packages/cli/src/cli.ts +585 -170
- package/assets/packages/cli/src/client.ts +194 -36
- package/assets/packages/cli/src/documentation.ts +135 -30
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agent-directory.ts +7 -2
- package/assets/packages/cli/src/internal/agent-response-tool.ts +57 -15
- package/assets/packages/cli/src/internal/agents.ts +248 -60
- package/assets/packages/cli/src/internal/commands.ts +82 -20
- package/assets/packages/cli/src/internal/documentation-bundle.ts +44 -13
- package/assets/packages/cli/src/internal/engine.ts +396 -87
- package/assets/packages/cli/src/internal/errors.ts +13 -5
- package/assets/packages/cli/src/internal/execution-context.ts +20 -12
- package/assets/packages/cli/src/internal/file-coordinator.ts +95 -26
- package/assets/packages/cli/src/internal/file-names.ts +4 -1
- package/assets/packages/cli/src/internal/launch-request.ts +74 -22
- package/assets/packages/cli/src/internal/logs.ts +10 -2
- package/assets/packages/cli/src/internal/metrics.ts +247 -61
- package/assets/packages/cli/src/internal/pi-assets.ts +105 -23
- package/assets/packages/cli/src/internal/run-lease.ts +72 -22
- package/assets/packages/cli/src/internal/run-log.ts +49 -20
- package/assets/packages/cli/src/internal/run-names.ts +120 -12
- package/assets/packages/cli/src/internal/run-state.ts +306 -71
- package/assets/packages/cli/src/internal/run-store.ts +216 -53
- package/assets/packages/cli/src/internal/usage.ts +23 -7
- package/assets/packages/cli/src/internal/worker-directory.ts +9 -3
- package/assets/packages/cli/src/internal/workflow-registry.ts +281 -70
- package/assets/packages/cli/src/internal/working-directory.ts +2 -1
- package/assets/packages/cli/src/workflow-loader.ts +341 -74
- package/assets/packages/core/src/atomic-files.ts +13 -4
- package/assets/packages/core/src/workflow-transition.ts +13 -2
- package/assets/packages/sdk/src/api.ts +248 -80
- package/assets/packages/sdk/src/index.ts +6 -1
- package/assets/packages/sdk/src/schema.ts +75 -21
- package/assets/tests/workflow-ref.test.ts +242 -57
- package/bin/norn.mjs +13 -4
- package/dist/cli.js +344 -90
- package/dist/client.js +100 -22
- package/dist/documentation.js +102 -25
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agent-directory.js +3 -1
- package/dist/internal/agent-response-tool.js +42 -11
- package/dist/internal/agents.js +149 -37
- package/dist/internal/commands.js +65 -16
- package/dist/internal/documentation-bundle.js +23 -11
- package/dist/internal/engine.js +304 -66
- package/dist/internal/errors.js +4 -2
- package/dist/internal/execution-context.js +2 -1
- package/dist/internal/file-coordinator.js +59 -19
- package/dist/internal/launch-request.js +32 -14
- package/dist/internal/metrics.js +138 -38
- package/dist/internal/pi-assets.js +70 -19
- package/dist/internal/run-lease.js +29 -9
- package/dist/internal/run-log.js +24 -12
- package/dist/internal/run-state.js +172 -48
- package/dist/internal/run-store.js +157 -45
- package/dist/internal/worker-directory.js +6 -2
- package/dist/internal/workflow-registry.js +123 -32
- package/dist/internal/working-directory.js +2 -1
- package/dist/workflow-loader.js +198 -41
- package/package.json +2 -2
|
@@ -31,7 +31,10 @@ export class NornRunLease {
|
|
|
31
31
|
private readonly processOwner: NornRunProcessOwner,
|
|
32
32
|
) {}
|
|
33
33
|
|
|
34
|
-
static async acquire(
|
|
34
|
+
static async acquire(
|
|
35
|
+
runRoot: string,
|
|
36
|
+
processOwner: NornRunProcessOwner = currentRunProcessOwner(),
|
|
37
|
+
): Promise<NornRunLease> {
|
|
35
38
|
const lockRoot = join(runRoot, LOCK_DIR_NAME);
|
|
36
39
|
for (let attempt = 1; attempt <= ACQUIRE_ATTEMPTS; attempt++) {
|
|
37
40
|
try {
|
|
@@ -48,7 +51,9 @@ export class NornRunLease {
|
|
|
48
51
|
} catch (error) {
|
|
49
52
|
if (!isNodeError(error) || error.code !== "EEXIST") throw error;
|
|
50
53
|
const owner = await readRunLeaseOwner(join(lockRoot, OWNER_FILE_NAME));
|
|
51
|
-
const isStale = owner
|
|
54
|
+
const isStale = owner
|
|
55
|
+
? isRunLeaseStale(owner)
|
|
56
|
+
: await isRunLeaseDirectoryStale(lockRoot);
|
|
52
57
|
if (!isStale) throw new Error(`Run is already active: ${runRoot}`);
|
|
53
58
|
await rm(lockRoot, { recursive: true, force: true });
|
|
54
59
|
}
|
|
@@ -59,7 +64,8 @@ export class NornRunLease {
|
|
|
59
64
|
async assertOwned(): Promise<void> {
|
|
60
65
|
if (this.isReleased) throw new Error("Run lease has been released");
|
|
61
66
|
const owner = await readRunLeaseOwner(this.ownerPath);
|
|
62
|
-
if (!owner || owner.token !== this.token)
|
|
67
|
+
if (!owner || owner.token !== this.token)
|
|
68
|
+
throw new Error("Run lease was lost");
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
async release(): Promise<void> {
|
|
@@ -68,7 +74,8 @@ export class NornRunLease {
|
|
|
68
74
|
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
69
75
|
await this.heartbeatChain.catch(() => undefined);
|
|
70
76
|
const owner = await readRunLeaseOwner(this.ownerPath);
|
|
71
|
-
if (owner?.token === this.token)
|
|
77
|
+
if (owner?.token === this.token)
|
|
78
|
+
await rm(this.lockRoot, { recursive: true, force: true });
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
private get ownerPath(): string {
|
|
@@ -77,10 +84,12 @@ export class NornRunLease {
|
|
|
77
84
|
|
|
78
85
|
private startHeartbeat(): void {
|
|
79
86
|
this.heartbeat = setInterval(() => {
|
|
80
|
-
this.heartbeatChain = this.heartbeatChain
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
this.heartbeatChain = this.heartbeatChain
|
|
88
|
+
.then(() => this.refreshHeartbeat())
|
|
89
|
+
.catch(() => {
|
|
90
|
+
this.isReleased = true;
|
|
91
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
92
|
+
});
|
|
84
93
|
}, HEARTBEAT_INTERVAL_MS);
|
|
85
94
|
this.heartbeat.unref?.();
|
|
86
95
|
}
|
|
@@ -88,13 +97,21 @@ export class NornRunLease {
|
|
|
88
97
|
private async refreshHeartbeat(): Promise<void> {
|
|
89
98
|
if (this.isReleased) return;
|
|
90
99
|
const owner = await readRunLeaseOwner(this.ownerPath);
|
|
91
|
-
if (!owner || owner.token !== this.token)
|
|
100
|
+
if (!owner || owner.token !== this.token)
|
|
101
|
+
throw new Error("Run lease was lost");
|
|
92
102
|
await this.writeOwner({ acquiredAt: owner.acquiredAt });
|
|
93
103
|
}
|
|
94
104
|
|
|
95
|
-
private async writeOwner(input: {
|
|
105
|
+
private async writeOwner(input: {
|
|
106
|
+
readonly acquiredAt: string;
|
|
107
|
+
}): Promise<void> {
|
|
96
108
|
const now = new Date().toISOString();
|
|
97
|
-
await writeJsonAtomically(this.ownerPath, {
|
|
109
|
+
await writeJsonAtomically(this.ownerPath, {
|
|
110
|
+
token: this.token,
|
|
111
|
+
acquiredAt: input.acquiredAt,
|
|
112
|
+
heartbeatAt: now,
|
|
113
|
+
...this.processOwner,
|
|
114
|
+
});
|
|
98
115
|
}
|
|
99
116
|
}
|
|
100
117
|
|
|
@@ -104,14 +121,18 @@ export type NornRunProcessOwner = {
|
|
|
104
121
|
readonly command: readonly string[];
|
|
105
122
|
};
|
|
106
123
|
|
|
107
|
-
export async function getRunLeaseHealth(
|
|
124
|
+
export async function getRunLeaseHealth(
|
|
125
|
+
runRoot: string,
|
|
126
|
+
): Promise<NornRunHealth> {
|
|
108
127
|
const lockRoot = join(runRoot, LOCK_DIR_NAME);
|
|
109
128
|
const owner = await readRunLeaseOwner(join(lockRoot, OWNER_FILE_NAME));
|
|
110
129
|
if (owner) return isRunLeaseStale(owner) ? "unhealthy" : "healthy";
|
|
111
|
-
return await isRunLeaseDirectoryStale(lockRoot) ? "unhealthy" : "healthy";
|
|
130
|
+
return (await isRunLeaseDirectoryStale(lockRoot)) ? "unhealthy" : "healthy";
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
export async function getRunLeaseOwner(
|
|
133
|
+
export async function getRunLeaseOwner(
|
|
134
|
+
runRoot: string,
|
|
135
|
+
): Promise<NornRunLeaseOwner | undefined> {
|
|
115
136
|
return readRunLeaseOwner(join(runRoot, LOCK_DIR_NAME, OWNER_FILE_NAME));
|
|
116
137
|
}
|
|
117
138
|
|
|
@@ -123,7 +144,9 @@ function currentRunProcessOwner(): NornRunProcessOwner {
|
|
|
123
144
|
};
|
|
124
145
|
}
|
|
125
146
|
|
|
126
|
-
async function readRunLeaseOwner(
|
|
147
|
+
async function readRunLeaseOwner(
|
|
148
|
+
path: string,
|
|
149
|
+
): Promise<NornRunLeaseOwner | undefined> {
|
|
127
150
|
try {
|
|
128
151
|
return parseRunLeaseOwner(JSON.parse(await readFile(path, "utf8")));
|
|
129
152
|
} catch (error) {
|
|
@@ -135,13 +158,40 @@ async function readRunLeaseOwner(path: string): Promise<NornRunLeaseOwner | unde
|
|
|
135
158
|
function parseRunLeaseOwner(value: unknown): NornRunLeaseOwner | undefined {
|
|
136
159
|
if (!value || typeof value !== "object") return undefined;
|
|
137
160
|
const owner = value as Partial<NornRunLeaseOwner>;
|
|
138
|
-
if (typeof owner.token !== "string" || owner.token.length === 0)
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
if (typeof owner.token !== "string" || owner.token.length === 0)
|
|
162
|
+
return undefined;
|
|
163
|
+
if (
|
|
164
|
+
typeof owner.acquiredAt !== "string" ||
|
|
165
|
+
Number.isNaN(Date.parse(owner.acquiredAt))
|
|
166
|
+
)
|
|
167
|
+
return undefined;
|
|
168
|
+
if (
|
|
169
|
+
typeof owner.heartbeatAt !== "string" ||
|
|
170
|
+
Number.isNaN(Date.parse(owner.heartbeatAt))
|
|
171
|
+
)
|
|
172
|
+
return undefined;
|
|
173
|
+
const pid =
|
|
174
|
+
typeof owner.pid === "number" && Number.isInteger(owner.pid)
|
|
175
|
+
? owner.pid
|
|
176
|
+
: 0;
|
|
177
|
+
const processGroupId =
|
|
178
|
+
typeof owner.processGroupId === "number" &&
|
|
179
|
+
Number.isInteger(owner.processGroupId)
|
|
180
|
+
? owner.processGroupId
|
|
181
|
+
: pid;
|
|
182
|
+
const command = Array.isArray(owner.command)
|
|
183
|
+
? owner.command.filter(
|
|
184
|
+
(value): value is string => typeof value === "string",
|
|
185
|
+
)
|
|
186
|
+
: [];
|
|
187
|
+
return {
|
|
188
|
+
token: owner.token,
|
|
189
|
+
acquiredAt: owner.acquiredAt,
|
|
190
|
+
heartbeatAt: owner.heartbeatAt,
|
|
191
|
+
pid,
|
|
192
|
+
processGroupId,
|
|
193
|
+
command,
|
|
194
|
+
};
|
|
145
195
|
}
|
|
146
196
|
|
|
147
197
|
function isRunLeaseStale(owner: NornRunLeaseOwner): boolean {
|
|
@@ -23,37 +23,66 @@ export type NornRunManifest = {
|
|
|
23
23
|
export class NornRunLogger {
|
|
24
24
|
private isInitialized = false;
|
|
25
25
|
|
|
26
|
-
constructor(
|
|
27
|
-
readonly
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
constructor(
|
|
27
|
+
private readonly input: {
|
|
28
|
+
readonly manifestPath: string;
|
|
29
|
+
readonly manifest: Omit<NornRunManifest, "events">;
|
|
30
|
+
readonly files: NornFileCoordinator;
|
|
31
|
+
},
|
|
32
|
+
) {}
|
|
31
33
|
|
|
32
|
-
static async load(
|
|
34
|
+
static async load(
|
|
35
|
+
manifestPath: string,
|
|
36
|
+
files: NornFileCoordinator,
|
|
37
|
+
): Promise<NornRunLogger> {
|
|
33
38
|
const manifest = parseManifest(await files.readText(manifestPath));
|
|
34
39
|
const logger = new NornRunLogger({ manifestPath, manifest, files });
|
|
35
40
|
logger.isInitialized = true;
|
|
36
41
|
return logger;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
async record(event: {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
async record(event: {
|
|
45
|
+
readonly type: string;
|
|
46
|
+
readonly [key: string]: unknown;
|
|
47
|
+
}): Promise<void> {
|
|
48
|
+
await this.input.files.withExclusiveLock(
|
|
49
|
+
this.input.manifestPath,
|
|
50
|
+
async (path) => {
|
|
51
|
+
let manifest: NornRunManifest;
|
|
52
|
+
try {
|
|
53
|
+
manifest = parseManifest(await readFile(path, "utf8"));
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (
|
|
56
|
+
this.isInitialized ||
|
|
57
|
+
!isNodeError(error) ||
|
|
58
|
+
error.code !== "ENOENT"
|
|
59
|
+
)
|
|
60
|
+
throw error;
|
|
61
|
+
manifest = { ...this.input.manifest, events: [] };
|
|
62
|
+
}
|
|
63
|
+
if (manifest.id !== this.input.manifest.id)
|
|
64
|
+
throw new Error("Run manifest identity changed");
|
|
65
|
+
await writeJsonAtomically(path, {
|
|
66
|
+
...manifest,
|
|
67
|
+
events: [
|
|
68
|
+
...manifest.events,
|
|
69
|
+
{ ...event, at: new Date().toISOString() },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
this.isInitialized = true;
|
|
73
|
+
},
|
|
74
|
+
);
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
77
|
|
|
55
78
|
function parseManifest(content: string): NornRunManifest {
|
|
56
79
|
const manifest = JSON.parse(content) as NornRunManifest;
|
|
57
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
!manifest ||
|
|
82
|
+
typeof manifest !== "object" ||
|
|
83
|
+
typeof manifest.id !== "string" ||
|
|
84
|
+
!Array.isArray(manifest.events)
|
|
85
|
+
)
|
|
86
|
+
throw new Error("Invalid run manifest");
|
|
58
87
|
return manifest;
|
|
59
88
|
}
|
|
@@ -1,22 +1,130 @@
|
|
|
1
1
|
const ADJECTIVES = [
|
|
2
|
-
"able",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"able",
|
|
3
|
+
"amber",
|
|
4
|
+
"brave",
|
|
5
|
+
"bright",
|
|
6
|
+
"calm",
|
|
7
|
+
"clever",
|
|
8
|
+
"crisp",
|
|
9
|
+
"daring",
|
|
10
|
+
"eager",
|
|
11
|
+
"fair",
|
|
12
|
+
"fast",
|
|
13
|
+
"fresh",
|
|
14
|
+
"gentle",
|
|
15
|
+
"golden",
|
|
16
|
+
"grand",
|
|
17
|
+
"happy",
|
|
18
|
+
"honest",
|
|
19
|
+
"jolly",
|
|
20
|
+
"kind",
|
|
21
|
+
"lively",
|
|
22
|
+
"lucky",
|
|
23
|
+
"merry",
|
|
24
|
+
"nimble",
|
|
25
|
+
"noble",
|
|
26
|
+
"patient",
|
|
27
|
+
"peaceful",
|
|
28
|
+
"proud",
|
|
29
|
+
"quiet",
|
|
30
|
+
"rapid",
|
|
31
|
+
"ready",
|
|
32
|
+
"royal",
|
|
33
|
+
"sharp",
|
|
34
|
+
"silent",
|
|
35
|
+
"silver",
|
|
36
|
+
"simple",
|
|
37
|
+
"steady",
|
|
38
|
+
"sunny",
|
|
39
|
+
"swift",
|
|
40
|
+
"tidy",
|
|
41
|
+
"vivid",
|
|
6
42
|
];
|
|
7
43
|
|
|
8
44
|
const MODIFIERS = [
|
|
9
|
-
"anchor",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
45
|
+
"anchor",
|
|
46
|
+
"autumn",
|
|
47
|
+
"brook",
|
|
48
|
+
"cedar",
|
|
49
|
+
"cloud",
|
|
50
|
+
"copper",
|
|
51
|
+
"dawn",
|
|
52
|
+
"ember",
|
|
53
|
+
"field",
|
|
54
|
+
"forest",
|
|
55
|
+
"harbor",
|
|
56
|
+
"hazel",
|
|
57
|
+
"hill",
|
|
58
|
+
"island",
|
|
59
|
+
"ivory",
|
|
60
|
+
"jade",
|
|
61
|
+
"lake",
|
|
62
|
+
"maple",
|
|
63
|
+
"meadow",
|
|
64
|
+
"moon",
|
|
65
|
+
"north",
|
|
66
|
+
"ocean",
|
|
67
|
+
"olive",
|
|
68
|
+
"pearl",
|
|
69
|
+
"pine",
|
|
70
|
+
"rain",
|
|
71
|
+
"river",
|
|
72
|
+
"rose",
|
|
73
|
+
"shadow",
|
|
74
|
+
"snow",
|
|
75
|
+
"solar",
|
|
76
|
+
"spring",
|
|
77
|
+
"stone",
|
|
78
|
+
"summer",
|
|
79
|
+
"thunder",
|
|
80
|
+
"timber",
|
|
81
|
+
"valley",
|
|
82
|
+
"violet",
|
|
83
|
+
"willow",
|
|
84
|
+
"winter",
|
|
13
85
|
];
|
|
14
86
|
|
|
15
87
|
const NOUNS = [
|
|
16
|
-
"badger",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
88
|
+
"badger",
|
|
89
|
+
"beacon",
|
|
90
|
+
"bison",
|
|
91
|
+
"canvas",
|
|
92
|
+
"comet",
|
|
93
|
+
"falcon",
|
|
94
|
+
"fjord",
|
|
95
|
+
"fox",
|
|
96
|
+
"garden",
|
|
97
|
+
"glade",
|
|
98
|
+
"grove",
|
|
99
|
+
"heron",
|
|
100
|
+
"lantern",
|
|
101
|
+
"lynx",
|
|
102
|
+
"marker",
|
|
103
|
+
"otter",
|
|
104
|
+
"panda",
|
|
105
|
+
"planet",
|
|
106
|
+
"prairie",
|
|
107
|
+
"raven",
|
|
108
|
+
"rocket",
|
|
109
|
+
"sail",
|
|
110
|
+
"signal",
|
|
111
|
+
"sparrow",
|
|
112
|
+
"summit",
|
|
113
|
+
"tiger",
|
|
114
|
+
"trail",
|
|
115
|
+
"voyage",
|
|
116
|
+
"whale",
|
|
117
|
+
"wolf",
|
|
118
|
+
"zephyr",
|
|
119
|
+
"orchid",
|
|
120
|
+
"bridge",
|
|
121
|
+
"castle",
|
|
122
|
+
"harvest",
|
|
123
|
+
"mirror",
|
|
124
|
+
"needle",
|
|
125
|
+
"quartz",
|
|
126
|
+
"ripple",
|
|
127
|
+
"signal",
|
|
20
128
|
];
|
|
21
129
|
|
|
22
130
|
export function generateRunName(existingNames: ReadonlySet<string>): string {
|