lark-remote 0.1.8 → 0.1.10
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 +38 -0
- package/README.en.md +4 -4
- package/README.md +4 -4
- package/dist/bridge/approval-coordinator.d.ts +18 -0
- package/dist/bridge/approval-coordinator.js +64 -3
- package/dist/bridge/index.d.ts +17 -0
- package/dist/bridge/index.js +35 -2
- package/dist/card/approval-render.js +105 -8
- package/dist/index.js +17 -3
- package/dist/order/alias-resolve.d.ts +6 -2
- package/dist/order/index.d.ts +17 -2
- package/dist/order/index.js +57 -0
- package/dist/router/index.d.ts +35 -3
- package/dist/router/index.js +529 -85
- package/dist/runner/claude/runner.js +22 -0
- package/dist/runner/claude/session.d.ts +26 -0
- package/dist/runner/claude/session.js +94 -13
- package/dist/runner/common/connection-based-runner.js +10 -5
- package/dist/runner/dsh/runner.d.ts +7 -0
- package/dist/runner/dsh/runner.js +125 -74
- package/dist/runner/kimi/acp/runner.js +15 -0
- package/dist/runner/pi/rpc/runner.js +6 -1
- package/dist/runner/registry.d.ts +3 -2
- package/dist/runner/registry.js +3 -2
- package/dist/runner/types.d.ts +33 -2
- package/dist/session/common/pagination.d.ts +1 -1
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/dsh/sessions.d.ts +6 -1
- package/dist/session/dsh/sessions.js +20 -5
- package/dist/workspace/index.d.ts +8 -0
- package/dist/workspace/index.js +20 -1
- package/package.json +1 -1
- package/dist/order/alias-store.d.ts +0 -38
- package/dist/order/alias-store.js +0 -106
package/dist/runner/types.d.ts
CHANGED
|
@@ -69,6 +69,23 @@ export type ApprovalAction = {
|
|
|
69
69
|
/** Claude:允许当前请求 + 本会话后续所有权限请求自动放行(允许所有)。 */
|
|
70
70
|
| {
|
|
71
71
|
action: 'accept_all';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 计划审批「拒绝并附修改意见」:deny + message,Claude 留在 plan 模式
|
|
75
|
+
* 按意见修订(等价 TUI「Tell Claude what to change」)。
|
|
76
|
+
*/
|
|
77
|
+
| {
|
|
78
|
+
action: 'decline_with_feedback';
|
|
79
|
+
message: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 计划审批「批准并采纳修改意见」:allow + updatedInput.plan(原计划追加
|
|
83
|
+
* 意见),触发 Claude 侧 planWasEdited → tool_result 回显
|
|
84
|
+
* "## Approved Plan (edited by user)"(等价 TUI shift+tab approve with feedback)。
|
|
85
|
+
*/
|
|
86
|
+
| {
|
|
87
|
+
action: 'accept_with_feedback';
|
|
88
|
+
plan: string;
|
|
72
89
|
} | {
|
|
73
90
|
action: 'decline';
|
|
74
91
|
} | {
|
|
@@ -145,6 +162,20 @@ export interface ApprovalView {
|
|
|
145
162
|
* 表达审批内容,避免落入 command 槽位显示无意义 `{}`。
|
|
146
163
|
*/
|
|
147
164
|
toolName?: string;
|
|
165
|
+
/**
|
|
166
|
+
* 计划全文(kind === 'tool' 且 toolName === 'ExitPlanMode' 时存在)。
|
|
167
|
+
* 来源:control_request input.plan(CLI normalizeToolInput 从磁盘注入,
|
|
168
|
+
* 非稳定)→ 计划文件读取(input.planFilePath / 会话内 Write/Edit 跟踪)。
|
|
169
|
+
*/
|
|
170
|
+
plan?: string;
|
|
171
|
+
/** 计划文件路径(ExitPlanMode;input.planFilePath 或会话内跟踪所得)。 */
|
|
172
|
+
planFilePath?: string;
|
|
173
|
+
/**
|
|
174
|
+
* 用户已填写的计划修改意见(ExitPlanMode 反馈输入框;coordinator
|
|
175
|
+
* planFeedback 回流,卡片回显)。供「拒绝并附意见」/「批准并采纳修改」
|
|
176
|
+
* 两个决策复用。
|
|
177
|
+
*/
|
|
178
|
+
planFeedback?: string;
|
|
148
179
|
/** 原始 input 的 JSON 字符串(kind === 'tool' 且 input 非空时展示)。 */
|
|
149
180
|
toolInput?: string;
|
|
150
181
|
fileChanges?: Array<{
|
|
@@ -306,7 +337,7 @@ export interface SpawnOptions {
|
|
|
306
337
|
settings?: string;
|
|
307
338
|
}
|
|
308
339
|
/**
|
|
309
|
-
* Seam (§3): the contract any agent runner (claude/codex/opencode/pi/kimi)
|
|
340
|
+
* Seam (§3): the contract any agent runner (claude/codex/opencode/pi/kimi/dsh)
|
|
310
341
|
* must satisfy for the router/bridge.
|
|
311
342
|
* Declared here so callers depend on the interface, not the concrete
|
|
312
343
|
* runner classes — test stubs satisfy this structurally.
|
|
@@ -443,7 +474,7 @@ export interface AgentRunner extends Runner {
|
|
|
443
474
|
}
|
|
444
475
|
/** Agent self-describing status info for /status display. */
|
|
445
476
|
export interface AgentStatusInfo {
|
|
446
|
-
/** Agent kind (claude/codex/opencode/pi/kimi). */
|
|
477
|
+
/** Agent kind (claude/codex/opencode/pi/kimi/dsh). */
|
|
447
478
|
kind: AgentKind;
|
|
448
479
|
/** Display model name. */
|
|
449
480
|
model: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared pagination / truncation helpers for agent session readers.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
4
|
+
* The six readers (claude/codex/opencode/pi/kimi/dsh) all implement the same
|
|
5
5
|
* offset-clamp + slice + total pattern for `listSessions`, and the same
|
|
6
6
|
* maxEvents tail-cap for `readSessionContent`. These were duplicated (and had
|
|
7
7
|
* drifted on the default limit). Centralized here so the contract is defined
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared pagination / truncation helpers for agent session readers.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
4
|
+
* The six readers (claude/codex/opencode/pi/kimi/dsh) all implement the same
|
|
5
5
|
* offset-clamp + slice + total pattern for `listSessions`, and the same
|
|
6
6
|
* maxEvents tail-cap for `readSessionContent`. These were duplicated (and had
|
|
7
7
|
* drifted on the default limit). Centralized here so the contract is defined
|
|
@@ -15,13 +15,18 @@
|
|
|
15
15
|
import type { AgentSession, AgentSessionReader, SessionContent } from '../../runner/index.js';
|
|
16
16
|
interface DshSessionReaderOptions {
|
|
17
17
|
host?: string;
|
|
18
|
+
/** 每次调用时解析当前 host(/config 修改 host 后 runner 重建、reader 也跟随新 host)。
|
|
19
|
+
* 优先于固定的 `host`。 */
|
|
20
|
+
hostProvider?: () => string | undefined;
|
|
18
21
|
/** Injectable synchronous unary transport. Defaults to spawnSync curl. */
|
|
19
22
|
syncRequest?: (baseUrl: string, method: string, payload: unknown) => unknown;
|
|
20
23
|
}
|
|
21
24
|
export declare class DshSessionReader implements AgentSessionReader {
|
|
22
|
-
private readonly
|
|
25
|
+
private readonly hostProvider;
|
|
23
26
|
private readonly syncRequest;
|
|
24
27
|
constructor(opts?: DshSessionReaderOptions);
|
|
28
|
+
/** 解析当前 host(hostProvider 优先,允许 /config 热改 host)。 */
|
|
29
|
+
private resolveBaseUrl;
|
|
25
30
|
private unary;
|
|
26
31
|
listSessions(cwd: string, opts?: {
|
|
27
32
|
limit?: number;
|
|
@@ -17,6 +17,13 @@ import { getLogger } from '../../logger/index.js';
|
|
|
17
17
|
import { DEFAULT_DSH_HOST } from '../../config/index.js';
|
|
18
18
|
import { capEvents, paginate } from '../common/pagination.js';
|
|
19
19
|
import { truncateUtf8, TOOL_RESULT_MAX_BYTES } from '../../common/truncate.js';
|
|
20
|
+
/**
|
|
21
|
+
* CC-05: history 拉取上限。DSH session.history 的 maxMessages 同时限制返回条数,
|
|
22
|
+
* 若用它同时充当展示截断,>50 事件的会话累计 usage 会少算。这里拉取一个足够大的
|
|
23
|
+
* 上限保证 usage 累加覆盖整个会话;展示仍由 capEvents(opts?.maxEvents ?? 50) 截断。
|
|
24
|
+
* (若 DSH 支持分页 cursor 可改成分页,当前用有界大拉取,见 review-fix-plan-cc.md)
|
|
25
|
+
*/
|
|
26
|
+
const DSH_HISTORY_FETCH_LIMIT = 10_000;
|
|
20
27
|
/** Synchronous unary POST via curl (the reader interface is synchronous). */
|
|
21
28
|
function defaultSyncRequest(baseUrl, method, payload) {
|
|
22
29
|
const body = JSON.stringify({
|
|
@@ -53,14 +60,20 @@ function defaultSyncRequest(baseUrl, method, payload) {
|
|
|
53
60
|
return parsed.result.value;
|
|
54
61
|
}
|
|
55
62
|
export class DshSessionReader {
|
|
56
|
-
|
|
63
|
+
hostProvider;
|
|
57
64
|
syncRequest;
|
|
58
65
|
constructor(opts = {}) {
|
|
59
|
-
|
|
66
|
+
const fixed = (opts.host ?? DEFAULT_DSH_HOST).replace(/\/$/, '');
|
|
67
|
+
this.hostProvider = opts.hostProvider ?? (() => fixed);
|
|
60
68
|
this.syncRequest = opts.syncRequest ?? defaultSyncRequest;
|
|
61
69
|
}
|
|
70
|
+
/** 解析当前 host(hostProvider 优先,允许 /config 热改 host)。 */
|
|
71
|
+
resolveBaseUrl() {
|
|
72
|
+
const host = this.hostProvider();
|
|
73
|
+
return (host && host.length > 0 ? host : DEFAULT_DSH_HOST).replace(/\/$/, '');
|
|
74
|
+
}
|
|
62
75
|
unary(method, payload) {
|
|
63
|
-
return this.syncRequest(this.
|
|
76
|
+
return this.syncRequest(this.resolveBaseUrl(), method, payload);
|
|
64
77
|
}
|
|
65
78
|
listSessions(cwd, opts) {
|
|
66
79
|
try {
|
|
@@ -90,7 +103,7 @@ export class DshSessionReader {
|
|
|
90
103
|
try {
|
|
91
104
|
const value = this.unary('session.history', {
|
|
92
105
|
sessionId,
|
|
93
|
-
maxMessages:
|
|
106
|
+
maxMessages: DSH_HISTORY_FETCH_LIMIT,
|
|
94
107
|
});
|
|
95
108
|
const events = [];
|
|
96
109
|
let displayTitle;
|
|
@@ -142,7 +155,9 @@ export class DshSessionReader {
|
|
|
142
155
|
};
|
|
143
156
|
}
|
|
144
157
|
return {
|
|
145
|
-
|
|
158
|
+
// CC-05: 展示仍按 maxEvents 尾截断(默认 50,保持原行为),usage 已在上面基于
|
|
159
|
+
// 完整 history 累加。
|
|
160
|
+
events: capEvents(events, opts?.maxEvents ?? 50),
|
|
146
161
|
...(sessionUsage ? { usage: sessionUsage } : {}),
|
|
147
162
|
...(displayTitle ? { displayTitle } : {}),
|
|
148
163
|
};
|
|
@@ -14,6 +14,8 @@ export interface WorkspaceEntry {
|
|
|
14
14
|
* lastUsedAt 语义(2026-08-19 语义对齐):保存(save)与使用(use/touch)
|
|
15
15
|
* 都会把时间戳更新为当前时间。save 内部调用 touch,保证「最近使用」排序
|
|
16
16
|
* 对新保存的 workspace 同样生效(新条目应排在列表最前,而非沉底)。
|
|
17
|
+
* 同一毫秒内连续操作时 touch 会用 max+1 保证严格递增,避免 Date.now 相同
|
|
18
|
+
* 导致最近排序退化为字母序。
|
|
17
19
|
*
|
|
18
20
|
* Data migration: old format `{ alias: "path" }` is normalized to
|
|
19
21
|
* `{ alias: { path, lastUsedAt: 0 } }` on first load and persisted.
|
|
@@ -31,6 +33,12 @@ export declare class WorkspaceStore {
|
|
|
31
33
|
/**
|
|
32
34
|
* Update lastUsedAt to now for the given alias and persist.
|
|
33
35
|
* No-op if the alias does not exist.
|
|
36
|
+
*
|
|
37
|
+
* To keep "recent" ordering stable even for rapid successive saves/uses in
|
|
38
|
+
* the same millisecond, never allow another entry to share the same
|
|
39
|
+
* timestamp while this entry is being touched. If a peer already has a
|
|
40
|
+
* timestamp at or after the current wall-clock time, use max+1 so the
|
|
41
|
+
* touched entry always becomes the most recently used.
|
|
34
42
|
*/
|
|
35
43
|
touch(name: string): void;
|
|
36
44
|
/**
|
package/dist/workspace/index.js
CHANGED
|
@@ -13,6 +13,8 @@ import { getLogger } from '../logger/index.js';
|
|
|
13
13
|
* lastUsedAt 语义(2026-08-19 语义对齐):保存(save)与使用(use/touch)
|
|
14
14
|
* 都会把时间戳更新为当前时间。save 内部调用 touch,保证「最近使用」排序
|
|
15
15
|
* 对新保存的 workspace 同样生效(新条目应排在列表最前,而非沉底)。
|
|
16
|
+
* 同一毫秒内连续操作时 touch 会用 max+1 保证严格递增,避免 Date.now 相同
|
|
17
|
+
* 导致最近排序退化为字母序。
|
|
16
18
|
*
|
|
17
19
|
* Data migration: old format `{ alias: "path" }` is normalized to
|
|
18
20
|
* `{ alias: { path, lastUsedAt: 0 } }` on first load and persisted.
|
|
@@ -83,12 +85,29 @@ export class WorkspaceStore {
|
|
|
83
85
|
/**
|
|
84
86
|
* Update lastUsedAt to now for the given alias and persist.
|
|
85
87
|
* No-op if the alias does not exist.
|
|
88
|
+
*
|
|
89
|
+
* To keep "recent" ordering stable even for rapid successive saves/uses in
|
|
90
|
+
* the same millisecond, never allow another entry to share the same
|
|
91
|
+
* timestamp while this entry is being touched. If a peer already has a
|
|
92
|
+
* timestamp at or after the current wall-clock time, use max+1 so the
|
|
93
|
+
* touched entry always becomes the most recently used.
|
|
86
94
|
*/
|
|
87
95
|
touch(name) {
|
|
88
96
|
const entry = this.data.get(name);
|
|
89
97
|
if (!entry)
|
|
90
98
|
return;
|
|
91
|
-
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
let max = Math.max(now, entry.lastUsedAt);
|
|
101
|
+
let hasPeerAtOrAfterNow = false;
|
|
102
|
+
for (const [otherName, other] of this.data) {
|
|
103
|
+
if (otherName === name)
|
|
104
|
+
continue;
|
|
105
|
+
if (other.lastUsedAt > max)
|
|
106
|
+
max = other.lastUsedAt;
|
|
107
|
+
if (other.lastUsedAt >= now)
|
|
108
|
+
hasPeerAtOrAfterNow = true;
|
|
109
|
+
}
|
|
110
|
+
entry.lastUsedAt = hasPeerAtOrAfterNow ? max + 1 : max;
|
|
92
111
|
this.persist();
|
|
93
112
|
}
|
|
94
113
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export declare const MAX_ALIASES = 50;
|
|
2
|
-
export declare const MAX_ALIAS_NAME_LENGTH = 20;
|
|
3
|
-
export declare const MAX_ALIAS_TEXT_LENGTH = 200;
|
|
4
|
-
/**
|
|
5
|
-
* 别名名称规则:`[A-Za-z_][A-Za-z0-9_]*`,≤20 字符,不能数字开头
|
|
6
|
-
* —— `$500`、`$3d` 这类正常文本永远不匹配,不会误展开。
|
|
7
|
-
*/
|
|
8
|
-
export declare const ALIAS_NAME_PATTERN: RegExp;
|
|
9
|
-
export interface AliasEntry {
|
|
10
|
-
name: string;
|
|
11
|
-
text: string;
|
|
12
|
-
createdAt: string;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* 全局别名存储。
|
|
16
|
-
*
|
|
17
|
-
* 独立于 OrderStore(aliases.json),避免破坏 orders.json 向后兼容。
|
|
18
|
-
* 原子写(tmp + rename)+ 损坏恢复,复用 OrderStore/WorkspaceStore 同一模式。
|
|
19
|
-
*/
|
|
20
|
-
export declare class AliasStore {
|
|
21
|
-
private filePath;
|
|
22
|
-
private data;
|
|
23
|
-
constructor(filePath?: string);
|
|
24
|
-
/** Reload data from disk (useful when another process may have written). */
|
|
25
|
-
reload(): void;
|
|
26
|
-
private load;
|
|
27
|
-
private persist;
|
|
28
|
-
has(name: string): boolean;
|
|
29
|
-
get(name: string): AliasEntry | undefined;
|
|
30
|
-
list(): AliasEntry[];
|
|
31
|
-
/**
|
|
32
|
-
* 注册/更新别名(同名覆盖文本,保留 createdAt)。返回写入后的条目。
|
|
33
|
-
* 名称/文本/数量超限时抛错,由调用方提示用户。
|
|
34
|
-
*/
|
|
35
|
-
set(name: string, text: string): AliasEntry;
|
|
36
|
-
/** 删除别名;不存在返回 false。 */
|
|
37
|
-
remove(name: string): boolean;
|
|
38
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import { atomicWriteJson } from '../persistence/atomic-write.js';
|
|
4
|
-
import { loadJsonFile } from '../persistence/load-json-file.js';
|
|
5
|
-
export const MAX_ALIASES = 50;
|
|
6
|
-
export const MAX_ALIAS_NAME_LENGTH = 20;
|
|
7
|
-
export const MAX_ALIAS_TEXT_LENGTH = 200;
|
|
8
|
-
/**
|
|
9
|
-
* 别名名称规则:`[A-Za-z_][A-Za-z0-9_]*`,≤20 字符,不能数字开头
|
|
10
|
-
* —— `$500`、`$3d` 这类正常文本永远不匹配,不会误展开。
|
|
11
|
-
*/
|
|
12
|
-
export const ALIAS_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
13
|
-
/**
|
|
14
|
-
* 全局别名存储。
|
|
15
|
-
*
|
|
16
|
-
* 独立于 OrderStore(aliases.json),避免破坏 orders.json 向后兼容。
|
|
17
|
-
* 原子写(tmp + rename)+ 损坏恢复,复用 OrderStore/WorkspaceStore 同一模式。
|
|
18
|
-
*/
|
|
19
|
-
export class AliasStore {
|
|
20
|
-
filePath;
|
|
21
|
-
data;
|
|
22
|
-
constructor(filePath) {
|
|
23
|
-
this.filePath = filePath ?? path.join(os.homedir(), '.lark-remote', 'aliases.json');
|
|
24
|
-
this.data = [];
|
|
25
|
-
this.load();
|
|
26
|
-
}
|
|
27
|
-
/** Reload data from disk (useful when another process may have written). */
|
|
28
|
-
reload() {
|
|
29
|
-
this.load();
|
|
30
|
-
}
|
|
31
|
-
load() {
|
|
32
|
-
const parsed = loadJsonFile(this.filePath, undefined);
|
|
33
|
-
if (!parsed || !Array.isArray(parsed)) {
|
|
34
|
-
this.data = [];
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const valid = [];
|
|
38
|
-
for (const e of parsed) {
|
|
39
|
-
if (e &&
|
|
40
|
-
typeof e.name === 'string' &&
|
|
41
|
-
typeof e.text === 'string' &&
|
|
42
|
-
typeof e.createdAt === 'string' &&
|
|
43
|
-
ALIAS_NAME_PATTERN.test(e.name) &&
|
|
44
|
-
e.name.length <= MAX_ALIAS_NAME_LENGTH) {
|
|
45
|
-
valid.push({
|
|
46
|
-
name: e.name,
|
|
47
|
-
text: e.text.slice(0, MAX_ALIAS_TEXT_LENGTH),
|
|
48
|
-
createdAt: e.createdAt,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
this.data = valid;
|
|
53
|
-
}
|
|
54
|
-
persist() {
|
|
55
|
-
atomicWriteJson(this.filePath, this.data);
|
|
56
|
-
}
|
|
57
|
-
has(name) {
|
|
58
|
-
return this.data.some((a) => a.name === name);
|
|
59
|
-
}
|
|
60
|
-
get(name) {
|
|
61
|
-
return this.data.find((a) => a.name === name);
|
|
62
|
-
}
|
|
63
|
-
list() {
|
|
64
|
-
return [...this.data];
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* 注册/更新别名(同名覆盖文本,保留 createdAt)。返回写入后的条目。
|
|
68
|
-
* 名称/文本/数量超限时抛错,由调用方提示用户。
|
|
69
|
-
*/
|
|
70
|
-
set(name, text) {
|
|
71
|
-
if (!ALIAS_NAME_PATTERN.test(name) || name.length > MAX_ALIAS_NAME_LENGTH) {
|
|
72
|
-
throw new Error(`别名名称只能包含字母/数字/下划线,不能数字开头,且不超过 ${MAX_ALIAS_NAME_LENGTH} 字符`);
|
|
73
|
-
}
|
|
74
|
-
if (text.length > MAX_ALIAS_TEXT_LENGTH) {
|
|
75
|
-
throw new Error(`别名文本超过 ${MAX_ALIAS_TEXT_LENGTH} 字符限制`);
|
|
76
|
-
}
|
|
77
|
-
if (!this.has(name) && this.data.length >= MAX_ALIASES) {
|
|
78
|
-
throw new Error(`别名数量已达上限 ${MAX_ALIASES}`);
|
|
79
|
-
}
|
|
80
|
-
const existing = this.get(name);
|
|
81
|
-
const entry = {
|
|
82
|
-
name,
|
|
83
|
-
text,
|
|
84
|
-
createdAt: existing?.createdAt ?? new Date().toISOString(),
|
|
85
|
-
};
|
|
86
|
-
const idx = this.data.findIndex((a) => a.name === name);
|
|
87
|
-
if (idx >= 0) {
|
|
88
|
-
this.data[idx] = entry;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
this.data.push(entry);
|
|
92
|
-
}
|
|
93
|
-
this.persist();
|
|
94
|
-
return entry;
|
|
95
|
-
}
|
|
96
|
-
/** 删除别名;不存在返回 false。 */
|
|
97
|
-
remove(name) {
|
|
98
|
-
const before = this.data.length;
|
|
99
|
-
this.data = this.data.filter((a) => a.name !== name);
|
|
100
|
-
if (this.data.length === before)
|
|
101
|
-
return false;
|
|
102
|
-
this.persist();
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
//# sourceMappingURL=alias-store.js.map
|