cli-blueprint 7.0.39 → 7.0.41
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/broker-account-storage.mjs +30 -13
- package/official-skill-update.mjs +3 -2
- package/package.json +1 -1
- package/skill/SKILL.md +6 -7
- package/skill/skill.json +1 -1
|
@@ -13,6 +13,18 @@ const ACL_SID_ALIASES = Object.freeze({ SY: SYSTEM_SID, WD: 'S-1-1-0', BA: 'S-1-
|
|
|
13
13
|
BU: 'S-1-5-32-545', AU: 'S-1-5-11', CO: 'S-1-3-0', CG: 'S-1-3-1', AN: 'S-1-5-7' })
|
|
14
14
|
const SID_PATTERN = /^S-1-(?:[0-9]+-)*[0-9]+$/
|
|
15
15
|
const ACL_TIMEOUT_MS = 15_000
|
|
16
|
+
const WINDOWS_SYSTEM_EXECUTABLES = new Set(['whoami.exe', 'icacls.exe', 'where.exe'])
|
|
17
|
+
|
|
18
|
+
export function windowsSystemExecutable(name, environment = process.env) {
|
|
19
|
+
if (!WINDOWS_SYSTEM_EXECUTABLES.has(name)) throw new Error('Unsupported Windows system executable')
|
|
20
|
+
const root = environment.SystemRoot
|
|
21
|
+
if (typeof root !== 'string' || !/^[A-Za-z]:[\\/]/.test(root)
|
|
22
|
+
|| /[\u0000-\u001f"<>|?*]/.test(root) || root.slice(2).includes(':')
|
|
23
|
+
|| root.split(/[\\/]/).includes('..')) {
|
|
24
|
+
throw new Error('SystemRoot must identify an absolute Windows installation directory')
|
|
25
|
+
}
|
|
26
|
+
return win32.join(root, 'System32', name)
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
export function currentAccountHome() {
|
|
18
30
|
const home = userInfo().homedir
|
|
@@ -69,17 +81,19 @@ export function assertRestrictedWindowsAcl(text, ownerSid) {
|
|
|
69
81
|
if (expected.size) throw new Error('Windows account ACL is missing the user or SYSTEM')
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
async function windowsOwnerSid(run) {
|
|
73
|
-
const result = await run('whoami.exe', ['/user', '/fo', 'csv', '/nh'],
|
|
84
|
+
async function windowsOwnerSid(run, environment) {
|
|
85
|
+
const result = await run(windowsSystemExecutable('whoami.exe', environment), ['/user', '/fo', 'csv', '/nh'],
|
|
86
|
+
{ shell: false, windowsHide: true, timeout: ACL_TIMEOUT_MS })
|
|
74
87
|
const candidates = result.stdout.match(/S-1-(?:[0-9]+-)*[0-9]+/g)
|
|
75
88
|
if (candidates === null || candidates.length !== 1 || !SID_PATTERN.test(candidates[0])) throw new Error('Current Windows account SID could not be verified')
|
|
76
89
|
return candidates[0]
|
|
77
90
|
}
|
|
78
91
|
|
|
79
|
-
async function readWindowsAcl(path, run) {
|
|
92
|
+
async function readWindowsAcl(path, run, environment) {
|
|
80
93
|
const temporary = join(dirname(path), '.acl-' + randomUUID() + '.txt')
|
|
81
94
|
try {
|
|
82
|
-
await run('icacls.exe', [path, '/save', temporary, '/q'],
|
|
95
|
+
await run(windowsSystemExecutable('icacls.exe', environment), [path, '/save', temporary, '/q'],
|
|
96
|
+
{ shell: false, windowsHide: true, timeout: ACL_TIMEOUT_MS })
|
|
83
97
|
return aclText(await readFile(temporary))
|
|
84
98
|
} finally {
|
|
85
99
|
try { await rm(temporary) } catch (error) { if (error.code !== 'ENOENT') throw error }
|
|
@@ -88,19 +102,21 @@ async function readWindowsAcl(path, run) {
|
|
|
88
102
|
|
|
89
103
|
async function protectWindowsPath(path, directory, dependencies) {
|
|
90
104
|
const run = dependencies.execFile === undefined ? runFile : dependencies.execFile
|
|
91
|
-
const
|
|
105
|
+
const environment = dependencies.environment === undefined ? process.env : dependencies.environment
|
|
106
|
+
const owner = await windowsOwnerSid(run, environment)
|
|
92
107
|
const flags = directory ? '(OI)(CI)F' : 'F'
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
const icacls = windowsSystemExecutable('icacls.exe', environment)
|
|
109
|
+
await run(icacls, [path, '/inheritance:r', '/grant:r', '*' + owner + ':' + flags,
|
|
110
|
+
'*' + SYSTEM_SID + ':' + flags], { shell: false, windowsHide: true, timeout: ACL_TIMEOUT_MS })
|
|
111
|
+
const { entries } = windowsAclEntries(await readWindowsAcl(path, run, environment))
|
|
96
112
|
for (const entry of entries) {
|
|
97
113
|
const sid = Object.hasOwn(ACL_SID_ALIASES, entry.sid) ? ACL_SID_ALIASES[entry.sid] : entry.sid
|
|
98
114
|
if (entry.type === 'A' && [owner, SYSTEM_SID].includes(sid)) continue
|
|
99
115
|
if (!SID_PATTERN.test(sid)) throw new Error('Unexpected Windows ACL trustee')
|
|
100
|
-
await run(
|
|
101
|
-
{ windowsHide: true, timeout: ACL_TIMEOUT_MS })
|
|
116
|
+
await run(icacls, [path, entry.type === 'D' ? '/remove:d' : '/remove:g', '*' + sid],
|
|
117
|
+
{ shell: false, windowsHide: true, timeout: ACL_TIMEOUT_MS })
|
|
102
118
|
}
|
|
103
|
-
assertRestrictedWindowsAcl(await readWindowsAcl(path, run), owner)
|
|
119
|
+
assertRestrictedWindowsAcl(await readWindowsAcl(path, run, environment), owner)
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
export async function protectAccountPath(path, directory, dependencies = {}) {
|
|
@@ -130,6 +146,7 @@ export async function verifyAccountPath(path, dependencies = {}) {
|
|
|
130
146
|
return
|
|
131
147
|
}
|
|
132
148
|
const run = dependencies.execFile === undefined ? runFile : dependencies.execFile
|
|
133
|
-
const
|
|
134
|
-
|
|
149
|
+
const environment = dependencies.environment === undefined ? process.env : dependencies.environment
|
|
150
|
+
const owner = await windowsOwnerSid(run, environment)
|
|
151
|
+
assertRestrictedWindowsAcl(await readWindowsAcl(path, run, environment), owner)
|
|
135
152
|
}
|
|
@@ -3,7 +3,7 @@ import { execFile, spawn } from 'node:child_process'
|
|
|
3
3
|
import { promisify } from 'node:util'
|
|
4
4
|
import { lstat, mkdtemp, readFile, rename, rm } from 'node:fs/promises'
|
|
5
5
|
import { join, win32 } from 'node:path'
|
|
6
|
-
import { assertAccountAncestors, currentAccountHome, ensureAccountDirectory } from './broker-account-storage.mjs'
|
|
6
|
+
import { assertAccountAncestors, currentAccountHome, ensureAccountDirectory, windowsSystemExecutable } from './broker-account-storage.mjs'
|
|
7
7
|
import { pathToFileURL } from 'node:url'
|
|
8
8
|
import { accountBrokerDirectory } from './broker-credentials.mjs'
|
|
9
9
|
import { createBrokerTransport } from './broker-transport.mjs'
|
|
@@ -70,7 +70,8 @@ export function windowsNpmEntry(output) {
|
|
|
70
70
|
|
|
71
71
|
async function npmInvocation(environment) {
|
|
72
72
|
if (process.platform !== 'win32') return { executable: 'npm', args: [] }
|
|
73
|
-
const found = await runFile('where.exe', ['npm'],
|
|
73
|
+
const found = await runFile(windowsSystemExecutable('where.exe', environment), ['npm'],
|
|
74
|
+
{ env: environment, shell: false, windowsHide: true, timeout: LOOKUP_TIMEOUT_MS })
|
|
74
75
|
const entry = windowsNpmEntry(found.stdout)
|
|
75
76
|
for (const path of [entry.command, entry.script]) {
|
|
76
77
|
await assertAccountAncestors(win32.dirname(path), 'win32')
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: '把一个目标编译为可执行、可验证、可追溯的工程
|
|
|
5
5
|
|
|
6
6
|
# Blueprint Skill
|
|
7
7
|
|
|
8
|
-
Package version: v7.0.
|
|
8
|
+
Package version: v7.0.41
|
|
9
9
|
|
|
10
10
|
远端 Hermes 编译器版本:0.4.0(独立于 npm 包版本)
|
|
11
11
|
|
|
@@ -253,8 +253,7 @@ After `capabilities`, read `officialCatalog`. Default allowlist is official skil
|
|
|
253
253
|
| B3 | 业务模板库与粗粒度模式 | 规划中 | 当前没有模板操作,`template` 与 `coarseMode` 均不是受支持输入。 |
|
|
254
254
|
| B4 | 验收回传、开放问题闭环、Validator 桥接 | 部分实现 | `acceptance-report` 已逐项核对共享 TestEvidence;`answer-questions` 与自动调用 Validator 仍未实现。 |
|
|
255
255
|
|
|
256
|
-
只调用 `capabilities` 返回的六个操作。不要根据规划中条目构造请求,也不要把 npm 包版本 `v7.0.
|
|
257
|
-
|
|
256
|
+
只调用 `capabilities` 返回的六个操作。不要根据规划中条目构造请求,也不要把 npm 包版本 `v7.0.41` 与远端 Hermes 编译器版本 `0.4.0` 混为一谈。
|
|
258
257
|
|
|
259
258
|
## 机器任务与验收回传
|
|
260
259
|
|
|
@@ -323,13 +322,13 @@ After `capabilities`, read `officialCatalog`. Default allowlist is official skil
|
|
|
323
322
|
3. 自报、回复送达和动作完成不等于工程交付验证。reported始终待验收;Swarm接受工程任务时复用Validator校验签名、有效期、计划/产物/任务绑定。无证据、伪造runner或失败检查不得成为绿色完成。
|
|
324
323
|
4. 原任务交接前保存检查点并释放旧锁;回程只发持久通知,宿主消费后重新核验基线、快照与写入权限。历史恢复结果不是新授权。技能不能自行唤醒未接入的IDE。
|
|
325
324
|
5. 心跳停止仅允许自动回收尚未开工的assigned任务;claimed/running进入执行结果待核对状态,禁止盲目重复执行。已回传、已验收、失败和取消任务不会被自动重派。服务器停滞回收同时保存会员通知,对话界面定期读取展示。
|
|
326
|
-
6.
|
|
327
|
-
7. 云端沙箱开关按调用会员读取;关闭时仅允许当前受审官方源码摘要在受控worker中直接执行,并记录executionIsolation
|
|
325
|
+
6. 读取预算、截止和续时确认仅在云端沙箱已开启且本任务实际使用 sandbox 时生效。纯本地或权威响应确认的非沙箱执行,在已授权目标和范围内自动持续,不因旧预算过期、文件数或token额度暂停,也不生成扩展或续时确认;宿主可保留budget-read审计。远端状态未知时只读查询原调用,不推定关闭,不要求扩预算;纯本地无需查询云端。仅实际沙箱内预计长任务在预算初始化后、深读前提出一次精确自动续时策略,真实授权后才自动续时;时间、文件数、token和写入权限分别计量,额度/次数耗尽、撤销和完成保留明确停止规则。读取预算不是付费充值,续时由宿主在读取时触发。
|
|
326
|
+
7. 云端沙箱开关按调用会员读取;关闭时仅允许当前受审官方源码摘要在受控worker中直接执行,并记录executionIsolation。预算上下文取同一任务成功runtime响应顶层executionContext,包含cloudSandboxEnabled与executionIsolation;仅true且sandbox启用预算,不用模型自报、调用方参数或旧截图代替真实执行状态。纯本地显式使用{executionIsolation:"local"};已有预算通过budget-context绑定或更新实际环境,保留原用量、期限和授权。未知或修改过的源码明确要求sandbox,不伪造隔离结果;worker直接执行不是OS沙箱。
|
|
328
327
|
8. 使用技能前检查官方发布版本并自动升级可管理的安装副本与客户端;配置失败或升级失败停止并报告。通过configure的JSON标准输入导入一次账号凭据,后续项目/分支/任务共享;密钥不进入源码、URL或命令参数。显式环境覆盖必须是绝对路径。网页复制在点击时获取当前凭据,页面仅展示无密钥地址;已撤销密钥没有权限自动获取新密钥,需已认证网页重新同步一次。
|
|
329
328
|
|
|
330
|
-
English: keep simple work with the main agent. Delegate only substantial independent business deliverables when benefits exceed coordination costs; use the fewest needed agents, never create idle management agents. Enabling Aimlock does not authorize delegation. Finish all authorized work; a cost complaint is not a stop request. Preserve the original goal; verify execution evidence; resume from durable checkpoints; never replay uncertain side effects. Read
|
|
329
|
+
English: keep simple work with the main agent. Delegate only substantial independent business deliverables when benefits exceed coordination costs; use the fewest needed agents, never create idle management agents. Enabling Aimlock does not authorize delegation. Finish all authorized work; a cost complaint is not a stop request. Preserve the original goal; verify execution evidence; resume from durable checkpoints; never replay uncertain side effects. Read budgets and bounded renewal approvals apply only when cloud sandboxing is enabled and actually used for this task. Local or confirmed non-sandbox work continues within its authorized scope without budget prompts; unknown remote state requires a read-only query, never an assumed opt-out or an extension request. Reuse account credentials and check managed skill updates before use; revoked credentials require an authenticated refresh.
|
|
331
330
|
|
|
332
|
-
Русский: сохраняйте исходную цель, проверяйте доказательства выполнения и возобновляйте работу из сохранённой точки. Не повторяйте операции с неизвестным результатом.
|
|
331
|
+
Русский: сохраняйте исходную цель, проверяйте доказательства выполнения и возобновляйте работу из сохранённой точки. Не повторяйте операции с неизвестным результатом. Бюджет чтения и ограниченное разрешение на продление действуют только при включённой облачной песочнице, фактически используемой этой задачей. Локальная работа и подтверждённое выполнение без песочницы продолжаются в разрешённых пределах без запросов бюджета; неизвестное удалённое состояние требует запроса только для чтения, а не предположения об отключении или запроса расширения. Ключи учётной записи используются повторно, обновления навыков проверяются перед вызовом.
|
|
333
332
|
|
|
334
333
|
## 账号共享凭据与自动更新
|
|
335
334
|
|
package/skill/skill.json
CHANGED