cueme 0.1.15 → 0.1.17
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 +3 -3
- package/README.zh-CN.md +3 -3
- package/docs/proto.md +41 -2
- package/package.json +4 -6
- package/src/envelope.js +8 -4
- package/src/handler.js +1 -0
- package/src/proto.js +169 -53
package/README.md
CHANGED
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/cueme)
|
|
15
15
|
|
|
16
16
|
[](https://github.com/nmhjklnm/cue-stack)
|
|
17
|
-
[](https://github.com/nmhjklnm/cue-console)
|
|
18
|
-
[](https://github.com/nmhjklnm/cue-command)
|
|
19
17
|

|
|
20
18
|
|
|
19
|
+
Source: <https://github.com/nmhjklnm/cue-stack/tree/main/cue-command>
|
|
20
|
+
|
|
21
21
|
[Contributing](./CONTRIBUTING.md) · [Trademark](./TRADEMARK.md)
|
|
22
22
|
|
|
23
23
|
A command protocol adapter for Cue, compatible with the existing SQLite mailbox (`~/.cue/cue.db`).
|
|
@@ -36,7 +36,7 @@ npm install -g cueme
|
|
|
36
36
|
|
|
37
37
|
Copy the contents of `protocol.md` into your runtime's system prompt / persistent rules:
|
|
38
38
|
|
|
39
|
-
- [`protocol.md`](https://github.com/nmhjklnm/cue-
|
|
39
|
+
- [`protocol.md`](https://github.com/nmhjklnm/cue-stack/blob/main/cue-command/protocol.md)
|
|
40
40
|
|
|
41
41
|
If you installed via npm, `protocol.md` is also included in the package.
|
|
42
42
|
|
package/README.zh-CN.md
CHANGED
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/cueme)
|
|
15
15
|
|
|
16
16
|
[](https://github.com/nmhjklnm/cue-stack)
|
|
17
|
-
[](https://github.com/nmhjklnm/cue-console)
|
|
18
|
-
[](https://github.com/nmhjklnm/cue-command)
|
|
19
17
|

|
|
20
18
|
|
|
19
|
+
源码: <https://github.com/nmhjklnm/cue-stack/tree/main/cue-command>
|
|
20
|
+
|
|
21
21
|
[Contributing](./CONTRIBUTING.md) · [Trademark](./TRADEMARK.md)
|
|
22
22
|
|
|
23
23
|
这是 Cue 的命令行协议适配器,兼容现有的 SQLite mailbox(`~/.cue/cue.db`)。
|
|
@@ -36,7 +36,7 @@ npm install -g cueme
|
|
|
36
36
|
|
|
37
37
|
把 `protocol.md` 的内容复制到你正在使用的 runtime 的系统提示词 / 持久规则里:
|
|
38
38
|
|
|
39
|
-
- [`protocol.md`](https://github.com/nmhjklnm/cue-
|
|
39
|
+
- [`protocol.md`](https://github.com/nmhjklnm/cue-stack/blob/main/cue-command/protocol.md)
|
|
40
40
|
|
|
41
41
|
如果你是通过 npm 安装的,`protocol.md` 也会包含在安装包里。
|
|
42
42
|
|
package/docs/proto.md
CHANGED
|
@@ -6,7 +6,19 @@ This document describes the `cueme proto` command family.
|
|
|
6
6
|
|
|
7
7
|
`cueme proto` injects the shared `protocol.md` into a specific agent file by composing:
|
|
8
8
|
|
|
9
|
-
`final_proto = prefix(agent) + "\n\n" +
|
|
9
|
+
`final_proto = prefix(agent) + "\n\n" + proto_block + runtime_block`
|
|
10
|
+
|
|
11
|
+
`proto_block` and `runtime_block` are separate managed blocks:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
<!-- HUMAN_AGENT_PROTO_BEGIN -->
|
|
15
|
+
... protocol.md ...
|
|
16
|
+
<!-- HUMAN_AGENT_PROTO_END -->
|
|
17
|
+
|
|
18
|
+
<!-- HUMAN_AGENT_RUNTIME_BEGIN -->
|
|
19
|
+
... runtime-specific guidance ...
|
|
20
|
+
<!-- HUMAN_AGENT_RUNTIME_END -->
|
|
21
|
+
```
|
|
10
22
|
|
|
11
23
|
The injected content is managed between sentinel markers and may be overwritten by `cueme proto apply`.
|
|
12
24
|
|
|
@@ -23,6 +35,8 @@ Required keys:
|
|
|
23
35
|
- supports `~` and `%ENV%` expansions (e.g. `%APPDATA%`, `%USERPROFILE%`)
|
|
24
36
|
- `cueme.proto.prefix`: map of prefix by `<agent>`
|
|
25
37
|
- can be a string or string array (joined with `\n`)
|
|
38
|
+
- `cueme.proto.runtime`: map of runtime-specific guidance by `<agent>`
|
|
39
|
+
- can be a string or string array (joined with `\n`)
|
|
26
40
|
- `cueme.proto.protocol_path`: absolute or relative path to the shared `protocol.md`
|
|
27
41
|
- supports `~` and `%ENV%` expansions
|
|
28
42
|
|
|
@@ -38,7 +52,15 @@ Example:
|
|
|
38
52
|
"windows.windsurf": "%USERPROFILE%\\.codeium\\windsurf\\memories\\global_rules.md",
|
|
39
53
|
|
|
40
54
|
"linux.vscode": "~/.config/Code/User/prompts/human_proto.md",
|
|
41
|
-
"linux.windsurf": "~/.codeium/windsurf/memories/global_rules.md"
|
|
55
|
+
"linux.windsurf": "~/.codeium/windsurf/memories/global_rules.md",
|
|
56
|
+
|
|
57
|
+
"macos.codex": "~/.codex/AGENTS.md",
|
|
58
|
+
"windows.codex": "%USERPROFILE%\\.codex\\AGENTS.md",
|
|
59
|
+
"linux.codex": "~/.codex/AGENTS.md",
|
|
60
|
+
|
|
61
|
+
"macos.claude": "~/.claude/CLAUDE.md",
|
|
62
|
+
"windows.claude": "%USERPROFILE%\\.claude\\CLAUDE.md",
|
|
63
|
+
"linux.claude": "~/.claude/CLAUDE.md"
|
|
42
64
|
},
|
|
43
65
|
"cueme.proto.prefix": {
|
|
44
66
|
"vscode": [
|
|
@@ -46,8 +68,19 @@ Example:
|
|
|
46
68
|
"applyTo: '**'",
|
|
47
69
|
"---"
|
|
48
70
|
],
|
|
71
|
+
"codex": [],
|
|
72
|
+
"claude": [],
|
|
49
73
|
"windsurf": []
|
|
50
74
|
},
|
|
75
|
+
"cueme.proto.runtime": {
|
|
76
|
+
"codex": [
|
|
77
|
+
"When calling cueme cue/pause, set the tool wait timeout to 24h to avoid premature tool timeouts.",
|
|
78
|
+
"不要误解“更新 todo_list”的指令,因为你是 codex,你需要做的是:把 update_plan 当作 todo_list 来维护任务,而不是创建本地 todo_list 文件。"
|
|
79
|
+
],
|
|
80
|
+
"claude": [
|
|
81
|
+
"When calling cueme cue/pause, set the tool wait timeout to 24h to avoid premature tool timeouts."
|
|
82
|
+
]
|
|
83
|
+
},
|
|
51
84
|
"cueme.proto.protocol_path": "~/path/to/protocol.md"
|
|
52
85
|
}
|
|
53
86
|
```
|
|
@@ -60,6 +93,10 @@ Injected content is managed between these markers:
|
|
|
60
93
|
<!-- HUMAN_AGENT_PROTO_BEGIN -->
|
|
61
94
|
... managed content ...
|
|
62
95
|
<!-- HUMAN_AGENT_PROTO_END -->
|
|
96
|
+
|
|
97
|
+
<!-- HUMAN_AGENT_RUNTIME_BEGIN -->
|
|
98
|
+
... managed content ...
|
|
99
|
+
<!-- HUMAN_AGENT_RUNTIME_END -->
|
|
63
100
|
```
|
|
64
101
|
|
|
65
102
|
Notes:
|
|
@@ -101,6 +138,8 @@ Auto-detect (current platform only):
|
|
|
101
138
|
|
|
102
139
|
- `vscode`: `.vscode/prompts/human_proto.md` (workspace) then platform user path
|
|
103
140
|
- `windsurf`: `.codeium/windsurf/memories/global_rules.md` (workspace) then platform user path
|
|
141
|
+
- `codex`: uses `~/.codex/AGENTS.md` by default (not auto-detected)
|
|
142
|
+
- `claude`: uses `~/.claude/CLAUDE.md` by default (not auto-detected)
|
|
104
143
|
|
|
105
144
|
### Helpers
|
|
106
145
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Cue command protocol adapter (stdin/stdout JSON)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"files": [
|
|
@@ -18,12 +18,10 @@
|
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"prepare": "node -c src/cli.js && node -c src/handler.js"
|
|
23
|
-
},
|
|
24
21
|
"dependencies": {
|
|
25
22
|
"better-sqlite3": "^12.6.0",
|
|
26
23
|
"chardet": "^2.1.1",
|
|
27
24
|
"iconv-lite": "^0.7.2"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {}
|
|
27
|
+
}
|
package/src/envelope.js
CHANGED
|
@@ -3,10 +3,14 @@ function parseTagBlocksEnvelope(raw, opts = {}) {
|
|
|
3
3
|
const text = String(raw == null ? '' : raw);
|
|
4
4
|
const trimmed = text.trim();
|
|
5
5
|
if (!trimmed) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
return {
|
|
7
|
+
ok: false,
|
|
8
|
+
error:
|
|
9
|
+
'error: stdin cannot be empty. You MUST provide input using tag-block envelope:\n' +
|
|
10
|
+
'<cueme_prompt>\n...\n</cueme_prompt>\n' +
|
|
11
|
+
(allowPayload ? '<cueme_payload>\n...\n</cueme_payload>\n' : '') +
|
|
12
|
+
'This is critical for proper interaction flow.\n',
|
|
13
|
+
};
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
if (trimmed.startsWith('{')) {
|
package/src/handler.js
CHANGED
package/src/proto.js
CHANGED
|
@@ -4,9 +4,13 @@ const path = require('path');
|
|
|
4
4
|
|
|
5
5
|
const BEGIN_MARKER = '<!-- HUMAN_AGENT_PROTO_BEGIN -->';
|
|
6
6
|
const END_MARKER = '<!-- HUMAN_AGENT_PROTO_END -->';
|
|
7
|
+
const RUNTIME_BEGIN_MARKER = '<!-- HUMAN_AGENT_RUNTIME_BEGIN -->';
|
|
8
|
+
const RUNTIME_END_MARKER = '<!-- HUMAN_AGENT_RUNTIME_END -->';
|
|
7
9
|
|
|
8
10
|
const BEGIN_MARKER_RE = /<!--\s*(?:HUMAN|HUAMN)_AGENT_PROTO_BEGIN\s*-->/;
|
|
9
11
|
const END_MARKER_RE = /<!--\s*(?:HUMAN|HUAMN)_AGENT_PROTO_END\s*-->/;
|
|
12
|
+
const RUNTIME_BEGIN_MARKER_RE = /<!--\s*(?:HUMAN|HUAMN)_AGENT_RUNTIME_BEGIN\s*-->/;
|
|
13
|
+
const RUNTIME_END_MARKER_RE = /<!--\s*(?:HUMAN|HUAMN)_AGENT_RUNTIME_END\s*-->/;
|
|
10
14
|
|
|
11
15
|
function getPlatformKey() {
|
|
12
16
|
const p = process.platform;
|
|
@@ -146,6 +150,16 @@ function defaultPathMapTemplate() {
|
|
|
146
150
|
out['windows.kiro'] = path.join(userProfile, '.kiro', 'steering', 'cueme_proto.md');
|
|
147
151
|
out['linux.kiro'] = path.join(home, '.kiro', 'steering', 'cueme_proto.md');
|
|
148
152
|
|
|
153
|
+
// Codex (global)
|
|
154
|
+
out['macos.codex'] = path.join(home, '.codex', 'AGENTS.md');
|
|
155
|
+
out['windows.codex'] = path.join(userProfile, '.codex', 'AGENTS.md');
|
|
156
|
+
out['linux.codex'] = path.join(home, '.codex', 'AGENTS.md');
|
|
157
|
+
|
|
158
|
+
// Claude Code (global)
|
|
159
|
+
out['macos.claude'] = path.join(home, '.claude', 'CLAUDE.md');
|
|
160
|
+
out['windows.claude'] = path.join(userProfile, '.claude', 'CLAUDE.md');
|
|
161
|
+
out['linux.claude'] = path.join(home, '.claude', 'CLAUDE.md');
|
|
162
|
+
|
|
149
163
|
return out;
|
|
150
164
|
}
|
|
151
165
|
|
|
@@ -154,10 +168,21 @@ function defaultConfigTemplate() {
|
|
|
154
168
|
return {
|
|
155
169
|
'cueme.proto.path': defaultPathMapTemplate(),
|
|
156
170
|
'cueme.proto.prefix': {
|
|
171
|
+
codex: [],
|
|
172
|
+
claude: [],
|
|
157
173
|
windsurf: [],
|
|
158
174
|
vscode: ['---', 'applyTo: "**"', '---'],
|
|
159
175
|
kiro: [],
|
|
160
176
|
},
|
|
177
|
+
'cueme.proto.runtime': {
|
|
178
|
+
codex: [
|
|
179
|
+
'When calling cueme cue/pause, set the tool wait timeout to 24h to avoid premature tool timeouts.',
|
|
180
|
+
'不要误解“更新 todo_list”的指令,因为你是 codex,你需要做的是:把 update_plan 当作 todo_list 来维护任务,而不是创建本地 todo_list 文件。',
|
|
181
|
+
],
|
|
182
|
+
claude: [
|
|
183
|
+
'When calling cueme cue/pause, set the tool wait timeout to 24h to avoid premature tool timeouts.',
|
|
184
|
+
],
|
|
185
|
+
},
|
|
161
186
|
'cueme.proto.protocol_path': protocolPath,
|
|
162
187
|
};
|
|
163
188
|
}
|
|
@@ -259,7 +284,11 @@ function ensureDirForFile(filePath) {
|
|
|
259
284
|
|
|
260
285
|
function buildFinalProto({ cfg, agent }) {
|
|
261
286
|
const prefixMap = cfg['cueme.proto.prefix'] || {};
|
|
262
|
-
const
|
|
287
|
+
const runtimeMap = cfg['cueme.proto.runtime'] || {};
|
|
288
|
+
let protocolPath = cfg['cueme.proto.protocol_path'];
|
|
289
|
+
if (typeof protocolPath !== 'string' || protocolPath.trim().length === 0) {
|
|
290
|
+
protocolPath = path.join(__dirname, '..', 'protocol.md');
|
|
291
|
+
}
|
|
263
292
|
|
|
264
293
|
const prefixRaw = prefixMap[agent];
|
|
265
294
|
let prefix;
|
|
@@ -271,8 +300,16 @@ function buildFinalProto({ cfg, agent }) {
|
|
|
271
300
|
throw new Error(`error: prefix not configured: cueme.proto.prefix["${agent}"]`);
|
|
272
301
|
}
|
|
273
302
|
|
|
274
|
-
|
|
275
|
-
|
|
303
|
+
const runtimeRaw = runtimeMap[agent];
|
|
304
|
+
let runtime = '';
|
|
305
|
+
if (runtimeRaw == null || runtimeRaw === '') {
|
|
306
|
+
runtime = '';
|
|
307
|
+
} else if (typeof runtimeRaw === 'string') {
|
|
308
|
+
runtime = runtimeRaw;
|
|
309
|
+
} else if (Array.isArray(runtimeRaw) && runtimeRaw.every((x) => typeof x === 'string')) {
|
|
310
|
+
runtime = runtimeRaw.join('\n');
|
|
311
|
+
} else {
|
|
312
|
+
throw new Error(`error: runtime not configured: cueme.proto.runtime["${agent}"]`);
|
|
276
313
|
}
|
|
277
314
|
|
|
278
315
|
const protocolPathExpanded = expandPath(protocolPath);
|
|
@@ -284,10 +321,10 @@ function buildFinalProto({ cfg, agent }) {
|
|
|
284
321
|
try {
|
|
285
322
|
protocol = fs.readFileSync(resolvedProtocolPath, 'utf8');
|
|
286
323
|
} catch {
|
|
287
|
-
throw new Error(
|
|
324
|
+
throw new Error(`error: cannot read protocol file: ${resolvedProtocolPath}`);
|
|
288
325
|
}
|
|
289
326
|
|
|
290
|
-
return { prefix, protocol };
|
|
327
|
+
return { prefix, protocol, runtime };
|
|
291
328
|
}
|
|
292
329
|
|
|
293
330
|
function resolveTargetPath({ cfg, agent }) {
|
|
@@ -302,41 +339,114 @@ function resolveTargetPath({ cfg, agent }) {
|
|
|
302
339
|
return path.isAbsolute(expanded) ? expanded : path.resolve(process.cwd(), expanded);
|
|
303
340
|
}
|
|
304
341
|
|
|
305
|
-
function
|
|
342
|
+
function makeProtoBlock({ protocol, eol }) {
|
|
306
343
|
const normalizedProto = String(protocol || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
307
344
|
const protoLines = normalizedProto.split('\n');
|
|
308
|
-
|
|
309
|
-
|
|
345
|
+
return [BEGIN_MARKER, ...protoLines, END_MARKER].join(eol) + eol;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function makeRuntimeBlock({ runtime, eol }) {
|
|
349
|
+
const runtimeText = String(runtime || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
|
|
350
|
+
if (!runtimeText) return '';
|
|
351
|
+
const runtimeLines = runtimeText.split('\n');
|
|
352
|
+
return [RUNTIME_BEGIN_MARKER, ...runtimeLines, RUNTIME_END_MARKER].join(eol) + eol;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function findBlockRange(text, beginRe, endRe) {
|
|
356
|
+
const beginMatch = text.match(beginRe);
|
|
357
|
+
const endMatch = text.match(endRe);
|
|
358
|
+
if (!beginMatch || !endMatch || endMatch.index <= beginMatch.index) return null;
|
|
359
|
+
return { beginIdx: beginMatch.index, endIdx: endMatch.index, endLen: endMatch[0].length };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function extractBlock({ text, range, eol }) {
|
|
363
|
+
let block = text.slice(range.beginIdx, range.endIdx + range.endLen);
|
|
364
|
+
const after = text.slice(range.endIdx + range.endLen);
|
|
365
|
+
if (after.startsWith(eol)) block += eol;
|
|
366
|
+
return block;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function replaceBlock({ text, range, block, eol }) {
|
|
370
|
+
const before = text.slice(0, range.beginIdx);
|
|
371
|
+
const after = text.slice(range.endIdx + range.endLen);
|
|
372
|
+
const afterTrim = after.startsWith(eol) ? after.slice(eol.length) : after;
|
|
373
|
+
if (!block) return before + afterTrim;
|
|
374
|
+
return before + block + afterTrim;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function appendBlock({ text, block, eol }) {
|
|
378
|
+
if (!block) return text;
|
|
379
|
+
let out = text || '';
|
|
380
|
+
if (out && !out.endsWith(eol)) out += eol;
|
|
381
|
+
out += block;
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function makeCombinedBlock({ prefix, protoBlock, runtimeBlock, eol }) {
|
|
386
|
+
let block = protoBlock + (runtimeBlock || '');
|
|
310
387
|
if (prefix && prefix.trim()) {
|
|
311
388
|
const normalizedPrefix = String(prefix).replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
312
|
-
return normalizedPrefix + eol + eol +
|
|
389
|
+
return normalizedPrefix + eol + eol + block;
|
|
313
390
|
}
|
|
314
|
-
|
|
315
|
-
return managedBlock;
|
|
391
|
+
return block;
|
|
316
392
|
}
|
|
317
393
|
|
|
318
|
-
function
|
|
319
|
-
const eol = detectEol(existing);
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
394
|
+
function applyManagedBlocks({ existing, prefix, protocol, runtime }) {
|
|
395
|
+
const eol = existing ? detectEol(existing) : os.EOL;
|
|
396
|
+
const protoBlock = makeProtoBlock({ protocol, eol });
|
|
397
|
+
const runtimeBlock = makeRuntimeBlock({ runtime, eol });
|
|
398
|
+
const hasProto = findBlockRange(existing, BEGIN_MARKER_RE, END_MARKER_RE);
|
|
399
|
+
const hasRuntime = findBlockRange(existing, RUNTIME_BEGIN_MARKER_RE, RUNTIME_END_MARKER_RE);
|
|
400
|
+
const hasAny = Boolean(hasProto || hasRuntime);
|
|
401
|
+
|
|
402
|
+
if (!hasAny) {
|
|
403
|
+
const combinedBlock = makeCombinedBlock({ prefix, protoBlock, runtimeBlock, eol });
|
|
404
|
+
return {
|
|
405
|
+
out: appendBlock({ text: existing, block: combinedBlock, eol }),
|
|
406
|
+
prefix_added: Boolean(prefix && prefix.trim()),
|
|
407
|
+
proto_action: 'added',
|
|
408
|
+
runtime_action: runtimeBlock ? 'added' : 'skipped',
|
|
409
|
+
};
|
|
410
|
+
}
|
|
328
411
|
|
|
329
|
-
|
|
330
|
-
|
|
412
|
+
let out = existing;
|
|
413
|
+
let proto_action = 'skipped';
|
|
414
|
+
let runtime_action = 'skipped';
|
|
415
|
+
|
|
416
|
+
const protoRange = findBlockRange(out, BEGIN_MARKER_RE, END_MARKER_RE);
|
|
417
|
+
if (protoRange) {
|
|
418
|
+
const existingBlock = extractBlock({ text: out, range: protoRange, eol });
|
|
419
|
+
if (existingBlock === protoBlock) {
|
|
420
|
+
proto_action = 'unchanged';
|
|
421
|
+
} else {
|
|
422
|
+
out = replaceBlock({ text: out, range: protoRange, block: protoBlock, eol });
|
|
423
|
+
proto_action = 'updated';
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
out = appendBlock({ text: out, block: protoBlock, eol });
|
|
427
|
+
proto_action = 'added';
|
|
428
|
+
}
|
|
331
429
|
|
|
332
|
-
|
|
333
|
-
|
|
430
|
+
const runtimeRange = findBlockRange(out, RUNTIME_BEGIN_MARKER_RE, RUNTIME_END_MARKER_RE);
|
|
431
|
+
if (runtimeRange) {
|
|
432
|
+
if (runtimeBlock) {
|
|
433
|
+
const existingBlock = extractBlock({ text: out, range: runtimeRange, eol });
|
|
434
|
+
if (existingBlock === runtimeBlock) {
|
|
435
|
+
runtime_action = 'unchanged';
|
|
436
|
+
} else {
|
|
437
|
+
out = replaceBlock({ text: out, range: runtimeRange, block: runtimeBlock, eol });
|
|
438
|
+
runtime_action = 'updated';
|
|
439
|
+
}
|
|
440
|
+
} else {
|
|
441
|
+
out = replaceBlock({ text: out, range: runtimeRange, block: '', eol });
|
|
442
|
+
runtime_action = 'removed';
|
|
443
|
+
}
|
|
444
|
+
} else if (runtimeBlock) {
|
|
445
|
+
out = appendBlock({ text: out, block: runtimeBlock, eol });
|
|
446
|
+
runtime_action = 'added';
|
|
334
447
|
}
|
|
335
448
|
|
|
336
|
-
|
|
337
|
-
if (!out.endsWith(eol)) out += eol;
|
|
338
|
-
out += block;
|
|
339
|
-
return out;
|
|
449
|
+
return { out, prefix_added: false, proto_action, runtime_action };
|
|
340
450
|
}
|
|
341
451
|
|
|
342
452
|
function listAgents({ cfg }) {
|
|
@@ -371,7 +481,7 @@ function protoLs() {
|
|
|
371
481
|
function protoApply(agent) {
|
|
372
482
|
const cfg = readConfigOrThrow({ auto_init: true });
|
|
373
483
|
const targetPath = resolveTargetPath({ cfg, agent });
|
|
374
|
-
const { prefix, protocol } = buildFinalProto({ cfg, agent });
|
|
484
|
+
const { prefix, protocol, runtime } = buildFinalProto({ cfg, agent });
|
|
375
485
|
|
|
376
486
|
let existing = '';
|
|
377
487
|
let exists = false;
|
|
@@ -383,20 +493,22 @@ function protoApply(agent) {
|
|
|
383
493
|
exists = false;
|
|
384
494
|
}
|
|
385
495
|
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
} else {
|
|
393
|
-
out = applyManagedBlock({ existing, prefix, protocol });
|
|
394
|
-
}
|
|
496
|
+
const { out, prefix_added, proto_action, runtime_action } = applyManagedBlocks({
|
|
497
|
+
existing,
|
|
498
|
+
prefix,
|
|
499
|
+
protocol,
|
|
500
|
+
runtime,
|
|
501
|
+
});
|
|
395
502
|
|
|
396
503
|
ensureDirForFile(targetPath);
|
|
397
504
|
fs.writeFileSync(targetPath, out, 'utf8');
|
|
398
505
|
|
|
399
|
-
|
|
506
|
+
const parts = [
|
|
507
|
+
`prefix=${prefix_added ? 'added' : 'skipped'}`,
|
|
508
|
+
`proto=${proto_action}`,
|
|
509
|
+
`runtime=${runtime_action}`,
|
|
510
|
+
];
|
|
511
|
+
return `ok: applied to ${targetPath} (${parts.join(', ')})`;
|
|
400
512
|
}
|
|
401
513
|
|
|
402
514
|
function protoRemove(agent) {
|
|
@@ -410,23 +522,27 @@ function protoRemove(agent) {
|
|
|
410
522
|
return `ok: file does not exist: ${targetPath}`;
|
|
411
523
|
}
|
|
412
524
|
|
|
413
|
-
const
|
|
414
|
-
|
|
525
|
+
const eol = detectEol(existing);
|
|
526
|
+
let out = existing;
|
|
527
|
+
let removed = false;
|
|
415
528
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
529
|
+
const removeBlock = (text, beginRe, endRe) => {
|
|
530
|
+
const range = findBlockRange(text, beginRe, endRe);
|
|
531
|
+
if (!range) return { text, removed: false };
|
|
532
|
+
return { text: replaceBlock({ text, range, block: '', eol }), removed: true };
|
|
533
|
+
};
|
|
419
534
|
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
535
|
+
const protoResult = removeBlock(out, BEGIN_MARKER_RE, END_MARKER_RE);
|
|
536
|
+
out = protoResult.text;
|
|
537
|
+
removed = removed || protoResult.removed;
|
|
423
538
|
|
|
424
|
-
const
|
|
425
|
-
|
|
539
|
+
const runtimeResult = removeBlock(out, RUNTIME_BEGIN_MARKER_RE, RUNTIME_END_MARKER_RE);
|
|
540
|
+
out = runtimeResult.text;
|
|
541
|
+
removed = removed || runtimeResult.removed;
|
|
426
542
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
543
|
+
if (!removed) {
|
|
544
|
+
return `ok: no managed block found in: ${targetPath}`;
|
|
545
|
+
}
|
|
430
546
|
|
|
431
547
|
if (out.trim().length === 0) {
|
|
432
548
|
try {
|