@ouro.bot/cli 0.1.0-alpha.71 → 0.1.0-alpha.73
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/changelog.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.73",
|
|
6
|
+
"changes": [
|
|
7
|
+
"New `ouro config models --agent <name>` command: list available models for the current provider. For github-copilot, queries the models API; other providers show a static message.",
|
|
8
|
+
"Fix: `ouro config model` now validates model availability for github-copilot before writing, showing available models if the requested one isn't found.",
|
|
9
|
+
"Fix: system prompt now tells the agent that model/provider changes take effect on the next turn automatically (no restart needed)."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.72",
|
|
14
|
+
"changes": [
|
|
15
|
+
"Fix: Compound shell commands (&&, ;, |, ||) are now checked per-subcommand for untrusted users instead of blanket-blocked. Safe compound commands like `ls && pwd` are allowed; only commands containing an untrusted subcommand are denied.",
|
|
16
|
+
"New `ouro config model --agent <name> <model-name>` command: change the active model for any provider. Reads provider from agent.json, updates the model field in secrets.json. Gated at friend trust level.",
|
|
17
|
+
"New `ouro friend update <id> --trust <level>` command: change an existing friend's trust level (stranger, acquaintance, friend, family). Gated at family trust level."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.71",
|
|
6
22
|
"changes": [
|
|
@@ -37,6 +37,7 @@ exports.readAgentConfigForAgent = readAgentConfigForAgent;
|
|
|
37
37
|
exports.writeAgentProviderSelection = writeAgentProviderSelection;
|
|
38
38
|
exports.loadAgentSecrets = loadAgentSecrets;
|
|
39
39
|
exports.writeProviderCredentials = writeProviderCredentials;
|
|
40
|
+
exports.writeAgentModel = writeAgentModel;
|
|
40
41
|
exports.collectRuntimeAuthCredentials = collectRuntimeAuthCredentials;
|
|
41
42
|
exports.resolveHatchCredentials = resolveHatchCredentials;
|
|
42
43
|
exports.runRuntimeAuthFlow = runRuntimeAuthFlow;
|
|
@@ -186,6 +187,26 @@ function writeProviderCredentials(agentName, provider, credentials, deps = {}) {
|
|
|
186
187
|
writeSecrets(secretsPath, secrets);
|
|
187
188
|
return { secretsPath, secrets };
|
|
188
189
|
}
|
|
190
|
+
const MODEL_FIELD = {
|
|
191
|
+
azure: "modelName",
|
|
192
|
+
minimax: "model",
|
|
193
|
+
anthropic: "model",
|
|
194
|
+
"openai-codex": "model",
|
|
195
|
+
"github-copilot": "model",
|
|
196
|
+
};
|
|
197
|
+
function writeAgentModel(agentName, modelName, deps = {}) {
|
|
198
|
+
const { config } = readAgentConfigForAgent(agentName, deps.bundlesRoot);
|
|
199
|
+
const provider = config.provider;
|
|
200
|
+
const { secretsPath, secrets } = loadAgentSecrets(agentName, deps);
|
|
201
|
+
const providerSecrets = secrets.providers[provider];
|
|
202
|
+
/* v8 ignore next -- fallback: all known providers are in MODEL_FIELD @preserve */
|
|
203
|
+
const fieldName = MODEL_FIELD[provider] ?? "model";
|
|
204
|
+
/* v8 ignore next -- defensive: fieldName always exists in template @preserve */
|
|
205
|
+
const previousModel = providerSecrets[fieldName] ?? "";
|
|
206
|
+
providerSecrets[fieldName] = modelName;
|
|
207
|
+
writeSecrets(secretsPath, secrets);
|
|
208
|
+
return { secretsPath, provider, previousModel };
|
|
209
|
+
}
|
|
189
210
|
function readCodexAccessToken(homeDir) {
|
|
190
211
|
const authPath = path.join(homeDir, ".codex", "auth.json");
|
|
191
212
|
try {
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
37
|
+
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
37
38
|
exports.parseOuroCommand = parseOuroCommand;
|
|
38
39
|
exports.discoverExistingCredentials = discoverExistingCredentials;
|
|
39
40
|
exports.createDefaultOuroCliDeps = createDefaultOuroCliDeps;
|
|
@@ -269,6 +270,8 @@ function usage() {
|
|
|
269
270
|
" ouro [up]",
|
|
270
271
|
" ouro stop|down|status|logs|hatch",
|
|
271
272
|
" ouro -v|--version",
|
|
273
|
+
" ouro config model --agent <name> <model-name>",
|
|
274
|
+
" ouro config models --agent <name>",
|
|
272
275
|
" ouro auth --agent <name> [--provider <provider>]",
|
|
273
276
|
" ouro auth verify --agent <name> [--provider <provider>]",
|
|
274
277
|
" ouro auth switch --agent <name> --provider <provider>",
|
|
@@ -285,6 +288,7 @@ function usage() {
|
|
|
285
288
|
" ouro friend list [--agent <name>]",
|
|
286
289
|
" ouro friend show <id> [--agent <name>]",
|
|
287
290
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
291
|
+
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
288
292
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
289
293
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
290
294
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
@@ -486,7 +490,30 @@ async function verifyProviderCredentials(provider, providers, fetchImpl = fetch)
|
|
|
486
490
|
return "failed (no api key)";
|
|
487
491
|
return "ok";
|
|
488
492
|
}
|
|
489
|
-
|
|
493
|
+
async function listGithubCopilotModels(baseUrl, token, fetchImpl = fetch) {
|
|
494
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
495
|
+
const response = await fetchImpl(url, {
|
|
496
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
497
|
+
});
|
|
498
|
+
if (!response.ok) {
|
|
499
|
+
throw new Error(`model listing failed (HTTP ${response.status})`);
|
|
500
|
+
}
|
|
501
|
+
const body = await response.json();
|
|
502
|
+
/* v8 ignore start -- response shape handling: tested via config-models.test.ts @preserve */
|
|
503
|
+
const items = Array.isArray(body) ? body : (body?.data ?? []);
|
|
504
|
+
return items.map((item) => {
|
|
505
|
+
const rec = item;
|
|
506
|
+
const capabilities = Array.isArray(rec.capabilities)
|
|
507
|
+
? rec.capabilities.filter((c) => typeof c === "string")
|
|
508
|
+
: undefined;
|
|
509
|
+
return {
|
|
510
|
+
id: String(rec.id ?? rec.name ?? ""),
|
|
511
|
+
name: String(rec.name ?? rec.id ?? ""),
|
|
512
|
+
...(capabilities ? { capabilities } : {}),
|
|
513
|
+
};
|
|
514
|
+
});
|
|
515
|
+
/* v8 ignore stop */
|
|
516
|
+
}
|
|
490
517
|
function parseHatchCommand(args) {
|
|
491
518
|
let agentName;
|
|
492
519
|
let humanName;
|
|
@@ -760,12 +787,56 @@ function parseFriendCommand(args) {
|
|
|
760
787
|
...(agent ? { agent } : {}),
|
|
761
788
|
};
|
|
762
789
|
}
|
|
790
|
+
if (sub === "update") {
|
|
791
|
+
const friendId = rest[0];
|
|
792
|
+
if (!friendId)
|
|
793
|
+
throw new Error(`Usage: ouro friend update <id> --trust <level>`);
|
|
794
|
+
let trustLevel;
|
|
795
|
+
/* v8 ignore start -- flag parsing loop: tested via CLI parsing tests @preserve */
|
|
796
|
+
for (let i = 1; i < rest.length; i++) {
|
|
797
|
+
if (rest[i] === "--trust" && rest[i + 1]) {
|
|
798
|
+
trustLevel = rest[i + 1];
|
|
799
|
+
i += 1;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/* v8 ignore stop */
|
|
803
|
+
const VALID_TRUST_LEVELS = new Set(["stranger", "acquaintance", "friend", "family"]);
|
|
804
|
+
if (!trustLevel || !VALID_TRUST_LEVELS.has(trustLevel)) {
|
|
805
|
+
throw new Error(`Usage: ouro friend update <id> --trust <stranger|acquaintance|friend|family>`);
|
|
806
|
+
}
|
|
807
|
+
return {
|
|
808
|
+
kind: "friend.update",
|
|
809
|
+
friendId,
|
|
810
|
+
trustLevel: trustLevel,
|
|
811
|
+
...(agent ? { agent } : {}),
|
|
812
|
+
};
|
|
813
|
+
}
|
|
763
814
|
if (sub === "link")
|
|
764
815
|
return parseLinkCommand(rest, "friend.link");
|
|
765
816
|
if (sub === "unlink")
|
|
766
817
|
return parseLinkCommand(rest, "friend.unlink");
|
|
767
818
|
throw new Error(`Usage\n${usage()}`);
|
|
768
819
|
}
|
|
820
|
+
function parseConfigCommand(args) {
|
|
821
|
+
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
822
|
+
const [sub, ...rest] = cleaned;
|
|
823
|
+
if (!sub)
|
|
824
|
+
throw new Error(`Usage\n${usage()}`);
|
|
825
|
+
if (sub === "model") {
|
|
826
|
+
if (!agent)
|
|
827
|
+
throw new Error("--agent is required for config model");
|
|
828
|
+
const modelName = rest[0];
|
|
829
|
+
if (!modelName)
|
|
830
|
+
throw new Error(`Usage: ouro config model --agent <name> <model-name>`);
|
|
831
|
+
return { kind: "config.model", agent, modelName };
|
|
832
|
+
}
|
|
833
|
+
if (sub === "models") {
|
|
834
|
+
if (!agent)
|
|
835
|
+
throw new Error("--agent is required for config models");
|
|
836
|
+
return { kind: "config.models", agent };
|
|
837
|
+
}
|
|
838
|
+
throw new Error(`Usage\n${usage()}`);
|
|
839
|
+
}
|
|
769
840
|
function parseMcpCommand(args) {
|
|
770
841
|
const [sub, ...rest] = args;
|
|
771
842
|
if (!sub)
|
|
@@ -808,6 +879,8 @@ function parseOuroCommand(args) {
|
|
|
808
879
|
return parseReminderCommand(args.slice(1));
|
|
809
880
|
if (head === "friend")
|
|
810
881
|
return parseFriendCommand(args.slice(1));
|
|
882
|
+
if (head === "config")
|
|
883
|
+
return parseConfigCommand(args.slice(1));
|
|
811
884
|
if (head === "mcp")
|
|
812
885
|
return parseMcpCommand(args.slice(1));
|
|
813
886
|
if (head === "whoami") {
|
|
@@ -1447,6 +1520,19 @@ async function executeFriendCommand(command, store) {
|
|
|
1447
1520
|
});
|
|
1448
1521
|
return `created: ${id} (${command.name}, ${trustLevel})`;
|
|
1449
1522
|
}
|
|
1523
|
+
if (command.kind === "friend.update") {
|
|
1524
|
+
const current = await store.get(command.friendId);
|
|
1525
|
+
if (!current)
|
|
1526
|
+
return `friend not found: ${command.friendId}`;
|
|
1527
|
+
const now = new Date().toISOString();
|
|
1528
|
+
await store.put(command.friendId, {
|
|
1529
|
+
...current,
|
|
1530
|
+
trustLevel: command.trustLevel,
|
|
1531
|
+
role: command.trustLevel,
|
|
1532
|
+
updatedAt: now,
|
|
1533
|
+
});
|
|
1534
|
+
return `updated: ${command.friendId} → trust=${command.trustLevel}`;
|
|
1535
|
+
}
|
|
1450
1536
|
if (command.kind === "friend.link") {
|
|
1451
1537
|
const current = await store.get(command.friendId);
|
|
1452
1538
|
if (!current)
|
|
@@ -1673,7 +1759,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1673
1759
|
}
|
|
1674
1760
|
// ── friend subcommands (local, no daemon socket needed) ──
|
|
1675
1761
|
if (command.kind === "friend.list" || command.kind === "friend.show" || command.kind === "friend.create" ||
|
|
1676
|
-
command.kind === "friend.link" || command.kind === "friend.unlink") {
|
|
1762
|
+
command.kind === "friend.update" || command.kind === "friend.link" || command.kind === "friend.unlink") {
|
|
1677
1763
|
/* v8 ignore start -- production default: requires full identity setup @preserve */
|
|
1678
1764
|
let store = deps.friendStore;
|
|
1679
1765
|
if (!store) {
|
|
@@ -1740,6 +1826,70 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1740
1826
|
return message;
|
|
1741
1827
|
}
|
|
1742
1828
|
/* v8 ignore stop */
|
|
1829
|
+
// ── config models (local, no daemon socket needed) ──
|
|
1830
|
+
/* v8 ignore start -- config models: tested via daemon-cli.test.ts @preserve */
|
|
1831
|
+
if (command.kind === "config.models") {
|
|
1832
|
+
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
1833
|
+
const provider = config.provider;
|
|
1834
|
+
if (provider !== "github-copilot") {
|
|
1835
|
+
const message = `model listing not available for ${provider} — check provider documentation.`;
|
|
1836
|
+
deps.writeStdout(message);
|
|
1837
|
+
return message;
|
|
1838
|
+
}
|
|
1839
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1840
|
+
const ghConfig = secrets.providers["github-copilot"];
|
|
1841
|
+
if (!ghConfig.githubToken || !ghConfig.baseUrl) {
|
|
1842
|
+
throw new Error(`github-copilot credentials not configured. Run \`ouro auth --agent ${command.agent} --provider github-copilot\` first.`);
|
|
1843
|
+
}
|
|
1844
|
+
const fetchFn = deps.fetchImpl ?? fetch;
|
|
1845
|
+
const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
|
|
1846
|
+
if (models.length === 0) {
|
|
1847
|
+
const message = "no models found";
|
|
1848
|
+
deps.writeStdout(message);
|
|
1849
|
+
return message;
|
|
1850
|
+
}
|
|
1851
|
+
const lines = ["available models:"];
|
|
1852
|
+
for (const m of models) {
|
|
1853
|
+
const caps = m.capabilities?.length ? ` (${m.capabilities.join(", ")})` : "";
|
|
1854
|
+
lines.push(` ${m.id}${caps}`);
|
|
1855
|
+
}
|
|
1856
|
+
const message = lines.join("\n");
|
|
1857
|
+
deps.writeStdout(message);
|
|
1858
|
+
return message;
|
|
1859
|
+
}
|
|
1860
|
+
/* v8 ignore stop */
|
|
1861
|
+
// ── config model (local, no daemon socket needed) ──
|
|
1862
|
+
/* v8 ignore start -- config model: tested via daemon-cli.test.ts @preserve */
|
|
1863
|
+
if (command.kind === "config.model") {
|
|
1864
|
+
// Validate model availability for github-copilot before writing
|
|
1865
|
+
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
1866
|
+
if (config.provider === "github-copilot") {
|
|
1867
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1868
|
+
const ghConfig = secrets.providers["github-copilot"];
|
|
1869
|
+
if (ghConfig.githubToken && ghConfig.baseUrl) {
|
|
1870
|
+
const fetchFn = deps.fetchImpl ?? fetch;
|
|
1871
|
+
try {
|
|
1872
|
+
const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
|
|
1873
|
+
const available = models.map((m) => m.id);
|
|
1874
|
+
if (available.length > 0 && !available.includes(command.modelName)) {
|
|
1875
|
+
const message = `model '${command.modelName}' not found. available models:\n${available.map((id) => ` ${id}`).join("\n")}`;
|
|
1876
|
+
deps.writeStdout(message);
|
|
1877
|
+
return message;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
catch {
|
|
1881
|
+
// Validation failed — fall through and write anyway
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, command.modelName);
|
|
1886
|
+
const message = previousModel
|
|
1887
|
+
? `updated ${command.agent} model on ${provider}: ${previousModel} → ${command.modelName}`
|
|
1888
|
+
: `set ${command.agent} model on ${provider}: ${command.modelName}`;
|
|
1889
|
+
deps.writeStdout(message);
|
|
1890
|
+
return message;
|
|
1891
|
+
}
|
|
1892
|
+
/* v8 ignore stop */
|
|
1743
1893
|
// ── whoami (local, no daemon socket needed) ──
|
|
1744
1894
|
if (command.kind === "whoami") {
|
|
1745
1895
|
if (command.agent) {
|
package/dist/mind/prompt.js
CHANGED
|
@@ -178,14 +178,19 @@ my bones give me the \`ouro\` cli. always pass \`--agent ${agentName}\`:
|
|
|
178
178
|
ouro task update --agent ${agentName} <id> <status>
|
|
179
179
|
ouro friend list --agent ${agentName}
|
|
180
180
|
ouro friend show --agent ${agentName} <id>
|
|
181
|
+
ouro friend update --agent ${agentName} <id> --trust <level>
|
|
181
182
|
ouro session list --agent ${agentName}
|
|
182
183
|
ouro reminder create --agent ${agentName} <title> --body <body>
|
|
184
|
+
ouro config model --agent ${agentName} <model-name>
|
|
185
|
+
ouro config models --agent ${agentName}
|
|
183
186
|
ouro auth --agent ${agentName} --provider <provider>
|
|
184
187
|
ouro auth verify --agent ${agentName} [--provider <provider>]
|
|
185
188
|
ouro auth switch --agent ${agentName} --provider <provider>
|
|
186
189
|
ouro mcp list --agent ${agentName}
|
|
187
190
|
ouro mcp call --agent ${agentName} <server> <tool> --args '{...}'
|
|
188
|
-
ouro --help
|
|
191
|
+
ouro --help
|
|
192
|
+
|
|
193
|
+
provider/model changes via \`ouro config model\` or \`ouro auth switch\` take effect on the next turn automatically — no restart needed.`;
|
|
189
194
|
}
|
|
190
195
|
function mcpToolsSection(mcpManager) {
|
|
191
196
|
if (!mcpManager)
|
|
@@ -48,7 +48,6 @@ const REASONS = {
|
|
|
48
48
|
readBeforeOverwrite: "i need to read that file first before i can overwrite it.",
|
|
49
49
|
protectedPath: "that path is protected — i can read it but not modify it.",
|
|
50
50
|
destructiveCommand: "that command is too dangerous to run — it could cause irreversible damage.",
|
|
51
|
-
compoundCommand: "i can only run simple commands for you — no chaining with && or ;",
|
|
52
51
|
// Trust reasons (vary by relationship)
|
|
53
52
|
needsTrust: "i'd need a closer friend to vouch for you before i can do that.",
|
|
54
53
|
needsTrustForWrite: "i'd need a closer friend to vouch for you before i can write files outside my home.",
|
|
@@ -90,9 +89,6 @@ function splitShellCommands(command) {
|
|
|
90
89
|
return [command];
|
|
91
90
|
return command.split(COMPOUND_SEPARATORS).filter(Boolean);
|
|
92
91
|
}
|
|
93
|
-
function isCompoundCommand(command) {
|
|
94
|
-
return SUBSHELL_PATTERN.test(command) || splitShellCommands(command).length > 1;
|
|
95
|
-
}
|
|
96
92
|
// --- shell commands that write to protected paths ---
|
|
97
93
|
function shellWritesToProtectedPath(command) {
|
|
98
94
|
const redirectMatch = command.match(/>\s*(\S+)/);
|
|
@@ -169,7 +165,10 @@ exports.OURO_CLI_TRUST_MANIFEST = {
|
|
|
169
165
|
"friend list": "friend",
|
|
170
166
|
"friend show": "friend",
|
|
171
167
|
"friend create": "friend",
|
|
168
|
+
"friend update": "family",
|
|
172
169
|
"reminder create": "friend",
|
|
170
|
+
"config model": "friend",
|
|
171
|
+
"config models": "friend",
|
|
173
172
|
"mcp list": "acquaintance",
|
|
174
173
|
"mcp call": "friend",
|
|
175
174
|
auth: "family",
|
|
@@ -234,11 +233,21 @@ function checkSingleShellCommandTrust(command, trustLevel) {
|
|
|
234
233
|
return deny(REASONS.needsTrust);
|
|
235
234
|
}
|
|
236
235
|
function checkShellTrustGuardrails(command, trustLevel) {
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
return
|
|
241
|
-
|
|
236
|
+
// Subshell patterns ($(), backticks) can't be reliably split — check as single command
|
|
237
|
+
/* v8 ignore next -- subshell branch: tested via guardrails.test.ts @preserve */
|
|
238
|
+
if (SUBSHELL_PATTERN.test(command)) {
|
|
239
|
+
return checkSingleShellCommandTrust(command, trustLevel);
|
|
240
|
+
}
|
|
241
|
+
// Compound commands: check each subcommand individually
|
|
242
|
+
const subcommands = splitShellCommands(command);
|
|
243
|
+
if (subcommands.length === 0)
|
|
244
|
+
return checkSingleShellCommandTrust(command, trustLevel);
|
|
245
|
+
for (const sub of subcommands) {
|
|
246
|
+
const result = checkSingleShellCommandTrust(sub, trustLevel);
|
|
247
|
+
if (!result.allowed)
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
return allow;
|
|
242
251
|
}
|
|
243
252
|
function checkWriteTrustGuardrails(toolName, args, context) {
|
|
244
253
|
if (toolName !== "write_file" && toolName !== "edit_file")
|
package/dist/repertoire/tools.js
CHANGED
|
@@ -103,7 +103,7 @@ async function execTool(name, args, ctx) {
|
|
|
103
103
|
event: "tool.start",
|
|
104
104
|
component: "tools",
|
|
105
105
|
message: "tool execution started",
|
|
106
|
-
meta: { name },
|
|
106
|
+
meta: { name, ...(name === "shell" && args.command ? { command: args.command } : {}) },
|
|
107
107
|
});
|
|
108
108
|
// Look up from combined registry
|
|
109
109
|
const def = allDefinitions.find((d) => d.tool.function.name === name);
|