agent-lyceum 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/README.zh-TW.md +5 -3
- package/dist/cli.js +59 -52
- package/dist/doctor.js +1 -1
- package/dist/memory-tidy.d.ts +1 -1
- package/dist/memory-tidy.js +1 -1
- package/dist/status.d.ts +1 -1
- package/dist/status.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,15 +26,17 @@ Requires Node 20+, plus `claude` and/or `codex` on your `PATH` (already logged i
|
|
|
26
26
|
|---|---|---|
|
|
27
27
|
| Project | `init [-y]` | Create the home and the global agent library. [details](docs/commands.md#init) |
|
|
28
28
|
| Project | `project add <name> --dir <repo>` | Register a project. [details](docs/commands.md#project-add) |
|
|
29
|
-
| Project | `project
|
|
29
|
+
| Project | `project` / `project remove <name> [--purge]` | List or unregister projects. [details](docs/commands.md#project-and-project-remove) |
|
|
30
30
|
| Diagnose | `doctor [-p name] [--task-file f] [--resolved] [--json]` | Check config (with enforcement levels), git, the lock and runtime CLIs without running agents; `--resolved` prints every effective setting and its source. [details](docs/commands.md#doctor) |
|
|
31
31
|
| Diagnose | `status [-p name] [--monitor]` | Show agents, mail, the current wake-up and past runs (`--json`, `--monitor`). [details](docs/commands.md#status) |
|
|
32
32
|
| Run | `run ["task"] [--task-file f] [--agent a] [-p name]` | Give the task to the lead and run until done; with `--agent`, to one member alone. [details](docs/commands.md#run) |
|
|
33
33
|
| Run | `resume [run-id] [--answer] [-p name]` | Continue an interrupted or failed run; `--answer` first answers the questions it is waiting on. [details](docs/commands.md#resume) |
|
|
34
34
|
| Run | `clear <run-id> [-p name] [--dry-run] [--keep-worktrees]` / `clear --lock --force` | Delete a run with its task memory and worktrees; or remove the lock a crashed run left behind. [details](docs/commands.md#clear) |
|
|
35
35
|
| Run | `call <agent> ["task"] [--task-file f] [--dir d]` | Call one global agent on its own, outside any project; prints its answer. [details](docs/commands.md#call) |
|
|
36
|
-
| Run | `
|
|
37
|
-
| Run | `
|
|
36
|
+
| Run | `tidy [-p name] [--agent a] [--layer project\|global] [--dry-run]` | Have agents tidy their own memory (manual only; archives, never deletes). [details](docs/commands.md#tidy) |
|
|
37
|
+
| Run | `tidy --undo <timestamp> -p name --agent a` | Undo a tidy: move its archived files back. [details](docs/commands.md#tidy) |
|
|
38
|
+
|
|
39
|
+
`agent-lyceum --help` lists each command with a one-line summary; `agent-lyceum <command> --help` prints its full description and options.
|
|
38
40
|
|
|
39
41
|
Ctrl-C cancels a run cleanly and it can be continued with `resume` ([Stopping a run](docs/commands.md#stopping-a-run)). `run` and `resume` exit `0` only when the lead reported `completed`, `2` for partial or blocked, `1` for failed, `3` while waiting for your answers, `130` for cancelled ([Exit codes](docs/commands.md#exit-codes)). Without `-p`, the project is inferred from the current directory ([Choosing the project](docs/commands.md#choosing-the-project)).
|
|
40
42
|
|
package/README.zh-TW.md
CHANGED
|
@@ -26,15 +26,17 @@ npx agent-lyceum status
|
|
|
26
26
|
|---|---|---|
|
|
27
27
|
| 專案 | `init [-y]` | 建立 home 與全域 agent 庫。 [詳細](docs/commands.zh-TW.md#init) |
|
|
28
28
|
| 專案 | `project add <name> --dir <repo>` | 註冊專案。 [詳細](docs/commands.zh-TW.md#project-add) |
|
|
29
|
-
| 專案 | `project
|
|
29
|
+
| 專案 | `project` / `project remove <name> [--purge]` | 列出或取消註冊專案。 [詳細](docs/commands.zh-TW.md#project-and-project-remove) |
|
|
30
30
|
| 診斷 | `doctor [-p name] [--task-file f] [--resolved] [--json]` | 不執行 agent,檢查設定(含防護等級)、git、鎖與 runtime CLI;`--resolved` 印出每項生效設定與其來源。 [詳細](docs/commands.zh-TW.md#doctor) |
|
|
31
31
|
| 診斷 | `status [-p name] [--monitor]` | 顯示 agent、信件、目前執行與歷次 run(支援 `--json`、`--monitor`)。 [詳細](docs/commands.zh-TW.md#status) |
|
|
32
32
|
| 執行 | `run ["task"] [--task-file f] [--agent a] [-p name]` | 把任務交給 lead 並執行到結束;加 `--agent` 則只交給某個成員單獨做。 [詳細](docs/commands.zh-TW.md#run) |
|
|
33
33
|
| 執行 | `resume [run-id] [--answer] [-p name]` | 接續被中斷或失敗的 run;`--answer` 會先回答它等待中的問題。 [詳細](docs/commands.zh-TW.md#resume) |
|
|
34
34
|
| 執行 | `clear <run-id> [-p name] [--dry-run] [--keep-worktrees]` / `clear --lock --force` | 刪除 run 與其任務記憶、worktree;或移除當機 run 留下的專案鎖。 [詳細](docs/commands.zh-TW.md#clear) |
|
|
35
35
|
| 執行 | `call <agent> ["task"] [--task-file f] [--dir d]` | 單獨呼叫一個 global agent(不需專案),印出它的回答。 [詳細](docs/commands.zh-TW.md#call) |
|
|
36
|
-
| 執行 | `
|
|
37
|
-
| 執行 | `
|
|
36
|
+
| 執行 | `tidy [-p name] [--agent a] [--layer project\|global] [--dry-run]` | 讓 agent 整理自己的記憶(只能手動觸發;封存而非刪除)。 [詳細](docs/commands.zh-TW.md#tidy) |
|
|
37
|
+
| 執行 | `tidy --undo <時間戳> -p name --agent a` | 還原一次整理:把封存的檔案搬回。 [詳細](docs/commands.zh-TW.md#tidy) |
|
|
38
|
+
|
|
39
|
+
`agent-lyceum --help` 只列出每個指令的一行簡述;`agent-lyceum <指令> --help` 會印出該指令的完整說明與選項。
|
|
38
40
|
|
|
39
41
|
Ctrl-C 會乾淨地取消 run,之後可用 `resume` 接續([中止 run](docs/commands.zh-TW.md#中止-run))。`run`/`resume` 只有在 lead 回報 `completed` 時才 exit `0`;`partial`/`blocked` 為 `2`,`failed` 為 `1`,等待你回答時為 `3`,取消為 `130`([退出碼](docs/commands.zh-TW.md#退出碼))。未指定 `-p` 時,專案由目前目錄推斷([選擇專案](docs/commands.zh-TW.md#選擇專案))。
|
|
40
42
|
|
package/dist/cli.js
CHANGED
|
@@ -71,7 +71,17 @@ program
|
|
|
71
71
|
console.log(` kept ${path.relative(h, f)}`);
|
|
72
72
|
console.log(`\nNext: agent-lyceum project add <name> --dir <your-repo>`);
|
|
73
73
|
});
|
|
74
|
-
const project = program
|
|
74
|
+
const project = program
|
|
75
|
+
.command("project")
|
|
76
|
+
.summary("List registered projects, or add / remove one")
|
|
77
|
+
.description("Without a subcommand, list the registered projects.\n\nSubcommands:\n - add <name> --dir <repo>: register a project\n - remove <name> [--purge]: unregister one")
|
|
78
|
+
.action(() => {
|
|
79
|
+
const list = listProjects(home());
|
|
80
|
+
if (!list.length)
|
|
81
|
+
console.log("No projects registered.");
|
|
82
|
+
for (const p of list)
|
|
83
|
+
console.log(`${p.name} -> ${p.dir}`);
|
|
84
|
+
});
|
|
75
85
|
project
|
|
76
86
|
.command("add <name>")
|
|
77
87
|
.description("Register a project (config and context live in the home, not in the repo)")
|
|
@@ -85,16 +95,6 @@ project
|
|
|
85
95
|
fail(e.message);
|
|
86
96
|
}
|
|
87
97
|
});
|
|
88
|
-
project
|
|
89
|
-
.command("list")
|
|
90
|
-
.description("List registered projects")
|
|
91
|
-
.action(() => {
|
|
92
|
-
const list = listProjects(home());
|
|
93
|
-
if (!list.length)
|
|
94
|
-
console.log("No projects registered.");
|
|
95
|
-
for (const p of list)
|
|
96
|
-
console.log(`${p.name} -> ${p.dir}`);
|
|
97
|
-
});
|
|
98
98
|
project
|
|
99
99
|
.command("remove <name>")
|
|
100
100
|
.description("Unregister a project (keeps its context unless --purge)")
|
|
@@ -222,7 +222,8 @@ async function driveRun(pr, runDir, first, assumeDefaults) {
|
|
|
222
222
|
}
|
|
223
223
|
program
|
|
224
224
|
.command("run [task]")
|
|
225
|
-
.
|
|
225
|
+
.summary("Start the dispatcher with a task for the lead")
|
|
226
|
+
.description('Start the dispatcher: give the task to the lead.\n\nGive the task either way:\n - "<task>": as text\n - --task-file <path>: from a file\n\nWho gets it:\n - by default, the lead\n - --agent <name>: that one member alone')
|
|
226
227
|
.option("-p, --project <name>")
|
|
227
228
|
.option("--task-file <path>", "read the task from a file")
|
|
228
229
|
.option("--agent <name>", "run only this member of the project, alone: it cannot mail teammates or ask you, and its own `done` ends the run")
|
|
@@ -268,7 +269,8 @@ program
|
|
|
268
269
|
});
|
|
269
270
|
program
|
|
270
271
|
.command("resume [run-id]")
|
|
271
|
-
.
|
|
272
|
+
.summary("Continue an interrupted (or failed) run")
|
|
273
|
+
.description("Continue an interrupted (or failed) run: same run dir, sessions and round count; the task is not re-sent.\n\nWhich run:\n - without an id: the newest run that is not done or running\n - with an id: that run")
|
|
272
274
|
.option("-p, --project <name>")
|
|
273
275
|
.option("--answer", "the run is waiting for answers: open ask-reply.md in $EDITOR, check the answers, then continue")
|
|
274
276
|
.option("--no-edit", "with --answer: do not open the editor; check the file as it is")
|
|
@@ -347,9 +349,13 @@ function unlockProject(pr, force) {
|
|
|
347
349
|
}
|
|
348
350
|
program
|
|
349
351
|
.command("clear [run-id]")
|
|
350
|
-
.
|
|
351
|
-
"
|
|
352
|
-
"
|
|
352
|
+
.summary("Delete a run by id, or remove a stale run lock")
|
|
353
|
+
.description("Delete a task (run) by id: its run directory (state, log, result, mailboxes), its task memory and its agent worktrees, branches and snapshots.\n\n" +
|
|
354
|
+
"Long-term (global and project) memory is never touched.\n" +
|
|
355
|
+
"Refuses while the run is running, or while a worktree holds work that is not in the repo.\n\n" +
|
|
356
|
+
"With --lock (no run id) it does something else:\n" +
|
|
357
|
+
" - removes the run lock a crashed run left behind\n" +
|
|
358
|
+
" - needs --force to actually remove it")
|
|
353
359
|
.option("-p, --project <name>")
|
|
354
360
|
.option("--dry-run", "only list what would be deleted and what would be kept")
|
|
355
361
|
.option("--lock", "remove the project's run lock left behind by a crashed run (takes no run id; needs --force)")
|
|
@@ -412,7 +418,8 @@ program
|
|
|
412
418
|
});
|
|
413
419
|
program
|
|
414
420
|
.command("doctor")
|
|
415
|
-
.
|
|
421
|
+
.summary("Check the setup without running any agent")
|
|
422
|
+
.description("Check the setup without running any agent. It checks:\n - configuration and enforcement levels\n - git\n - the project lock\n - what each runtime CLI supports (read from its --version and --help)\n\nWith --resolved it checks nothing and instead prints:\n - every effective setting\n - where each one came from")
|
|
416
423
|
.option("-p, --project <name>")
|
|
417
424
|
.option("--task-file <path>", "also check a task file")
|
|
418
425
|
.option("--resolved", "print the merged settings (global library, project file, defaults) and their sources; runs no other check")
|
|
@@ -466,7 +473,8 @@ function printResolved(projectName, json) {
|
|
|
466
473
|
}
|
|
467
474
|
program
|
|
468
475
|
.command("call <agent> [task]")
|
|
469
|
-
.
|
|
476
|
+
.summary("Call one global-library agent on its own, outside any project")
|
|
477
|
+
.description("Call one agent of the global library on its own, outside any project.\n\n - it works in --dir (default: the current directory)\n - its final answer is printed\n - it cannot mail anyone or ask you")
|
|
470
478
|
.option("--task-file <path>", "read the task from a file")
|
|
471
479
|
.option("--dir <path>", "the directory the agent works in (default: the current directory)")
|
|
472
480
|
.action(async (agentName, task, opts) => {
|
|
@@ -499,7 +507,6 @@ program
|
|
|
499
507
|
fail(e instanceof ConfigError ? e.message : e.message);
|
|
500
508
|
}
|
|
501
509
|
});
|
|
502
|
-
const memoryCmd = program.command("memory").description("Look after the agents' long-term memory");
|
|
503
510
|
function layerOpt(v) {
|
|
504
511
|
if (v === undefined || v === "project")
|
|
505
512
|
return "project";
|
|
@@ -507,16 +514,44 @@ function layerOpt(v) {
|
|
|
507
514
|
return "global";
|
|
508
515
|
return fail(`--layer must be "project" or "global" (task memory is not tidied: \`clear\` deletes it).`);
|
|
509
516
|
}
|
|
510
|
-
|
|
517
|
+
program
|
|
511
518
|
.command("tidy")
|
|
512
|
-
.
|
|
519
|
+
.summary("Have agents tidy their own memory, or undo one tidy")
|
|
520
|
+
.description("Have agents tidy their own memory: merge duplicates, fix stale entries, archive what is obsolete (never deletes).\n\n" +
|
|
521
|
+
"Manual only; nothing triggers it automatically.\n\n" +
|
|
522
|
+
"With --undo <stamp> it tidies nothing and instead:\n" +
|
|
523
|
+
" - moves that tidy's archived files back (never overwrites)\n" +
|
|
524
|
+
" - re-adds them to MEMORY.md\n" +
|
|
525
|
+
" - needs --agent, and cannot be combined with --dry-run")
|
|
513
526
|
.option("-p, --project <name>")
|
|
514
|
-
.option("--agent <name>", "tidy only this agent (default: every agent)")
|
|
527
|
+
.option("--agent <name>", "tidy only this agent (default: every agent); required with --undo")
|
|
515
528
|
.option("--layer <layer>", "project (default) or global; global memory is shared by every project, so it is never tidied unless you ask")
|
|
516
529
|
.option("--dry-run", "only show the memory sizes and what changed in the project; wake nobody, change nothing")
|
|
530
|
+
.option("--undo <stamp>", "undo the tidy with this timestamp: move its archived files back")
|
|
517
531
|
.action(async (opts) => {
|
|
518
532
|
try {
|
|
519
533
|
const layer = layerOpt(opts.layer);
|
|
534
|
+
if (opts.undo !== undefined) {
|
|
535
|
+
if (opts.dryRun)
|
|
536
|
+
fail("--undo cannot be combined with --dry-run.");
|
|
537
|
+
if (!opts.agent)
|
|
538
|
+
fail("--undo needs --agent <name>: an archive belongs to one agent's memory.");
|
|
539
|
+
const target = loadProject(opts.project);
|
|
540
|
+
const undoLease = takeLock(target, `restore-${tidyStamp()}`);
|
|
541
|
+
try {
|
|
542
|
+
const r = restoreMemory({ project: target, agent: opts.agent, layer, stamp: opts.undo });
|
|
543
|
+
for (const f of r.restored)
|
|
544
|
+
console.log(` restored ${f}`);
|
|
545
|
+
for (const s of r.skipped)
|
|
546
|
+
console.log(` skipped ${s.rel}: ${s.reason}`);
|
|
547
|
+
if (!r.restored.length && !r.skipped.length)
|
|
548
|
+
console.log(`Archive ${opts.undo} holds no files to restore.`);
|
|
549
|
+
}
|
|
550
|
+
finally {
|
|
551
|
+
undoLease.release();
|
|
552
|
+
}
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
520
555
|
const pr = loadProject(opts.project);
|
|
521
556
|
if (opts.dryRun) {
|
|
522
557
|
const dry = await tidyMemory({ project: pr, layer, agent: opts.agent, dryRun: true });
|
|
@@ -544,7 +579,7 @@ memoryCmd
|
|
|
544
579
|
console.log(`No agent has ${layer} memory configured. ${layer === "project" ? "The template gives agents global memory only: set `memory.project` for an agent in project.yaml (or tidy global memory with --layer global)." : "Set `memory.global` for an agent."}`);
|
|
545
580
|
for (const r of results) {
|
|
546
581
|
if (r.status === "tidied")
|
|
547
|
-
console.log(` ${r.agent} (${r.layer}): tidied. Archive: ${path.join(r.dir, ".archive", r.archive ?? "")} — undo with: agent-lyceum
|
|
582
|
+
console.log(` ${r.agent} (${r.layer}): tidied. Archive: ${path.join(r.dir, ".archive", r.archive ?? "")} — undo with: agent-lyceum tidy --undo ${r.archive} -p ${pr.name} --agent ${r.agent}${r.layer === "global" ? " --layer global" : ""}`);
|
|
548
583
|
else if (r.status === "skipped")
|
|
549
584
|
console.log(` ${r.agent} (${r.layer}): skipped — ${r.problems.join("; ")}`);
|
|
550
585
|
else if (r.status === "failed") {
|
|
@@ -559,34 +594,6 @@ memoryCmd
|
|
|
559
594
|
fail(e instanceof ConfigError ? e.message : e.message);
|
|
560
595
|
}
|
|
561
596
|
});
|
|
562
|
-
memoryCmd
|
|
563
|
-
.command("restore <stamp>")
|
|
564
|
-
.description("Move the files of one tidy's archive back (nothing that exists now is overwritten) and re-add them to MEMORY.md")
|
|
565
|
-
.option("-p, --project <name>")
|
|
566
|
-
.requiredOption("--agent <name>")
|
|
567
|
-
.option("--layer <layer>", "project (default) or global")
|
|
568
|
-
.action((stamp, opts) => {
|
|
569
|
-
try {
|
|
570
|
-
const layer = layerOpt(opts.layer);
|
|
571
|
-
const pr = loadProject(opts.project);
|
|
572
|
-
const lease = takeLock(pr, `restore-${tidyStamp()}`);
|
|
573
|
-
try {
|
|
574
|
-
const r = restoreMemory({ project: pr, agent: opts.agent, layer, stamp });
|
|
575
|
-
for (const f of r.restored)
|
|
576
|
-
console.log(` restored ${f}`);
|
|
577
|
-
for (const s of r.skipped)
|
|
578
|
-
console.log(` skipped ${s.rel}: ${s.reason}`);
|
|
579
|
-
if (!r.restored.length && !r.skipped.length)
|
|
580
|
-
console.log(`Archive ${stamp} holds no files to restore.`);
|
|
581
|
-
}
|
|
582
|
-
finally {
|
|
583
|
-
lease.release();
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
catch (e) {
|
|
587
|
-
fail(e.message);
|
|
588
|
-
}
|
|
589
|
-
});
|
|
590
597
|
function useColor() {
|
|
591
598
|
if (process.env.NO_COLOR)
|
|
592
599
|
return false;
|
|
@@ -657,7 +664,7 @@ program
|
|
|
657
664
|
const HELP_LAYERS = {
|
|
658
665
|
"Project:": ["init", "project"],
|
|
659
666
|
"Diagnose:": ["doctor", "status"],
|
|
660
|
-
"Run:": ["run", "resume", "clear", "call", "
|
|
667
|
+
"Run:": ["run", "resume", "clear", "call", "tidy"],
|
|
661
668
|
};
|
|
662
669
|
for (const [heading, names] of Object.entries(HELP_LAYERS))
|
|
663
670
|
for (const c of program.commands)
|
package/dist/doctor.js
CHANGED
|
@@ -134,7 +134,7 @@ export async function diagnoseProject(project, opts = {}) {
|
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
136
|
for (const a of tidyAdvice(project))
|
|
137
|
-
checks.push({ level: "info", subject: "memory", message: `${a.agent}: ${a.reason}. Consider: agent-lyceum
|
|
137
|
+
checks.push({ level: "info", subject: "memory", message: `${a.agent}: ${a.reason}. Consider: agent-lyceum tidy -p ${project.name} --agent ${a.agent} (doctor only suggests; it never tidies).` });
|
|
138
138
|
const runtimes = await probeProjectRuntimes(project, opts);
|
|
139
139
|
checks.push(...judgeRuntimes(project, runtimes));
|
|
140
140
|
checks.push({ level: "info", subject: "login", message: "Login status is unknown: agent-lyceum does not read credentials, and the CLIs offer no read-only login check it can rely on." });
|
package/dist/memory-tidy.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export interface TidyResult {
|
|
|
88
88
|
}
|
|
89
89
|
/** Scratch space of one tidy (snapshot and the invocation's logs); outside every memory dir and every run. */
|
|
90
90
|
export declare const tidyWorkDir: (project: ResolvedProject, stamp: string, agent: string) => string;
|
|
91
|
-
/** Tidy the memory of the chosen agents, one after another. Only ever called by a person (`agent-lyceum
|
|
91
|
+
/** Tidy the memory of the chosen agents, one after another. Only ever called by a person (`agent-lyceum tidy`). */
|
|
92
92
|
export declare function tidyMemory(o: TidyOptions): Promise<TidyResult[]>;
|
|
93
93
|
export interface RestoreResult {
|
|
94
94
|
restored: string[];
|
package/dist/memory-tidy.js
CHANGED
|
@@ -255,7 +255,7 @@ async function tidyOne(o, t, stamp) {
|
|
|
255
255
|
writeTidyState(t.dir, { ...(changes?.head ? { head: changes.head } : {}), at: (o.now?.() ?? new Date()).toISOString(), archive: stamp });
|
|
256
256
|
return { ...base, status: "tidied", archive: stamp, problems: [] };
|
|
257
257
|
}
|
|
258
|
-
/** Tidy the memory of the chosen agents, one after another. Only ever called by a person (`agent-lyceum
|
|
258
|
+
/** Tidy the memory of the chosen agents, one after another. Only ever called by a person (`agent-lyceum tidy`). */
|
|
259
259
|
export async function tidyMemory(o) {
|
|
260
260
|
const say = o.say ?? ((s) => console.log(s));
|
|
261
261
|
const targets = targetsFor(o.project, { layer: o.layer, agent: o.agent });
|
package/dist/status.d.ts
CHANGED
|
@@ -110,7 +110,7 @@ export interface StatusReport {
|
|
|
110
110
|
};
|
|
111
111
|
agents: AgentReport[];
|
|
112
112
|
run?: RunReport;
|
|
113
|
-
/** Hints that a `
|
|
113
|
+
/** Hints that a `tidy` might help. Only a suggestion; nothing runs it. */
|
|
114
114
|
memory_advice?: string[];
|
|
115
115
|
}
|
|
116
116
|
export interface TaskListReport {
|
package/dist/status.js
CHANGED
|
@@ -430,6 +430,6 @@ export function formatStatusReport(report, now = Date.now(), color = false) {
|
|
|
430
430
|
if (run)
|
|
431
431
|
lines.push("", `輸出 tokens:${tokens(run.output_tokens)}`);
|
|
432
432
|
if (report.memory_advice?.length)
|
|
433
|
-
lines.push("", `記憶:${report.memory_advice.join(";")}(建議執行 agent-lyceum
|
|
433
|
+
lines.push("", `記憶:${report.memory_advice.join(";")}(建議執行 agent-lyceum tidy -p ${report.project.name};status 只提示,不會自動整理)`);
|
|
434
434
|
return lines.join("\n");
|
|
435
435
|
}
|