llm-cli-gateway 2.2.0 → 2.4.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/CHANGELOG.md +86 -10
- package/README.md +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.js +84 -0
- package/dist/executor.js +17 -21
- package/dist/flight-recorder.d.ts +2 -1
- package/dist/index.d.ts +26 -6
- package/dist/index.js +771 -55
- package/dist/metrics.d.ts +3 -3
- package/dist/metrics.js +8 -8
- package/dist/request-helpers.d.ts +8 -8
- package/dist/resources.js +56 -7
- package/dist/session-manager-pg.d.ts +6 -6
- package/dist/session-manager-pg.js +1 -0
- package/dist/session-manager.d.ts +16 -12
- package/dist/session-manager.js +4 -1
- package/dist/upstream-contracts.d.ts +84 -0
- package/dist/upstream-contracts.js +698 -6
- package/dist/validation-tools.js +61 -1
- package/dist/xai-api-provider.d.ts +43 -0
- package/dist/xai-api-provider.js +191 -0
- package/migrations/001_initial_schema.sql +65 -0
- package/migrations/002_session_ids_as_text.sql +26 -0
- package/migrations/003_provider_type_sessions.sql +20 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
|
@@ -10,6 +10,34 @@ const PERMISSION_MODES = [
|
|
|
10
10
|
"bypassPermissions",
|
|
11
11
|
];
|
|
12
12
|
const EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
13
|
+
function scFlag(name, arity = "optional") {
|
|
14
|
+
return [
|
|
15
|
+
name,
|
|
16
|
+
{
|
|
17
|
+
arity,
|
|
18
|
+
description: `Subcommand-local ${name} option tracked for help-surface drift only`,
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
function scFlags(flags, arityOverrides = {}) {
|
|
23
|
+
return Object.fromEntries(flags.map(flag => scFlag(flag, arityOverrides[flag] ?? "optional")));
|
|
24
|
+
}
|
|
25
|
+
function subcommand(commandPath, summary, risk, flags = [], options = {}) {
|
|
26
|
+
return {
|
|
27
|
+
commandPath,
|
|
28
|
+
helpArgs: [["--help"]],
|
|
29
|
+
flags: scFlags(flags, options.flagArities),
|
|
30
|
+
maxPositionals: options.maxPositionals ?? 0,
|
|
31
|
+
aliases: options.aliases ?? [],
|
|
32
|
+
children: options.children ?? {},
|
|
33
|
+
risk,
|
|
34
|
+
exposure: options.exposure ?? "tracked_only",
|
|
35
|
+
tier: options.tier ?? "catalog",
|
|
36
|
+
tokenCost: options.tokenCost ?? "small",
|
|
37
|
+
summary,
|
|
38
|
+
conformanceFixtures: options.fixtures ?? [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
13
41
|
export const UPSTREAM_CLI_CONTRACTS = {
|
|
14
42
|
claude: {
|
|
15
43
|
cli: "claude",
|
|
@@ -23,6 +51,57 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
23
51
|
watchCategories: ["flags", "output-formats", "permission-modes", "session-resume", "models"],
|
|
24
52
|
},
|
|
25
53
|
helpArgs: [["--help"]],
|
|
54
|
+
subcommands: {
|
|
55
|
+
doctor: subcommand(["doctor"], "Run Claude Code diagnostic checks.", "read_only", [], {
|
|
56
|
+
tier: "diagnostic",
|
|
57
|
+
}),
|
|
58
|
+
mcp: subcommand(["mcp"], "Manage Claude MCP server configuration.", "writes_local_config"),
|
|
59
|
+
plugin: subcommand(["plugin"], "Manage Claude plugins.", "writes_local_config", [], {
|
|
60
|
+
aliases: ["plugins"],
|
|
61
|
+
}),
|
|
62
|
+
plugins: subcommand(["plugins"], "Alias for Claude plugin management.", "writes_local_config", [], {
|
|
63
|
+
aliases: ["plugin"],
|
|
64
|
+
}),
|
|
65
|
+
agents: subcommand(["agents"], "Inspect and manage Claude agent definitions.", "writes_local_config", [
|
|
66
|
+
"--add-dir",
|
|
67
|
+
"--agent",
|
|
68
|
+
"--allow-dangerously-skip-permissions",
|
|
69
|
+
"--cwd",
|
|
70
|
+
"--dangerously-skip-permissions",
|
|
71
|
+
"--effort",
|
|
72
|
+
"--json",
|
|
73
|
+
"--mcp-config",
|
|
74
|
+
"--model",
|
|
75
|
+
"--permission-mode",
|
|
76
|
+
"--plugin-dir",
|
|
77
|
+
"--setting-sources",
|
|
78
|
+
"--settings",
|
|
79
|
+
"--strict-mcp-config",
|
|
80
|
+
], { tier: "inspect", flagArities: { "--json": "none", "--strict-mcp-config": "none" } }),
|
|
81
|
+
auth: subcommand(["auth"], "Manage Claude authentication state.", "auth", [], {
|
|
82
|
+
exposure: "not_exposed",
|
|
83
|
+
}),
|
|
84
|
+
project: subcommand(["project"], "Manage Claude project configuration.", "writes_local_config"),
|
|
85
|
+
update: subcommand(["update"], "Update the Claude Code binary.", "updates_binary", [], {
|
|
86
|
+
exposure: "not_exposed",
|
|
87
|
+
}),
|
|
88
|
+
upgrade: subcommand(["upgrade"], "Alias for updating the Claude Code binary.", "updates_binary", [], {
|
|
89
|
+
exposure: "not_exposed",
|
|
90
|
+
}),
|
|
91
|
+
install: subcommand(["install"], "Install Claude Code shell integrations.", "writes_local_config", ["--force"], {
|
|
92
|
+
exposure: "not_exposed",
|
|
93
|
+
flagArities: { "--force": "none" },
|
|
94
|
+
}),
|
|
95
|
+
"auto-mode": subcommand(["auto-mode"], "Configure Claude auto-mode behavior.", "writes_local_config"),
|
|
96
|
+
ultrareview: subcommand(["ultrareview"], "Run Claude ultrareview diagnostics.", "executes_agent", ["--json", "--timeout"], {
|
|
97
|
+
tier: "diagnostic",
|
|
98
|
+
tokenCost: "medium",
|
|
99
|
+
flagArities: { "--json": "none" },
|
|
100
|
+
}),
|
|
101
|
+
"setup-token": subcommand(["setup-token"], "Configure Claude setup token authentication.", "auth", [], {
|
|
102
|
+
exposure: "not_exposed",
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
26
105
|
maxPositionals: 0,
|
|
27
106
|
mcpTools: ["claude_request", "claude_request_async"],
|
|
28
107
|
mcpParameters: [
|
|
@@ -282,6 +361,206 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
282
361
|
["exec", "--help"],
|
|
283
362
|
["exec", "resume", "--help"],
|
|
284
363
|
],
|
|
364
|
+
subcommands: {
|
|
365
|
+
exec: subcommand(["exec"], "Run Codex in non-interactive execution mode.", "executes_agent", [
|
|
366
|
+
"--add-dir",
|
|
367
|
+
"--cd",
|
|
368
|
+
"--color",
|
|
369
|
+
"--config",
|
|
370
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
371
|
+
"--dangerously-bypass-hook-trust",
|
|
372
|
+
"--disable",
|
|
373
|
+
"--enable",
|
|
374
|
+
"--ephemeral",
|
|
375
|
+
"--ignore-rules",
|
|
376
|
+
"--ignore-user-config",
|
|
377
|
+
"--image",
|
|
378
|
+
"--json",
|
|
379
|
+
"--local-provider",
|
|
380
|
+
"--model",
|
|
381
|
+
"--oss",
|
|
382
|
+
"--output-last-message",
|
|
383
|
+
"--output-schema",
|
|
384
|
+
"--profile",
|
|
385
|
+
"--sandbox",
|
|
386
|
+
"--skip-git-repo-check",
|
|
387
|
+
"--strict-config",
|
|
388
|
+
"--version",
|
|
389
|
+
]),
|
|
390
|
+
review: subcommand(["review"], "Run Codex code review workflows.", "executes_agent", [
|
|
391
|
+
"--base",
|
|
392
|
+
"--commit",
|
|
393
|
+
"--config",
|
|
394
|
+
"--disable",
|
|
395
|
+
"--enable",
|
|
396
|
+
"--strict-config",
|
|
397
|
+
"--title",
|
|
398
|
+
"--uncommitted",
|
|
399
|
+
]),
|
|
400
|
+
login: subcommand(["login"], "Authenticate Codex CLI.", "auth", [
|
|
401
|
+
"--config",
|
|
402
|
+
"--device-auth",
|
|
403
|
+
"--disable",
|
|
404
|
+
"--enable",
|
|
405
|
+
"--with-access-token",
|
|
406
|
+
"--with-api-key",
|
|
407
|
+
], { exposure: "not_exposed" }),
|
|
408
|
+
logout: subcommand(["logout"], "Clear Codex authentication state.", "auth", ["--config", "--disable", "--enable"], { exposure: "not_exposed" }),
|
|
409
|
+
mcp: subcommand(["mcp"], "Manage Codex MCP configuration.", "writes_local_config", [
|
|
410
|
+
"--config",
|
|
411
|
+
"--disable",
|
|
412
|
+
"--enable",
|
|
413
|
+
]),
|
|
414
|
+
plugin: subcommand(["plugin"], "Manage Codex plugins.", "writes_local_config", [
|
|
415
|
+
"--config",
|
|
416
|
+
"--disable",
|
|
417
|
+
"--enable",
|
|
418
|
+
]),
|
|
419
|
+
"mcp-server": subcommand(["mcp-server"], "Start Codex MCP server mode.", "starts_server", ["--config", "--disable", "--enable", "--strict-config"], { exposure: "not_exposed" }),
|
|
420
|
+
"app-server": subcommand(["app-server"], "Start Codex app server mode.", "starts_server", [
|
|
421
|
+
"--analytics-default-enabled",
|
|
422
|
+
"--config",
|
|
423
|
+
"--disable",
|
|
424
|
+
"--enable",
|
|
425
|
+
"--listen",
|
|
426
|
+
"--stdio",
|
|
427
|
+
"--strict-config",
|
|
428
|
+
"--ws-audience",
|
|
429
|
+
"--ws-auth",
|
|
430
|
+
"--ws-issuer",
|
|
431
|
+
"--ws-max-clock-skew-seconds",
|
|
432
|
+
"--ws-shared-secret-file",
|
|
433
|
+
"--ws-token-file",
|
|
434
|
+
"--ws-token-sha256",
|
|
435
|
+
], { exposure: "not_exposed" }),
|
|
436
|
+
"remote-control": subcommand(["remote-control"], "Inspect or manage Codex remote control state.", "network", ["--config", "--disable", "--enable", "--json"]),
|
|
437
|
+
completion: subcommand(["completion"], "Generate Codex shell completions.", "read_only", ["--config", "--disable", "--enable"], { tier: "inspect" }),
|
|
438
|
+
update: subcommand(["update"], "Update the Codex CLI binary.", "updates_binary", ["--config", "--disable", "--enable"], { exposure: "not_exposed" }),
|
|
439
|
+
doctor: subcommand(["doctor"], "Run Codex diagnostic checks.", "read_only", [
|
|
440
|
+
"--all",
|
|
441
|
+
"--ascii",
|
|
442
|
+
"--config",
|
|
443
|
+
"--disable",
|
|
444
|
+
"--enable",
|
|
445
|
+
"--json",
|
|
446
|
+
"--no-color",
|
|
447
|
+
"--summary",
|
|
448
|
+
], { tier: "diagnostic" }),
|
|
449
|
+
sandbox: subcommand(["sandbox"], "Run or inspect Codex sandbox behavior.", "executes_agent", [
|
|
450
|
+
"--cd",
|
|
451
|
+
"--config",
|
|
452
|
+
"--disable",
|
|
453
|
+
"--enable",
|
|
454
|
+
"--include-managed-config",
|
|
455
|
+
"--permissions-profile",
|
|
456
|
+
"--profile",
|
|
457
|
+
], { exposure: "not_exposed" }),
|
|
458
|
+
debug: subcommand(["debug"], "Run Codex debugging utilities.", "read_only", ["--config", "--disable", "--enable"], { tier: "diagnostic" }),
|
|
459
|
+
apply: subcommand(["apply"], "Apply a Codex patch to the workspace.", "destructive", ["--config", "--disable", "--enable"], { exposure: "not_exposed" }),
|
|
460
|
+
resume: subcommand(["resume"], "Resume Codex sessions from the interactive CLI.", "executes_agent", [
|
|
461
|
+
"--add-dir",
|
|
462
|
+
"--all",
|
|
463
|
+
"--ask-for-approval",
|
|
464
|
+
"--cd",
|
|
465
|
+
"--config",
|
|
466
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
467
|
+
"--dangerously-bypass-hook-trust",
|
|
468
|
+
"--disable",
|
|
469
|
+
"--enable",
|
|
470
|
+
"--image",
|
|
471
|
+
"--include-non-interactive",
|
|
472
|
+
"--last",
|
|
473
|
+
"--local-provider",
|
|
474
|
+
"--model",
|
|
475
|
+
"--no-alt-screen",
|
|
476
|
+
"--oss",
|
|
477
|
+
"--profile",
|
|
478
|
+
"--remote",
|
|
479
|
+
"--remote-auth-token-env",
|
|
480
|
+
"--sandbox",
|
|
481
|
+
"--search",
|
|
482
|
+
"--strict-config",
|
|
483
|
+
"--version",
|
|
484
|
+
]),
|
|
485
|
+
archive: subcommand(["archive"], "Archive Codex session state.", "writes_local_config", [
|
|
486
|
+
"--add-dir",
|
|
487
|
+
"--cd",
|
|
488
|
+
"--config",
|
|
489
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
490
|
+
"--dangerously-bypass-hook-trust",
|
|
491
|
+
"--disable",
|
|
492
|
+
"--enable",
|
|
493
|
+
"--image",
|
|
494
|
+
"--local-provider",
|
|
495
|
+
"--model",
|
|
496
|
+
"--oss",
|
|
497
|
+
"--profile",
|
|
498
|
+
"--remote",
|
|
499
|
+
"--remote-auth-token-env",
|
|
500
|
+
"--sandbox",
|
|
501
|
+
"--strict-config",
|
|
502
|
+
], { exposure: "not_exposed" }),
|
|
503
|
+
unarchive: subcommand(["unarchive"], "Restore archived Codex session state.", "writes_local_config", [
|
|
504
|
+
"--add-dir",
|
|
505
|
+
"--cd",
|
|
506
|
+
"--config",
|
|
507
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
508
|
+
"--dangerously-bypass-hook-trust",
|
|
509
|
+
"--disable",
|
|
510
|
+
"--enable",
|
|
511
|
+
"--image",
|
|
512
|
+
"--local-provider",
|
|
513
|
+
"--model",
|
|
514
|
+
"--oss",
|
|
515
|
+
"--profile",
|
|
516
|
+
"--remote",
|
|
517
|
+
"--remote-auth-token-env",
|
|
518
|
+
"--sandbox",
|
|
519
|
+
"--strict-config",
|
|
520
|
+
], { exposure: "not_exposed" }),
|
|
521
|
+
fork: subcommand(["fork"], "Fork a Codex session.", "executes_agent", [
|
|
522
|
+
"--add-dir",
|
|
523
|
+
"--all",
|
|
524
|
+
"--ask-for-approval",
|
|
525
|
+
"--cd",
|
|
526
|
+
"--config",
|
|
527
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
528
|
+
"--dangerously-bypass-hook-trust",
|
|
529
|
+
"--disable",
|
|
530
|
+
"--enable",
|
|
531
|
+
"--image",
|
|
532
|
+
"--last",
|
|
533
|
+
"--local-provider",
|
|
534
|
+
"--model",
|
|
535
|
+
"--no-alt-screen",
|
|
536
|
+
"--oss",
|
|
537
|
+
"--profile",
|
|
538
|
+
"--remote",
|
|
539
|
+
"--remote-auth-token-env",
|
|
540
|
+
"--sandbox",
|
|
541
|
+
"--search",
|
|
542
|
+
"--strict-config",
|
|
543
|
+
"--version",
|
|
544
|
+
]),
|
|
545
|
+
cloud: subcommand(["cloud"], "Inspect or manage Codex cloud features.", "network", [
|
|
546
|
+
"--config",
|
|
547
|
+
"--disable",
|
|
548
|
+
"--enable",
|
|
549
|
+
"--version",
|
|
550
|
+
]),
|
|
551
|
+
"exec-server": subcommand(["exec-server"], "Start Codex exec server mode.", "starts_server", [
|
|
552
|
+
"--config",
|
|
553
|
+
"--disable",
|
|
554
|
+
"--enable",
|
|
555
|
+
"--environment-id",
|
|
556
|
+
"--listen",
|
|
557
|
+
"--name",
|
|
558
|
+
"--remote",
|
|
559
|
+
"--strict-config",
|
|
560
|
+
"--use-agent-identity-auth",
|
|
561
|
+
], { exposure: "not_exposed" }),
|
|
562
|
+
features: subcommand(["features"], "Inspect or configure Codex feature flags.", "writes_local_config", ["--config", "--disable", "--enable"]),
|
|
563
|
+
},
|
|
285
564
|
command: { requiredFirstArg: "exec", optionalSecondArg: "resume" },
|
|
286
565
|
maxPositionals: 1,
|
|
287
566
|
resumeMaxPositionals: 2,
|
|
@@ -504,6 +783,38 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
504
783
|
watchCategories: ["flags", "approval-modes", "output-formats", "session-resume"],
|
|
505
784
|
},
|
|
506
785
|
helpArgs: [["--help"]],
|
|
786
|
+
subcommands: {
|
|
787
|
+
mcp: subcommand(["mcp"], "Manage Gemini MCP server configuration.", "writes_local_config", ["--debug"], {
|
|
788
|
+
flagArities: { "--debug": "none" },
|
|
789
|
+
}),
|
|
790
|
+
extensions: subcommand(["extensions"], "Manage Gemini extensions.", "writes_local_config", ["--debug"], {
|
|
791
|
+
aliases: ["extension"],
|
|
792
|
+
flagArities: { "--debug": "none" },
|
|
793
|
+
}),
|
|
794
|
+
extension: subcommand(["extension"], "Alias for Gemini extension management.", "writes_local_config", ["--debug"], {
|
|
795
|
+
aliases: ["extensions"],
|
|
796
|
+
flagArities: { "--debug": "none" },
|
|
797
|
+
}),
|
|
798
|
+
skills: subcommand(["skills"], "Manage Gemini skills.", "writes_local_config", ["--debug"], {
|
|
799
|
+
aliases: ["skill"],
|
|
800
|
+
flagArities: { "--debug": "none" },
|
|
801
|
+
}),
|
|
802
|
+
skill: subcommand(["skill"], "Alias for Gemini skill management.", "writes_local_config", ["--debug"], {
|
|
803
|
+
aliases: ["skills"],
|
|
804
|
+
flagArities: { "--debug": "none" },
|
|
805
|
+
}),
|
|
806
|
+
hooks: subcommand(["hooks"], "Manage Gemini hooks.", "writes_local_config", ["--debug"], {
|
|
807
|
+
aliases: ["hook"],
|
|
808
|
+
flagArities: { "--debug": "none" },
|
|
809
|
+
}),
|
|
810
|
+
hook: subcommand(["hook"], "Alias for Gemini hook management.", "writes_local_config", ["--debug"], {
|
|
811
|
+
aliases: ["hooks"],
|
|
812
|
+
flagArities: { "--debug": "none" },
|
|
813
|
+
}),
|
|
814
|
+
gemma: subcommand(["gemma"], "Run Gemini Gemma local-model helper surfaces.", "executes_agent", ["--debug"], {
|
|
815
|
+
flagArities: { "--debug": "none" },
|
|
816
|
+
}),
|
|
817
|
+
},
|
|
507
818
|
maxPositionals: 0,
|
|
508
819
|
mcpTools: ["gemini_request", "gemini_request_async"],
|
|
509
820
|
mcpParameters: [
|
|
@@ -625,6 +936,88 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
625
936
|
watchCategories: ["flags", "permission-modes", "session-resume", "sandbox", "output-formats"],
|
|
626
937
|
},
|
|
627
938
|
helpArgs: [["--help"]],
|
|
939
|
+
subcommands: {
|
|
940
|
+
agent: subcommand(["agent"], "Run Grok agent service helpers.", "executes_agent", [
|
|
941
|
+
"--agent-profile",
|
|
942
|
+
"--always-approve",
|
|
943
|
+
"--cli-chat-proxy-base-url",
|
|
944
|
+
"--grok-ws-origin",
|
|
945
|
+
"--grok-ws-url",
|
|
946
|
+
"--leader",
|
|
947
|
+
"--leader-socket",
|
|
948
|
+
"--model",
|
|
949
|
+
"--no-leader",
|
|
950
|
+
"--reasoning-effort",
|
|
951
|
+
"--reauth",
|
|
952
|
+
"--xai-api-base-url",
|
|
953
|
+
], {
|
|
954
|
+
children: {
|
|
955
|
+
stdio: subcommand(["agent", "stdio"], "Run Grok agent stdio mode.", "starts_server", ["--leader-socket"], { exposure: "not_exposed" }),
|
|
956
|
+
headless: subcommand(["agent", "headless"], "Run Grok headless agent mode.", "executes_agent", ["--grok-ws-origin", "--grok-ws-url", "--leader-socket"]),
|
|
957
|
+
serve: subcommand(["agent", "serve"], "Start Grok agent server mode.", "starts_server", [
|
|
958
|
+
"--bind",
|
|
959
|
+
"--grok-ws-origin",
|
|
960
|
+
"--grok-ws-url",
|
|
961
|
+
"--leader-socket",
|
|
962
|
+
"--remote",
|
|
963
|
+
"--secret",
|
|
964
|
+
], { exposure: "not_exposed" }),
|
|
965
|
+
leader: subcommand(["agent", "leader"], "Start Grok agent leader mode.", "starts_server", [
|
|
966
|
+
"--grok-ws-origin",
|
|
967
|
+
"--grok-ws-url",
|
|
968
|
+
"--leader-socket",
|
|
969
|
+
"--no-auto-update",
|
|
970
|
+
"--no-exit-on-disconnect",
|
|
971
|
+
], { exposure: "not_exposed" }),
|
|
972
|
+
},
|
|
973
|
+
}),
|
|
974
|
+
completions: subcommand(["completions"], "Generate Grok shell completions.", "read_only", ["--leader-socket"], { tier: "inspect" }),
|
|
975
|
+
export: subcommand(["export"], "Export Grok session data.", "read_only", ["--clipboard", "--leader-socket"], { tier: "inspect" }),
|
|
976
|
+
import: subcommand(["import"], "Import Grok session data.", "writes_local_config", [
|
|
977
|
+
"--json",
|
|
978
|
+
"--leader-socket",
|
|
979
|
+
"--list",
|
|
980
|
+
]),
|
|
981
|
+
inspect: subcommand(["inspect"], "Inspect Grok local state.", "read_only", ["--json", "--leader-socket"], { tier: "inspect" }),
|
|
982
|
+
leader: subcommand(["leader"], "Manage Grok leader process.", "starts_server", ["--leader-socket"], {
|
|
983
|
+
exposure: "not_exposed",
|
|
984
|
+
}),
|
|
985
|
+
login: subcommand(["login"], "Authenticate Grok CLI.", "auth", ["--device-auth", "--leader-socket", "--oauth"], { exposure: "not_exposed" }),
|
|
986
|
+
logout: subcommand(["logout"], "Clear Grok authentication state.", "auth", ["--leader-socket"], {
|
|
987
|
+
exposure: "not_exposed",
|
|
988
|
+
}),
|
|
989
|
+
mcp: subcommand(["mcp"], "Manage Grok MCP configuration.", "writes_local_config", [
|
|
990
|
+
"--leader-socket",
|
|
991
|
+
]),
|
|
992
|
+
memory: subcommand(["memory"], "Manage Grok memory state.", "writes_local_config", [
|
|
993
|
+
"--leader-socket",
|
|
994
|
+
]),
|
|
995
|
+
models: subcommand(["models"], "Inspect Grok model catalog.", "network", ["--leader-socket"], {
|
|
996
|
+
tier: "diagnostic",
|
|
997
|
+
}),
|
|
998
|
+
plugin: subcommand(["plugin"], "Manage Grok plugins.", "writes_local_config", [
|
|
999
|
+
"--leader-socket",
|
|
1000
|
+
]),
|
|
1001
|
+
sessions: subcommand(["sessions"], "Inspect Grok sessions.", "read_only", ["--leader-socket"], {
|
|
1002
|
+
tier: "inspect",
|
|
1003
|
+
}),
|
|
1004
|
+
setup: subcommand(["setup"], "Configure Grok CLI local setup.", "writes_local_config", ["--leader-socket"], { exposure: "not_exposed" }),
|
|
1005
|
+
ssh: subcommand(["ssh"], "Manage Grok SSH integration.", "network", ["--leader-socket"]),
|
|
1006
|
+
trace: subcommand(["trace"], "Inspect Grok trace data.", "read_only", ["--json", "--leader-socket", "--local", "--output"], { tier: "diagnostic" }),
|
|
1007
|
+
update: subcommand(["update"], "Update the Grok CLI binary.", "updates_binary", [
|
|
1008
|
+
"--alpha",
|
|
1009
|
+
"--check",
|
|
1010
|
+
"--force-reinstall",
|
|
1011
|
+
"--json",
|
|
1012
|
+
"--leader-socket",
|
|
1013
|
+
"--stable",
|
|
1014
|
+
"--version",
|
|
1015
|
+
], { exposure: "not_exposed" }),
|
|
1016
|
+
version: subcommand(["version"], "Print Grok version information.", "read_only", ["--json", "--leader-socket"], { tier: "diagnostic" }),
|
|
1017
|
+
worktree: subcommand(["worktree"], "Manage Grok worktree sessions.", "writes_local_config", [
|
|
1018
|
+
"--leader-socket",
|
|
1019
|
+
]),
|
|
1020
|
+
},
|
|
628
1021
|
maxPositionals: 0,
|
|
629
1022
|
mcpTools: ["grok_request", "grok_request_async"],
|
|
630
1023
|
mcpParameters: [
|
|
@@ -931,6 +1324,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
931
1324
|
watchCategories: ["flags", "agent-modes", "session-logging", "output-formats", "env-model"],
|
|
932
1325
|
},
|
|
933
1326
|
helpArgs: [["--help"]],
|
|
1327
|
+
subcommands: {},
|
|
934
1328
|
maxPositionals: 0,
|
|
935
1329
|
mcpTools: ["mistral_request", "mistral_request_async"],
|
|
936
1330
|
mcpParameters: [
|
|
@@ -1232,6 +1626,210 @@ export function assertUpstreamCliArgs(cli, args) {
|
|
|
1232
1626
|
throw new Error(`Upstream ${cli} CLI contract violation: ${details}`);
|
|
1233
1627
|
}
|
|
1234
1628
|
}
|
|
1629
|
+
function subcommandKey(commandPath) {
|
|
1630
|
+
return commandPath.join(" ");
|
|
1631
|
+
}
|
|
1632
|
+
function subcommandResourceUri(cli, commandPath) {
|
|
1633
|
+
return `provider-subcommands://${cli}/${commandPath.map(encodeURIComponent).join("/")}`;
|
|
1634
|
+
}
|
|
1635
|
+
export function flattenCliSubcommands(subcommands) {
|
|
1636
|
+
const flattened = [];
|
|
1637
|
+
const visit = (node) => {
|
|
1638
|
+
flattened.push(node);
|
|
1639
|
+
for (const child of Object.values(node.children ?? {}))
|
|
1640
|
+
visit(child);
|
|
1641
|
+
};
|
|
1642
|
+
for (const node of Object.values(subcommands ?? {}))
|
|
1643
|
+
visit(node);
|
|
1644
|
+
return flattened.sort((a, b) => subcommandKey(a.commandPath).localeCompare(subcommandKey(b.commandPath)));
|
|
1645
|
+
}
|
|
1646
|
+
export function getCliSubcommandContract(cli, commandPath) {
|
|
1647
|
+
const wanted = subcommandKey(commandPath);
|
|
1648
|
+
return (flattenCliSubcommands(UPSTREAM_CLI_CONTRACTS[cli].subcommands).find(contract => subcommandKey(contract.commandPath) === wanted) ?? null);
|
|
1649
|
+
}
|
|
1650
|
+
function serializeFlagContract(flag) {
|
|
1651
|
+
return {
|
|
1652
|
+
arity: flag.arity,
|
|
1653
|
+
values: flag.values ?? null,
|
|
1654
|
+
pattern: flag.pattern?.source ?? null,
|
|
1655
|
+
description: flag.description,
|
|
1656
|
+
hiddenFromHelp: flag.hiddenFromHelp ?? false,
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
export function serializeCliSubcommandContract(cli, contract) {
|
|
1660
|
+
return {
|
|
1661
|
+
provider: cli,
|
|
1662
|
+
commandPath: contract.commandPath,
|
|
1663
|
+
helpArgs: contract.helpArgs,
|
|
1664
|
+
flags: Object.fromEntries(Object.entries(contract.flags).map(([name, flag]) => [name, serializeFlagContract(flag)])),
|
|
1665
|
+
maxPositionals: contract.maxPositionals,
|
|
1666
|
+
aliases: contract.aliases ?? [],
|
|
1667
|
+
children: Object.values(contract.children ?? {}).map(child => ({
|
|
1668
|
+
commandPath: child.commandPath,
|
|
1669
|
+
summary: child.summary,
|
|
1670
|
+
resourceUri: subcommandResourceUri(cli, child.commandPath),
|
|
1671
|
+
})),
|
|
1672
|
+
risk: contract.risk,
|
|
1673
|
+
exposure: contract.exposure,
|
|
1674
|
+
tier: contract.tier,
|
|
1675
|
+
tokenCost: contract.tokenCost,
|
|
1676
|
+
summary: contract.summary,
|
|
1677
|
+
conformanceFixtures: contract.conformanceFixtures.map(fixture => ({
|
|
1678
|
+
id: fixture.id,
|
|
1679
|
+
description: fixture.description,
|
|
1680
|
+
expect: fixture.expect,
|
|
1681
|
+
})),
|
|
1682
|
+
resourceUri: subcommandResourceUri(cli, contract.commandPath),
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
export function listProviderSubcommands(options = {}) {
|
|
1686
|
+
const providers = options.provider
|
|
1687
|
+
? [options.provider]
|
|
1688
|
+
: Object.keys(UPSTREAM_CLI_CONTRACTS);
|
|
1689
|
+
const prefix = options.commandPathPrefix ?? [];
|
|
1690
|
+
const rows = [];
|
|
1691
|
+
for (const provider of providers) {
|
|
1692
|
+
for (const contract of flattenCliSubcommands(UPSTREAM_CLI_CONTRACTS[provider].subcommands)) {
|
|
1693
|
+
if (options.tier && contract.tier !== options.tier)
|
|
1694
|
+
continue;
|
|
1695
|
+
if (options.risk && contract.risk !== options.risk)
|
|
1696
|
+
continue;
|
|
1697
|
+
if (options.exposure && contract.exposure !== options.exposure)
|
|
1698
|
+
continue;
|
|
1699
|
+
if (prefix.length > 0 &&
|
|
1700
|
+
!prefix.every((part, index) => contract.commandPath[index] === part)) {
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
rows.push({
|
|
1704
|
+
provider,
|
|
1705
|
+
commandPath: contract.commandPath,
|
|
1706
|
+
aliases: contract.aliases ?? [],
|
|
1707
|
+
tier: contract.tier,
|
|
1708
|
+
risk: contract.risk,
|
|
1709
|
+
exposure: contract.exposure,
|
|
1710
|
+
tokenCost: contract.tokenCost,
|
|
1711
|
+
summary: contract.summary.length > 48
|
|
1712
|
+
? `${contract.summary.slice(0, 45).trimEnd()}...`
|
|
1713
|
+
: contract.summary,
|
|
1714
|
+
driftStatus: "unknown",
|
|
1715
|
+
resourceUri: subcommandResourceUri(provider, contract.commandPath),
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
return rows.sort((a, b) => `${a.provider}:${subcommandKey(a.commandPath)}`.localeCompare(`${b.provider}:${subcommandKey(b.commandPath)}`));
|
|
1720
|
+
}
|
|
1721
|
+
export function buildProviderSubcommandsCompactCatalog(options = {}) {
|
|
1722
|
+
return {
|
|
1723
|
+
schemaVersion: "provider-subcommands-catalog.v1",
|
|
1724
|
+
columns: [
|
|
1725
|
+
"provider",
|
|
1726
|
+
"commandPath",
|
|
1727
|
+
"aliases",
|
|
1728
|
+
"tier",
|
|
1729
|
+
"risk",
|
|
1730
|
+
"exposure",
|
|
1731
|
+
"tokenCost",
|
|
1732
|
+
"summary",
|
|
1733
|
+
"driftStatus",
|
|
1734
|
+
"resourceUri",
|
|
1735
|
+
],
|
|
1736
|
+
rows: listProviderSubcommands(options).map(row => [
|
|
1737
|
+
row.provider,
|
|
1738
|
+
row.commandPath.join(" "),
|
|
1739
|
+
row.aliases.join(","),
|
|
1740
|
+
row.tier,
|
|
1741
|
+
row.risk,
|
|
1742
|
+
row.exposure,
|
|
1743
|
+
row.tokenCost,
|
|
1744
|
+
row.summary,
|
|
1745
|
+
row.driftStatus,
|
|
1746
|
+
row.resourceUri,
|
|
1747
|
+
]),
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
export function validateUpstreamCliSubcommandArgs(cli, commandPath, args) {
|
|
1751
|
+
const contract = getCliSubcommandContract(cli, commandPath);
|
|
1752
|
+
const violations = [];
|
|
1753
|
+
if (!contract) {
|
|
1754
|
+
violations.push({
|
|
1755
|
+
cli,
|
|
1756
|
+
message: `${cli} subcommand "${subcommandKey(commandPath)}" is not declared in the upstream subcommand contract`,
|
|
1757
|
+
});
|
|
1758
|
+
return { ok: false, violations, commandPath };
|
|
1759
|
+
}
|
|
1760
|
+
const positionals = [];
|
|
1761
|
+
for (let i = 0; i < args.length; i++) {
|
|
1762
|
+
const arg = args[i];
|
|
1763
|
+
const flag = contract.flags[arg];
|
|
1764
|
+
if (!flag) {
|
|
1765
|
+
if (arg.startsWith("-")) {
|
|
1766
|
+
violations.push({
|
|
1767
|
+
cli,
|
|
1768
|
+
arg,
|
|
1769
|
+
index: i,
|
|
1770
|
+
message: `Unsupported ${cli} subcommand flag "${arg}" for ${subcommandKey(commandPath)}`,
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
else {
|
|
1774
|
+
positionals.push(arg);
|
|
1775
|
+
}
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
if (flag.arity === "none")
|
|
1779
|
+
continue;
|
|
1780
|
+
if (flag.arity === "one") {
|
|
1781
|
+
const value = args[i + 1];
|
|
1782
|
+
if (value === undefined) {
|
|
1783
|
+
violations.push({
|
|
1784
|
+
cli,
|
|
1785
|
+
arg,
|
|
1786
|
+
index: i,
|
|
1787
|
+
message: `${cli} subcommand flag "${arg}" requires one value`,
|
|
1788
|
+
});
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
validateFlagValue(cli, arg, flag, value, i + 1, violations);
|
|
1792
|
+
i += 1;
|
|
1793
|
+
continue;
|
|
1794
|
+
}
|
|
1795
|
+
if (flag.arity === "optional") {
|
|
1796
|
+
const value = args[i + 1];
|
|
1797
|
+
if (value !== undefined && !value.startsWith("-")) {
|
|
1798
|
+
validateFlagValue(cli, arg, flag, value, i + 1, violations);
|
|
1799
|
+
i += 1;
|
|
1800
|
+
}
|
|
1801
|
+
continue;
|
|
1802
|
+
}
|
|
1803
|
+
let consumed = 0;
|
|
1804
|
+
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
1805
|
+
validateFlagValue(cli, arg, flag, args[i + 1], i + 1, violations);
|
|
1806
|
+
i += 1;
|
|
1807
|
+
consumed += 1;
|
|
1808
|
+
}
|
|
1809
|
+
if (consumed === 0) {
|
|
1810
|
+
violations.push({
|
|
1811
|
+
cli,
|
|
1812
|
+
arg,
|
|
1813
|
+
index: i,
|
|
1814
|
+
message: `${cli} subcommand flag "${arg}" requires at least one value`,
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
if (positionals.length > contract.maxPositionals) {
|
|
1819
|
+
violations.push({
|
|
1820
|
+
cli,
|
|
1821
|
+
message: `${cli} subcommand "${subcommandKey(commandPath)}" has ${positionals.length} positional values; upstream subcommand contract allows ${contract.maxPositionals}`,
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
ok: violations.length === 0,
|
|
1826
|
+
violations,
|
|
1827
|
+
commandPath,
|
|
1828
|
+
risk: contract.risk,
|
|
1829
|
+
exposure: contract.exposure,
|
|
1830
|
+
tier: contract.tier,
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1235
1833
|
export function validateUpstreamCliEnv(cli, env) {
|
|
1236
1834
|
if (!env || Object.keys(env).length === 0)
|
|
1237
1835
|
return { ok: true, violations: [] };
|
|
@@ -1329,6 +1927,42 @@ export function computeFlagDrift(contract, helpText, discoveredFlags) {
|
|
|
1329
1927
|
}
|
|
1330
1928
|
return { missingFlags, extraFlags, acknowledgedExtraFlags, warnings };
|
|
1331
1929
|
}
|
|
1930
|
+
export function computeSubcommandFlagDrift(contract, executable, helpText, discoveredFlags) {
|
|
1931
|
+
const warnings = [];
|
|
1932
|
+
const missingFlags = [];
|
|
1933
|
+
for (const [flag, spec] of Object.entries(contract.flags)) {
|
|
1934
|
+
const inHelp = helpText.includes(flag);
|
|
1935
|
+
if (spec.hiddenFromHelp) {
|
|
1936
|
+
if (inHelp) {
|
|
1937
|
+
warnings.push(`${subcommandKey(contract.commandPath)} ${flag} is marked hiddenFromHelp but now appears in ${executable} help output; remove the hiddenFromHelp marker from the subcommand contract`);
|
|
1938
|
+
}
|
|
1939
|
+
continue;
|
|
1940
|
+
}
|
|
1941
|
+
if (!inHelp)
|
|
1942
|
+
missingFlags.push(flag);
|
|
1943
|
+
}
|
|
1944
|
+
const contractFlagSet = new Set(Object.keys(contract.flags));
|
|
1945
|
+
const acknowledged = new Set(contract.acknowledgedUpstreamFlags ?? []);
|
|
1946
|
+
const extraFlags = [];
|
|
1947
|
+
const acknowledgedExtraFlags = [];
|
|
1948
|
+
for (const flag of discoveredFlags) {
|
|
1949
|
+
if (contractFlagSet.has(flag))
|
|
1950
|
+
continue;
|
|
1951
|
+
if (acknowledged.has(flag)) {
|
|
1952
|
+
acknowledgedExtraFlags.push(flag);
|
|
1953
|
+
}
|
|
1954
|
+
else {
|
|
1955
|
+
extraFlags.push(flag);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
const discoveredSet = new Set(discoveredFlags);
|
|
1959
|
+
for (const flag of acknowledged) {
|
|
1960
|
+
if (!discoveredSet.has(flag)) {
|
|
1961
|
+
warnings.push(`acknowledged upstream subcommand flag ${flag} no longer appears in ${executable} ${subcommandKey(contract.commandPath)} help output; remove it from acknowledgedUpstreamFlags`);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return { missingFlags, extraFlags, acknowledgedExtraFlags, warnings };
|
|
1965
|
+
}
|
|
1332
1966
|
export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
|
|
1333
1967
|
const contract = UPSTREAM_CLI_CONTRACTS[cli];
|
|
1334
1968
|
const outputs = [];
|
|
@@ -1365,6 +1999,7 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
|
|
|
1365
1999
|
discoveredFlags: [],
|
|
1366
2000
|
helpHash: undefined,
|
|
1367
2001
|
versionHint: undefined,
|
|
2002
|
+
subcommands: {},
|
|
1368
2003
|
probedAt: new Date().toISOString(),
|
|
1369
2004
|
warnings: [result.error.message],
|
|
1370
2005
|
};
|
|
@@ -1381,6 +2016,7 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
|
|
|
1381
2016
|
const versionMatch = helpText.match(/^\s*(?:[A-Za-z][\w .-]+)?v?\d+\.\d+\S*/m);
|
|
1382
2017
|
const versionHint = versionMatch ? versionMatch[0].trim().slice(0, 80) : undefined;
|
|
1383
2018
|
const helpHash = createHash("sha256").update(helpText).digest("hex");
|
|
2019
|
+
const subcommands = probeInstalledCliSubcommands(cli, timeoutMs);
|
|
1384
2020
|
return {
|
|
1385
2021
|
cli,
|
|
1386
2022
|
executable: contract.executable,
|
|
@@ -1394,10 +2030,69 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
|
|
|
1394
2030
|
discoveredFlags,
|
|
1395
2031
|
helpHash,
|
|
1396
2032
|
versionHint,
|
|
2033
|
+
subcommands,
|
|
1397
2034
|
probedAt: new Date().toISOString(),
|
|
1398
2035
|
warnings,
|
|
1399
2036
|
};
|
|
1400
2037
|
}
|
|
2038
|
+
function probeInstalledCliSubcommands(cli, timeoutMs) {
|
|
2039
|
+
const contract = UPSTREAM_CLI_CONTRACTS[cli];
|
|
2040
|
+
const probes = {};
|
|
2041
|
+
for (const sub of flattenCliSubcommands(contract.subcommands)) {
|
|
2042
|
+
const outputs = [];
|
|
2043
|
+
const warnings = [];
|
|
2044
|
+
let available = true;
|
|
2045
|
+
const checkedHelpCommands = sub.helpArgs.map(helpArgs => [...sub.commandPath, ...helpArgs]);
|
|
2046
|
+
for (const helpArgs of sub.helpArgs) {
|
|
2047
|
+
const args = [...sub.commandPath, ...helpArgs];
|
|
2048
|
+
const extendedPath = getExtendedPath();
|
|
2049
|
+
const env = envWithExtendedPath(process.env, extendedPath);
|
|
2050
|
+
const resolved = resolveCommandForSpawn(contract.executable, args, {
|
|
2051
|
+
envPath: extendedPath,
|
|
2052
|
+
});
|
|
2053
|
+
const result = spawnSync(resolved.command, resolved.args, {
|
|
2054
|
+
encoding: "utf8",
|
|
2055
|
+
timeout: timeoutMs,
|
|
2056
|
+
maxBuffer: 1024 * 1024,
|
|
2057
|
+
env,
|
|
2058
|
+
windowsHide: true,
|
|
2059
|
+
windowsVerbatimArguments: resolved.windowsVerbatimArguments,
|
|
2060
|
+
});
|
|
2061
|
+
if (result.error) {
|
|
2062
|
+
available = false;
|
|
2063
|
+
warnings.push(result.error.message);
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
outputs.push(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
|
|
2067
|
+
if (result.status !== 0) {
|
|
2068
|
+
warnings.push(`${contract.executable} ${args.join(" ")} exited with status ${result.status}`);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
const helpText = outputs.join("\n");
|
|
2072
|
+
const discoveredFlags = available ? extractDiscoveredFlags(helpText) : [];
|
|
2073
|
+
const drift = available
|
|
2074
|
+
? computeSubcommandFlagDrift(sub, contract.executable, helpText, discoveredFlags)
|
|
2075
|
+
: { missingFlags: [], extraFlags: [], acknowledgedExtraFlags: [], warnings: [] };
|
|
2076
|
+
warnings.push(...drift.warnings);
|
|
2077
|
+
probes[subcommandKey(sub.commandPath)] = {
|
|
2078
|
+
commandPath: sub.commandPath,
|
|
2079
|
+
checkedHelpCommands,
|
|
2080
|
+
available,
|
|
2081
|
+
missingFlags: drift.missingFlags,
|
|
2082
|
+
extraFlags: drift.extraFlags,
|
|
2083
|
+
acknowledgedExtraFlags: drift.acknowledgedExtraFlags,
|
|
2084
|
+
discoveredFlags,
|
|
2085
|
+
helpHash: available ? createHash("sha256").update(helpText).digest("hex") : undefined,
|
|
2086
|
+
probedAt: new Date().toISOString(),
|
|
2087
|
+
warnings,
|
|
2088
|
+
risk: sub.risk,
|
|
2089
|
+
exposure: sub.exposure,
|
|
2090
|
+
tier: sub.tier,
|
|
2091
|
+
summary: sub.summary,
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
return probes;
|
|
2095
|
+
}
|
|
1401
2096
|
export function buildUpstreamContractReport(options = {}) {
|
|
1402
2097
|
const selected = options.cli ? [options.cli] : Object.keys(UPSTREAM_CLI_CONTRACTS);
|
|
1403
2098
|
const contracts = Object.fromEntries(selected.map(cli => {
|
|
@@ -1423,13 +2118,10 @@ export function buildUpstreamContractReport(options = {}) {
|
|
|
1423
2118
|
mcpParameters: contract.mcpParameters,
|
|
1424
2119
|
flags: Object.fromEntries(Object.entries(contract.flags).map(([name, flag]) => [
|
|
1425
2120
|
name,
|
|
1426
|
-
|
|
1427
|
-
arity: flag.arity,
|
|
1428
|
-
values: flag.values ?? null,
|
|
1429
|
-
pattern: flag.pattern?.source ?? null,
|
|
1430
|
-
description: flag.description,
|
|
1431
|
-
},
|
|
2121
|
+
serializeFlagContract(flag),
|
|
1432
2122
|
])),
|
|
2123
|
+
subcommandCount: flattenCliSubcommands(contract.subcommands).length,
|
|
2124
|
+
subcommandsCatalog: buildProviderSubcommandsCompactCatalog({ provider: cli }),
|
|
1433
2125
|
env: Object.fromEntries(Object.entries(contract.env ?? {}).map(([name, envContract]) => [
|
|
1434
2126
|
name,
|
|
1435
2127
|
{
|