cli-swarm 7.0.38 → 7.0.39
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 +14 -0
- package/broker-account-storage.mjs +135 -0
- package/broker-credentials.mjs +125 -0
- package/broker.mjs +13 -93
- package/installer-storage.mjs +205 -0
- package/installer.mjs +48 -63
- package/official-skill-update.mjs +151 -0
- package/package.json +10 -1
- package/skill/SKILL.md +72 -134
- package/skill/references/ops-heartbeat.md +3 -3
- package/skill/references/org-chart.md +5 -26
- package/skill/references/task-lifecycle.md +10 -2
- package/skill/references/traffic-light.md +3 -3
- package/skill/skill.json +1 -1
- package/swarm-coordinator-model.mjs +1 -0
- package/swarm-runtime.mjs +45 -111
- package/swarm-task-contract.mjs +8 -0
- package/swarm-task-handoff.mjs +19 -3
- package/swarm-task-policy.mjs +142 -0
- package/swarm-task-routing.mjs +3 -0
package/installer.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { latestOfficialSkillContext } from './official-skill-update.mjs'
|
|
2
|
+
import { installTarget, writeManagedSkill } from './installer-storage.mjs'
|
|
3
|
+
export { installTarget, readInstallMeta } from './installer-storage.mjs'
|
|
4
|
+
import { configureBrainClientCredential } from './broker-credentials.mjs'
|
|
1
5
|
/**
|
|
2
6
|
* 八个官方技能共用这一份安装器。packages/*-cli/installer.mjs 必须与本文件字节一致。
|
|
3
7
|
* 禁止第二套超时、第二套版本来源、第二套 bin 名。
|
|
4
8
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { dirname, join
|
|
9
|
+
import { readFileSync } from 'node:fs'
|
|
10
|
+
import { writeFile } from 'node:fs/promises'
|
|
11
|
+
import { dirname, join } from 'node:path'
|
|
8
12
|
import { stdin, stdout } from 'node:process'
|
|
9
13
|
import { createInterface } from 'node:readline/promises'
|
|
10
14
|
import { fileURLToPath } from 'node:url'
|
|
@@ -33,7 +37,6 @@ export {
|
|
|
33
37
|
|
|
34
38
|
import { createBrokerTransport } from './broker-transport.mjs'
|
|
35
39
|
|
|
36
|
-
const INSTALL_META = 'install-meta.json'
|
|
37
40
|
const BROKER_STDIN_MAX_BYTES = 1_048_576
|
|
38
41
|
|
|
39
42
|
function asObject(value, label) {
|
|
@@ -81,19 +84,6 @@ export function loadOfficialSkillContext(packageRoot) {
|
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
export function readInstallMeta(target) {
|
|
85
|
-
const path = join(target, INSTALL_META)
|
|
86
|
-
if (!existsSync(path)) return null
|
|
87
|
-
return asObject(JSON.parse(readFileSync(path, 'utf8')), INSTALL_META)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function installTarget(skillName, explicit) {
|
|
91
|
-
if (explicit) return resolve(explicit)
|
|
92
|
-
const codexHome = process.env.CODEX_HOME?.trim()
|
|
93
|
-
if (codexHome) return join(codexHome, 'skills', skillName)
|
|
94
|
-
return join(process.cwd(), '.codex', 'skills', skillName)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
87
|
export async function fetchLatestVersion(context) {
|
|
98
88
|
const request = createBrokerTransport({ environment: process.env })
|
|
99
89
|
const response = await request(context.latestEndpoint, { signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS) })
|
|
@@ -105,51 +95,38 @@ export async function fetchLatestVersion(context) {
|
|
|
105
95
|
}
|
|
106
96
|
}
|
|
107
97
|
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
await rm(join(target, 'references'), { recursive: true, force: true })
|
|
113
|
-
await cp(context.skillDir, target, { recursive: true, force: true })
|
|
114
|
-
const installed = asObject(JSON.parse(readFileSync(join(target, 'skill.json'), 'utf8')), 'installed skill.json')
|
|
115
|
-
const installedVersion = requiredString(installed.version, 'installed skill.json version')
|
|
116
|
-
await writeFile(join(target, INSTALL_META), `${JSON.stringify({
|
|
117
|
-
source: context.runtimeCode,
|
|
118
|
-
slug: context.skillName,
|
|
119
|
-
version: installedVersion,
|
|
120
|
-
packageVersion: context.packageVersion,
|
|
121
|
-
endpoint: context.endpoint,
|
|
122
|
-
installedAt: new Date().toISOString(),
|
|
123
|
-
}, null, 2)}\n`)
|
|
124
|
-
if (previous?.version && previous.version !== installedVersion) {
|
|
125
|
-
console.log(`${context.displayName} skill updated: ${target}`)
|
|
126
|
-
console.log(` ${previous.version} → ${installedVersion}`)
|
|
127
|
-
} else {
|
|
128
|
-
console.log(`${context.displayName} skill installed: ${target} (${installedVersion})`)
|
|
129
|
-
}
|
|
130
|
-
console.log('Next: return to your IDE and state the goal. The agent reads the installed SKILL.md.')
|
|
98
|
+
function writeInstallationResult(runtime, value) {
|
|
99
|
+
const line = JSON.stringify(value) + '\n'
|
|
100
|
+
if (runtime.writeOutput === undefined) process.stdout.write(line)
|
|
101
|
+
else runtime.writeOutput(line)
|
|
131
102
|
}
|
|
132
103
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
process.
|
|
104
|
+
function installationDependencies(dependencies) {
|
|
105
|
+
return dependencies === undefined ? brokerDependencies() : dependencies
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function installOfficialSkill(context, explicit, dependencies) {
|
|
109
|
+
const runtime = installationDependencies(dependencies)
|
|
110
|
+
const selected = await latestOfficialSkillContext(context, runtime)
|
|
111
|
+
const environment = runtime.environment === undefined ? process.env : runtime.environment
|
|
112
|
+
const workingDirectory = runtime.workingDirectory === undefined ? process.cwd() : runtime.workingDirectory
|
|
113
|
+
const target = installTarget(selected.skillName, explicit, environment, workingDirectory)
|
|
114
|
+
const installed = await writeManagedSkill(selected, target, { ...runtime, allowCreate: true })
|
|
115
|
+
if (installed.status === 'skipped') throw new Error('Skill install refused: ' + installed.reason)
|
|
116
|
+
writeInstallationResult(runtime, { installed, reloadRequired: installed.status !== 'current' })
|
|
117
|
+
return installed
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function checkOfficialSkill(context, explicit, dependencies) {
|
|
121
|
+
const runtime = installationDependencies(dependencies)
|
|
122
|
+
const selected = await latestOfficialSkillContext(context, runtime)
|
|
123
|
+
const environment = runtime.environment === undefined ? process.env : runtime.environment
|
|
124
|
+
const workingDirectory = runtime.workingDirectory === undefined ? process.cwd() : runtime.workingDirectory
|
|
125
|
+
const target = installTarget(selected.skillName, explicit, environment, workingDirectory)
|
|
126
|
+
const installed = await writeManagedSkill(selected, target, runtime)
|
|
127
|
+
if (installed.status === 'skipped') throw new Error('Skill check could not update its managed target: ' + installed.reason)
|
|
128
|
+
writeInstallationResult(runtime, { installed, reloadRequired: installed.status === 'updated' })
|
|
129
|
+
return installed
|
|
153
130
|
}
|
|
154
131
|
|
|
155
132
|
export function defaultUsage(context, extraLines) {
|
|
@@ -157,10 +134,12 @@ export function defaultUsage(context, extraLines) {
|
|
|
157
134
|
`${context.npmName} — install and run the ${context.displayName} skill from CLI.Tax`,
|
|
158
135
|
'',
|
|
159
136
|
'Usage:',
|
|
137
|
+
' configure < credential.json',
|
|
138
|
+
' Store the Brain Client credential for this account; never put tokens in command arguments.',
|
|
160
139
|
` npx ${context.npmName}@latest install [directory]`,
|
|
161
140
|
` Install the ${context.displayName} skill for the current IDE.`,
|
|
162
141
|
` npx ${context.npmName}@latest check [directory]`,
|
|
163
|
-
' Check
|
|
142
|
+
' Check and atomically update an already managed skill to the current official release.',
|
|
164
143
|
` npx ${context.npmName}@latest run`,
|
|
165
144
|
" Run this skill's applicability or onboarding flow; only a real HTTP invocation can trigger automatic evaluation.",
|
|
166
145
|
` npx ${context.npmName}@latest invoke <operation> <JSON-object>`,
|
|
@@ -169,7 +148,8 @@ export function defaultUsage(context, extraLines) {
|
|
|
169
148
|
' Read one {"operation":"...","input":{...}} request from JSON stdin.',
|
|
170
149
|
` npx ${context.npmName}@latest recover <operation> <requestId>`,
|
|
171
150
|
' Query an uncertain invocation without resending or charging again.',
|
|
172
|
-
'Credential:
|
|
151
|
+
'Credential: configure reads a token-file JSON document from stdin and stores it once for the current account.',
|
|
152
|
+
'An explicit CLITAX_BRAIN_CLIENT_TOKEN_FILE must be absolute. Revoked keys require a fresh authenticated copy from CLI.Tax.',
|
|
173
153
|
`Endpoint: ${context.endpoint}`,
|
|
174
154
|
]
|
|
175
155
|
if (extraLines?.length) lines.push('', ...extraLines)
|
|
@@ -270,7 +250,12 @@ export async function dispatchOfficialSkillCli(options) {
|
|
|
270
250
|
const command = args[0] ?? 'help'
|
|
271
251
|
const argument = args[1]
|
|
272
252
|
try {
|
|
273
|
-
if (command === '
|
|
253
|
+
if (command === 'configure') {
|
|
254
|
+
if (args.length !== 1) throw new Error('configure accepts credentials only through JSON stdin')
|
|
255
|
+
const configured = await configureBrainClientCredential(await readBrokerSource(stdin))
|
|
256
|
+
process.stdout.write(JSON.stringify(configured) + '\n')
|
|
257
|
+
}
|
|
258
|
+
else if (command === 'install') await installOfficialSkill(context, argument)
|
|
274
259
|
else if (command === 'check') await checkOfficialSkill(context, argument)
|
|
275
260
|
else if (command === 'run') await options.runCommand(context)
|
|
276
261
|
else if (command === 'recover') await runBrokerRecovery(context, args)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { refreshManagedSkillCopies } from './installer-storage.mjs'
|
|
2
|
+
import { execFile, spawn } from 'node:child_process'
|
|
3
|
+
import { promisify } from 'node:util'
|
|
4
|
+
import { lstat, mkdtemp, readFile, rename, rm } from 'node:fs/promises'
|
|
5
|
+
import { join, win32 } from 'node:path'
|
|
6
|
+
import { assertAccountAncestors, currentAccountHome, ensureAccountDirectory } from './broker-account-storage.mjs'
|
|
7
|
+
import { pathToFileURL } from 'node:url'
|
|
8
|
+
import { accountBrokerDirectory } from './broker-credentials.mjs'
|
|
9
|
+
import { createBrokerTransport } from './broker-transport.mjs'
|
|
10
|
+
|
|
11
|
+
const runFile = promisify(execFile)
|
|
12
|
+
export const LOOKUP_TIMEOUT_MS = 8000
|
|
13
|
+
const INSTALL_TIMEOUT_MS = 120_000
|
|
14
|
+
const RELEASE_PATTERN = /^(?:v)?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/
|
|
15
|
+
const OFFICIAL_IDENTITIES = Object.freeze({
|
|
16
|
+
'cli-aimlock': 'R3mQ8kWpXn', 'cli-blueprint': 'wvz6zmRWmX', 'cli-calctool': 'KKyA6xljUX',
|
|
17
|
+
'cli-swarm': 'zj7fTPVh4p', 'cli-validator': 'Xx9ZkQmW3p', 'cli-confirm-protocol': 'Cf8Pr7Tm2Q',
|
|
18
|
+
'cli-archguard': 'Ag4Ch8Rd2K', 'cli-mergeguard': 'Mm7GnPqR2v',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
function releaseVersion(value) {
|
|
22
|
+
if (typeof value !== 'string' || !RELEASE_PATTERN.test(value)) throw new Error('Official release version must be X.Y.Z')
|
|
23
|
+
return value.replace(/^v/, '')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function inspectOfficialRelease(context, dependencies = {}) {
|
|
27
|
+
if (OFFICIAL_IDENTITIES[context.npmName] !== context.runtimeCode || typeof context.packageRoot !== 'string'
|
|
28
|
+
|| context.endpoint !== 'https://cli.tax/' + context.runtimeCode) {
|
|
29
|
+
throw new Error('Official package identity is required for the update check')
|
|
30
|
+
}
|
|
31
|
+
const environment = dependencies.environment === undefined ? process.env : dependencies.environment
|
|
32
|
+
const request = dependencies.request === undefined ? createBrokerTransport({ environment }) : dependencies.request
|
|
33
|
+
const endpoint = 'https://cli.tax/api/public/skills/' + context.runtimeCode
|
|
34
|
+
if (!/^[A-Za-z0-9]{10}$/.test(context.runtimeCode)) throw new Error('Official runtime code is invalid')
|
|
35
|
+
const response = await request(endpoint, { redirect: 'error', signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS) })
|
|
36
|
+
if (!response.ok) throw new Error('Official release lookup failed: HTTP ' + response.status)
|
|
37
|
+
const payload = await response.json()
|
|
38
|
+
const version = releaseVersion(payload.version)
|
|
39
|
+
const local = releaseVersion(context.packageVersion)
|
|
40
|
+
const left = version.split('.').map(Number), right = local.split('.').map(Number)
|
|
41
|
+
const differing = left.findIndex((value, index) => value !== right[index])
|
|
42
|
+
if (differing !== -1 && left[differing] < right[differing]) {
|
|
43
|
+
throw new Error('The local development package is newer than the published release; refusing to downgrade or overwrite workspace sources')
|
|
44
|
+
}
|
|
45
|
+
return { version, current: local === version }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function validateCachedPackage(directory, context, version) {
|
|
49
|
+
const packageRoot = join(directory, 'node_modules', context.npmName)
|
|
50
|
+
await assertAccountAncestors(packageRoot)
|
|
51
|
+
const status = await lstat(packageRoot)
|
|
52
|
+
if (!status.isDirectory() || status.isSymbolicLink()) throw new Error('Cached official package must be a regular directory')
|
|
53
|
+
const manifest = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'))
|
|
54
|
+
const skill = JSON.parse(await readFile(join(packageRoot, 'skill', 'skill.json'), 'utf8'))
|
|
55
|
+
if (manifest.name !== context.npmName || manifest.version !== version || releaseVersion(skill.version) !== version
|
|
56
|
+
|| skill.name !== context.skillName || skill.endpoint !== context.endpoint || typeof skill.schemaVersion !== 'string') {
|
|
57
|
+
throw new Error('Installed official package identity does not match the published release')
|
|
58
|
+
}
|
|
59
|
+
return { ...context, packageRoot, packageVersion: version, skillVersion: skill.version,
|
|
60
|
+
schemaVersion: skill.schemaVersion, skillDir: join(packageRoot, 'skill') }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function windowsNpmEntry(output) {
|
|
64
|
+
const commands = output.split(/\r?\n/).map(line => line.trim()).filter(line => /\\npm\.cmd$/i.test(line))
|
|
65
|
+
if (!commands.length || !win32.isAbsolute(commands[0]) || /[\u0000-\u001f]/.test(commands[0])) {
|
|
66
|
+
throw new Error('where.exe did not return an absolute npm.cmd location')
|
|
67
|
+
}
|
|
68
|
+
return { command: commands[0], script: win32.join(win32.dirname(commands[0]), 'node_modules', 'npm', 'bin', 'npm-cli.js') }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function npmInvocation(environment) {
|
|
72
|
+
if (process.platform !== 'win32') return { executable: 'npm', args: [] }
|
|
73
|
+
const found = await runFile('where.exe', ['npm'], { env: environment, windowsHide: true, timeout: LOOKUP_TIMEOUT_MS })
|
|
74
|
+
const entry = windowsNpmEntry(found.stdout)
|
|
75
|
+
for (const path of [entry.command, entry.script]) {
|
|
76
|
+
await assertAccountAncestors(win32.dirname(path), 'win32')
|
|
77
|
+
const status = await lstat(path)
|
|
78
|
+
if (!status.isFile() || status.isSymbolicLink()) throw new Error('Windows npm entry must be a regular file without symlink ancestors')
|
|
79
|
+
}
|
|
80
|
+
return { executable: process.execPath, args: [entry.script] }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function installPackage(directory, context, version, environment) {
|
|
84
|
+
if (typeof environment.PATH !== 'string' || !environment.PATH) throw new Error('Package installation PATH is required')
|
|
85
|
+
const childEnvironment = { PATH: environment.PATH, HOME: currentAccountHome() }
|
|
86
|
+
for (const name of ['HTTPS_PROXY', 'HTTP_PROXY', 'NO_PROXY', 'NODE_EXTRA_CA_CERTS', 'SystemRoot', 'COMSPEC', 'PATHEXT']) {
|
|
87
|
+
if (environment[name] !== undefined) childEnvironment[name] = environment[name]
|
|
88
|
+
}
|
|
89
|
+
const invocation = await npmInvocation(childEnvironment)
|
|
90
|
+
await new Promise((accept, reject) => {
|
|
91
|
+
const child = spawn(invocation.executable, [...invocation.args, 'install', '--prefix', directory,
|
|
92
|
+
'--ignore-scripts', '--no-audit', '--no-fund', '--package-lock=false', '--save-exact',
|
|
93
|
+
'--registry=https://registry.npmjs.org', context.npmName + '@' + version],
|
|
94
|
+
{ env: childEnvironment, stdio: 'ignore', timeout: INSTALL_TIMEOUT_MS })
|
|
95
|
+
child.once('error', reject)
|
|
96
|
+
child.once('exit', (code, signal) => code === 0 ? accept()
|
|
97
|
+
: reject(new Error('Official package update failed: exit=' + code + ' signal=' + signal)))
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function latestOfficialSkillContext(context, dependencies = {}) {
|
|
102
|
+
const release = await inspectOfficialRelease(context, dependencies)
|
|
103
|
+
if (release.current) return context
|
|
104
|
+
const environment = dependencies.environment === undefined ? process.env : dependencies.environment
|
|
105
|
+
const home = dependencies.homeDirectory === undefined ? currentAccountHome() : dependencies.homeDirectory
|
|
106
|
+
const directory = join(accountBrokerDirectory(environment, process.platform, home), 'packages')
|
|
107
|
+
await ensureAccountDirectory(directory, dependencies)
|
|
108
|
+
const target = join(directory, context.npmName + '-' + release.version)
|
|
109
|
+
try {
|
|
110
|
+
await lstat(target)
|
|
111
|
+
return await validateCachedPackage(target, context, release.version)
|
|
112
|
+
} catch (error) { if (error.code !== 'ENOENT') throw error }
|
|
113
|
+
const staged = await mkdtemp(join(directory, '.update-'))
|
|
114
|
+
try {
|
|
115
|
+
const install = dependencies.installPackage === undefined ? installPackage : dependencies.installPackage
|
|
116
|
+
await install(staged, context, release.version, environment)
|
|
117
|
+
await validateCachedPackage(staged, context, release.version)
|
|
118
|
+
try { await rename(staged, target) } catch (error) {
|
|
119
|
+
if (!['EEXIST', 'ENOTEMPTY'].includes(error.code)) throw error
|
|
120
|
+
await validateCachedPackage(target, context, release.version)
|
|
121
|
+
}
|
|
122
|
+
return await validateCachedPackage(target, context, release.version)
|
|
123
|
+
} finally { await rm(staged, { recursive: true, force: true }) }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function latestOfficialModule(context, filename, dependencies) {
|
|
127
|
+
const updated = await latestOfficialSkillContext(context, dependencies)
|
|
128
|
+
if (updated.packageRoot === context.packageRoot) return null
|
|
129
|
+
return { context: updated, module: await import(pathToFileURL(join(updated.packageRoot, filename)).href) }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function withUpgradeMetadata(result, upgrade) {
|
|
133
|
+
if (result.upgrade === undefined) return { ...result, upgrade }
|
|
134
|
+
return { ...result, upgrade: { ...result.upgrade, previousVersion: upgrade.previousVersion,
|
|
135
|
+
runtimeUpdated: upgrade.runtimeUpdated || result.upgrade.runtimeUpdated,
|
|
136
|
+
reloadRequired: upgrade.reloadRequired || result.upgrade.reloadRequired,
|
|
137
|
+
managedCopies: [...upgrade.managedCopies, ...result.upgrade.managedCopies] } }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function prepareOfficialSkillUse(context, filename, dependencies) {
|
|
141
|
+
const selected = await latestOfficialSkillContext(context, dependencies)
|
|
142
|
+
const managedCopies = await refreshManagedSkillCopies(selected, dependencies)
|
|
143
|
+
const runtimeUpdated = selected.packageRoot !== context.packageRoot
|
|
144
|
+
const changedCopies = managedCopies.filter(item => item.status === 'updated')
|
|
145
|
+
const upgrade = { previousVersion: context.packageVersion, version: selected.packageVersion, runtimeUpdated,
|
|
146
|
+
reloadRequired: runtimeUpdated || changedCopies.length > 0, managedCopies,
|
|
147
|
+
documentationPaths: changedCopies.map(item => item.documentationPath) }
|
|
148
|
+
if (runtimeUpdated) upgrade.documentationPaths.push(join(selected.skillDir, 'SKILL.md'))
|
|
149
|
+
return { context: selected, upgrade,
|
|
150
|
+
module: runtimeUpdated ? await import(pathToFileURL(join(selected.packageRoot, filename)).href) : null }
|
|
151
|
+
}
|
package/package.json
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
"bin": {
|
|
3
3
|
"cli-swarm": "./cli.mjs"
|
|
4
4
|
},
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"cli-validator": "7.0.39"
|
|
7
|
+
},
|
|
5
8
|
"description": "Swarm skill installer for CLI.Tax: orchestrate N sub-agents with persistent AutoCoord locks and dependency waits.",
|
|
6
9
|
"exports": {
|
|
7
10
|
"./coordinator": "./swarm-coordinator.mjs",
|
|
@@ -11,6 +14,10 @@
|
|
|
11
14
|
"files": [
|
|
12
15
|
"cli.mjs",
|
|
13
16
|
"installer.mjs",
|
|
17
|
+
"installer-storage.mjs",
|
|
18
|
+
"broker-account-storage.mjs",
|
|
19
|
+
"broker-credentials.mjs",
|
|
20
|
+
"official-skill-update.mjs",
|
|
14
21
|
"broker.mjs",
|
|
15
22
|
"broker-failures.mjs",
|
|
16
23
|
"broker-recovery.mjs",
|
|
@@ -28,6 +35,8 @@
|
|
|
28
35
|
"swarm-task-routing.mjs",
|
|
29
36
|
"swarm-task-handoff.mjs",
|
|
30
37
|
"swarm-runtime.mjs",
|
|
38
|
+
"swarm-task-policy.mjs",
|
|
39
|
+
"swarm-task-contract.mjs",
|
|
31
40
|
"skill/references/org-chart.md",
|
|
32
41
|
"skill/references/task-lifecycle.md",
|
|
33
42
|
"skill/references/traffic-light.md",
|
|
@@ -43,5 +52,5 @@
|
|
|
43
52
|
"url": "https://github.com/88208555/swarm-clitax.git"
|
|
44
53
|
},
|
|
45
54
|
"type": "module",
|
|
46
|
-
"version": "7.0.
|
|
55
|
+
"version": "7.0.39"
|
|
47
56
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swarm
|
|
3
|
-
description: '
|
|
3
|
+
description: '按业务必要性编排实质性独立任务,默认主代理执行;确需委派时才创建最少子代理,提供派单、证据验收、持久任务与冲突协调。Use for justified independent business work with task dispatch, evidence acceptance and durable coordination; keep simple work with the main agent.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# swarm
|
|
7
7
|
|
|
8
|
-
Package version: v7.0.
|
|
8
|
+
Package version: v7.0.39
|
|
9
9
|
|
|
10
10
|
把「项目需求」编排为一支可观测、可自治、可安全运转的智能体蜂群。
|
|
11
11
|
|
|
@@ -13,151 +13,60 @@ Endpoint: https://cli.tax/zj7fTPVh4p
|
|
|
13
13
|
|
|
14
14
|
Request schema: `swarm.skill.request/1.0`
|
|
15
15
|
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
**关键**:老板一句话 → 组织架构 → 任务派单 → 红绿灯执行 → 运维自治 + 安全守卫 → 可运转蜂群。全程框架不变,换项目只换 JSON。
|
|
44
|
-
|
|
45
|
-
## 何时使用
|
|
46
|
-
|
|
47
|
-
- 用户有多个可并行/依赖编排的子任务(模块迁移、批量审核、多端开发、数据清洗、并行研究)
|
|
48
|
-
- 用户需要企业级分工、任务认领回传、进度红绿灯的可观测协作
|
|
49
|
-
- 用户需要自动回收卡死智能体并让继任者继承任务的自治能力
|
|
50
|
-
- 用户需要内置安全守卫(防注入、异常警报)的多智能体系统
|
|
51
|
-
- 用户希望任务先由 Blueprint 技能规划为可追溯蓝图,再交给蜂群执行(可选协同)
|
|
52
|
-
|
|
53
|
-
不要用于:单智能体就能完成的简单任务(用单 agent 即可);与任务编排无关的纯计算。
|
|
54
|
-
|
|
55
|
-
## Blueprint 协同(可选)
|
|
56
|
-
|
|
57
|
-
intake 时可选择 `blueprintEnabled`:任务先交给 Blueprint 技能规划为可追溯的工程蓝图
|
|
58
|
-
(结构/引用/验收全部闭合),再回到蜂群派单执行。开启后 org-chart 的下一步是 `blueprint-bridge`,
|
|
59
|
-
由它生成 blueprint 请求负载(`https://cli.tax/wvz6zmRWmX`,operation `compile-inline`),
|
|
60
|
-
`blueprint-bridge` 生成合法的 `blueprint.ir/1.0` 与 Blueprint 请求信封;只有 `compile-inline` 成功后才继续
|
|
61
|
-
`dispatch → claim → report → accept`。桥接本身不发起网络请求。
|
|
62
|
-
|
|
63
|
-
## Official catalog hops
|
|
64
|
-
|
|
65
|
-
After `capabilities`, read `officialCatalog`. Default allowlist is official skills. Call another skill only when its capability matches this demand. User-named extras enter only when the user names them; then confirm that skill's capabilities before invoke. Do not call chain-unrelated or self-extended skills.
|
|
66
|
-
|
|
67
|
-
## 核心原则
|
|
68
|
-
|
|
69
|
-
1. **组织即规则**:协作结构 = 企业级组织架构(决策/管理/执行三层),派单、审批、汇报都遵循层级规则。
|
|
70
|
-
2. **事实分层**:远端纯运行时仍由调用方回传 `tasks`;本地 AutoCoord 的锁、等待、事件和任务卡只认 `.coord/` 台账,禁止依赖对话上下文。
|
|
71
|
-
3. **红绿灯透明**:每个任务/智能体实时红/黄/绿状态,进度与错误持续上报,不隐藏阻塞。
|
|
72
|
-
4. **运维自治**:心跳停止/卡死 = 自动收回 + 派新智能体 + 继承任务续跑,不中断整体。
|
|
73
|
-
5. **安全守卫**:恶意注入、危险指令、越权请求在进入执行前被拦截并触发警报。
|
|
74
|
-
6. **ArchGuard 块级证据**:仅当任务启用了架构合同,worker 每完成一个真实代码块就先执行 checkpoint;report 必须携带 contract digest、ledger entry digest、漂移灯和回滚结果,红灯任务禁止 accept。无合同的存量项目不伪造 checkpoint。
|
|
75
|
-
7. **等待必须声明**:跨任务等待先登记 `dependency-wait`;挂起期间禁止读取,事件到达/任务死亡/超时/依赖成环都必须有明确出口。
|
|
76
|
-
|
|
77
|
-
## 五步实施流程
|
|
78
|
-
|
|
79
|
-
### 1. 组织架构(org-chart)
|
|
80
|
-
生成三层规则:
|
|
81
|
-
- 决策层:老板 / 主智能体(定目标、拆任务、验收)
|
|
82
|
-
- 管理层:调度智能体(派单)+ 运维智能体(心跳/回收/接替)+ 安全守卫(检测/警报)+ 协调器(冲突/锁/等待/唤醒)
|
|
83
|
-
- 执行层:N 个按需创建的子智能体(各自认领任务、执行、回传)
|
|
84
|
-
|
|
85
|
-
### 2. 任务编排(dispatch / claim / report / accept)
|
|
86
|
-
读取项目 JSON:
|
|
87
|
-
- `dispatch`:只派发依赖全部存在且已经 `accepted` 的 backlog 任务
|
|
88
|
-
- `claim`:子智能体认领任务(同一任务不可被重复认领)
|
|
89
|
-
- `report`:执行完成回传结果和 `cli.tax.test-evidence/1.0` 证据
|
|
90
|
-
- `accept`:只允许 `board` 调用;缺少合法且 `exitCode: 0` 的 TestEvidence 时阻断
|
|
91
|
-
|
|
92
|
-
`org-chart` 可接收 `tasks`,用无环依赖图的最大层宽给出 `recommendedWorkerCount`(上限 50)。缺失依赖或依赖环会阻断组织架构,不会静默采用用户输入的 worker 数。
|
|
93
|
-
|
|
94
|
-
### 3. 红绿灯(traffic-light)
|
|
95
|
-
- 🟢 green:任务已回传或验收,且全部 TestEvidence 结构合法、`exitCode` 为 0
|
|
96
|
-
- 🟡 yellow:未完成,或已回传但缺少通过证据
|
|
97
|
-
- 🔴 red:阻塞 / 失败 / 智能体心跳停止
|
|
98
|
-
- 调用方传入最新完整状态后可查询;运行时不保存事件流、不主动推送
|
|
99
|
-
|
|
100
|
-
### 4. 运维接管(ops)
|
|
101
|
-
- 固定运维智能体监控所有子智能体心跳
|
|
102
|
-
- 心跳超时/卡死 → 标记死亡 → 自主收回任务
|
|
103
|
-
- 派遣新智能体接替 → **继承原任务列表**(含已回传部分)继续执行
|
|
104
|
-
- 全程不中断其他智能体
|
|
105
|
-
|
|
106
|
-
### 5. 安全守卫(security-guard)
|
|
107
|
-
- 固定安全智能体扫描:
|
|
108
|
-
- 提示词注入(prompt injection)检测
|
|
109
|
-
- 危险指令(删除/越权/提权/外泄)检测
|
|
110
|
-
- 异常行为(高频重试/异常输入)触发警报
|
|
111
|
-
- 拦截结果进入审计日志,老板可查看
|
|
112
|
-
|
|
113
|
-
## 状态持久化边界
|
|
114
|
-
|
|
115
|
-
远端运行时是纯函数;本地 `cli-swarm local` 使用仓库 `.coord/` 作为唯一协调事实源。任务卡、锁、队列、事件、等待、裁决与审计由协调器原子写入,聊天只能引用这些记录。调用方自己的任务视图可保存为:
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
swarm-run/
|
|
119
|
-
├── org-chart.json # 组织架构规则(三层)
|
|
120
|
-
├── project.json # 项目需求(唯一事实源)
|
|
121
|
-
├── tasks.json # 任务包(派单/认领/回传状态)
|
|
122
|
-
├── traffic-light.json # 红绿灯状态快照
|
|
123
|
-
├── ops-audit.json # 运维接管记录(回收/接替/继承)
|
|
124
|
-
├── security-audit.json # 安全守卫记录(拦截/警报)
|
|
125
|
-
└── reports/ # 各智能体回传结果
|
|
126
|
-
```
|
|
16
|
+
## 创建前强制判断
|
|
17
|
+
|
|
18
|
+
默认单代理。仅因文件多、启用智能目标、调用蜂群、方便汇总或想省事,均不得创建子代理。先遵守下方执行完整性共同规则,记录业务必要性;简单查询、改名、少量修改、单条命令和例行检查由主代理完成。
|
|
19
|
+
|
|
20
|
+
管理、调度、运维、安全守卫、协调器是逻辑职责,由主代理承担。组织图不会要求为这些职责创建额外智能体。用户禁止委派时只使用本地台账与门禁。
|
|
21
|
+
|
|
22
|
+
## 按需编排
|
|
23
|
+
|
|
24
|
+
1. 调用 capabilities,仅加载与当前业务匹配的技能。主代理保留原目标和验收责任。
|
|
25
|
+
2. org-chart 根据每项任务的 facts 判断必要性;缺少业务事实时返回0个worker和single-agent。workerCount仅为0–50的上限,不能覆盖必要性、任务宽度与范围冲突检查。
|
|
26
|
+
3. 仅对批准的独立实质性交付创建最少执行者;先复用已有负责人。主代理必须同时有独立工作可推进,禁止空等、重复查阅和递归扩编。
|
|
27
|
+
4. dispatch再次验证facts和依赖证据;claim、report、accept依次推进,任何失败保持显式阻断。
|
|
28
|
+
5. 子代理回传后主代理整合并验收全部原始要求。成本抱怨不等于停止;不得以组织图、派单成功、部分测试或报告代替交付。
|
|
29
|
+
|
|
30
|
+
## 事实与验收
|
|
31
|
+
|
|
32
|
+
facts同时用于org-chart的tasks[].facts与dispatch的input.facts。它包含精确路径、原负责人、规模、并行安全以及delegation业务依据;具体合同见references/task-lifecycle.md。
|
|
33
|
+
|
|
34
|
+
reported始终为黄色。accept只允许board,必须有任务绑定且经Validator公钥验签、有效期、产物校验的TestEvidence;本地自报不能成为绿色。
|
|
35
|
+
|
|
36
|
+
Blueprint仅在需要蓝图时使用;blueprint-bridge生成请求,不代替实际编译或派单依据。有ArchGuard合同时报告绑定合同与代码块证据,红灯禁止验收。
|
|
37
|
+
|
|
38
|
+
## 连续性与协调
|
|
39
|
+
|
|
40
|
+
远端纯运行时由调用方传递完整tasks,本地AutoCoord以.coord台账为准。使用任务卡、冲突扫描、签名锁、基线握手与声明的依赖等待;挂起与重启后恢复原检查点。安全检查、心跳与运维由主代理或确定性服务承担,不另建监控智能体。
|
|
41
|
+
|
|
42
|
+
心跳停止仅回收未开工assigned任务;claimed/running等待核对原执行结果,禁止盲目重放。替换只复用健康执行者,没有合适执行者时主代理接手或明确记录阻碍,不能自动扩编。
|
|
127
43
|
|
|
128
44
|
## 实现状态
|
|
129
45
|
|
|
130
46
|
| ID | 能力 | 状态 | 边界 |
|
|
131
47
|
|---|---|---|---|
|
|
132
|
-
| S1 |
|
|
133
|
-
| S2 | 依赖闭包 | 已实现 |
|
|
134
|
-
| S3 |
|
|
135
|
-
| S4 |
|
|
136
|
-
| S5 | AutoCoord
|
|
137
|
-
| S6 |
|
|
138
|
-
|
|
139
|
-
Blueprint 桥接已生成远端可验证的完整 IR;`planningStatus` 是业务字段,不覆盖响应信封的 `status: succeeded`。
|
|
48
|
+
| S1 | 证据验收 | 已实现 | reported待验收;只有任务绑定可信签名通过才可接受。 |
|
|
49
|
+
| S2 | 依赖闭包 | 已实现 | 缺失依赖、重复ID和环阻断。 |
|
|
50
|
+
| S3 | 按需worker | 已实现 | 默认0;业务必要性、收益、独立范围和数量上限共同约束;实际创建由宿主执行。 |
|
|
51
|
+
| S4 | 完整状态 | 已实现 | 返回完整tasks;远端不持久化,调用方保存。 |
|
|
52
|
+
| S5 | AutoCoord | 已实现 | 本地台账、签名锁与基线握手,不额外创建管理智能体。 |
|
|
53
|
+
| S6 | 依赖等待 | 已实现 | 显式等待、超时与死锁处理。 |
|
|
140
54
|
|
|
141
55
|
## 参考文档
|
|
142
56
|
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
57
|
+
- references/org-chart.md:业务门槛、最少worker数量与逻辑角色。
|
|
58
|
+
- references/task-lifecycle.md:派单事实、状态迁移和Blueprint桥接。
|
|
59
|
+
- references/traffic-light.md:证据与红黄绿判定。
|
|
60
|
+
- references/ops-heartbeat.md:心跳与安全回收。
|
|
61
|
+
- references/security-guard.md:注入检测与明确边界。
|
|
62
|
+
- references/autocoord.md:持久台账、锁与依赖等待。
|
|
149
63
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
- 所有子智能体输入先过安全守卫(防注入/危险指令)
|
|
153
|
-
- 心跳/状态数据只由运维智能体修改,防伪造
|
|
154
|
-
- 任务回传结果进草稿/审计,不覆盖未验收数据
|
|
155
|
-
- 项目 JSON 中的敏感信息(密钥/凭据)不进入子智能体上下文
|
|
64
|
+
组织图只返回数据,不创建真实子代理或唤醒IDE;宿主必须执行上述门禁。敏感凭据不得进入子代理上下文。
|
|
156
65
|
|
|
157
66
|
## 受限调用与自动评价闭环
|
|
158
67
|
|
|
159
68
|
- IDE / 智能体必须通过本包 `invoke` 或 JSON-stdin `broker` 调用,不得直接拼装技能 HTTP 请求,也不得读取 BrainClient token。
|
|
160
|
-
- broker
|
|
69
|
+
- broker 默认读取账号共享凭据文件;显式 `CLITAX_BRAIN_CLIENT_TOKEN_FILE` 使用绝对路径覆盖;macOS/Linux 文件必须为当前 broker 账户所有且权限 `0600`,Windows 文件必须位于受限 `%LOCALAPPDATA%\CLI.Tax\broker` 目录。
|
|
161
70
|
- broker 只需要 Brain Client HTTPS、受限身份文件和调用方显式传入的路径,本身不需要完整磁盘访问。若要保证 IDE 无法读取身份文件,必须把 broker 放进独立低权限系统账户或沙箱服务,并只暴露受限 IPC;broker 与 IDE 同账户运行时,`0600` 不能隔离二者,禁止声称令牌已隔离。
|
|
162
71
|
- broker 只用 `Authorization: BrainClient …` 发起一次 runtime 请求。HTTP 成功后必须保留响应顶层原始 `feedbackReceiptId`、`feedbackInvocationId` 和 `feedbackEvaluation.digest`,不得生成、猜测、复用或跨调用转移。
|
|
163
72
|
- Brain Client 服务端必须严格绑定请求/响应的 `requestId` 和 `schemaVersion`,再根据真实状态、验证结果、服务端耗时与 findings 生成并持久化权威评分、评语和摘要。broker 不得生成分数或评语。
|
|
@@ -182,3 +91,32 @@ Blueprint 桥接已生成远端可验证的完整 IR;`planningStatus` 是业
|
|
|
182
91
|
用户明确要求在当前任务处理时,保留原目标;已由其他任务负责的范围需 `handoff-release` 完成安全交接,再重新验证 Aimlock 范围、快照与写入权限。禁止把别人的签名租约、预算或通过证据当作当前任务的新授权。归属不明只暂存新需求,不暂停原任务。
|
|
183
92
|
|
|
184
93
|
这些操作使用同一协调根目录的持久台账。宿主必须在新消息入口调用并消费结果;技能不能拦截未接入的 IDE,也不会自动创建会话、Git 分支、常驻服务或跨机器复制私密消息。
|
|
94
|
+
|
|
95
|
+
## 执行完整性共同规则
|
|
96
|
+
|
|
97
|
+
1. 工程目标、已接受范围和验收项必须持久化;新增需求先路由与合并,不能覆盖原目标。子任务有明确服务目标的理由,执行仅用本链已匹配技能。每次恢复读取 task-resume,核对剩余项、pending请求和continuationNotifications。
|
|
98
|
+
2. 默认由主代理完成工作,禁止为了省事创建子代理、把简单查找/改名/少量修改/单条命令/例行检查/汇总交接给多智能体,禁止为达到门槛拆分或夸大任务。启用Aimlock或Swarm模式不是创建授权,管理/运维/安全/协调是主代理职责,不额外创建常驻智能体。只有业务确需独立且实质性的交付、主代理同时有可推进的独立工作、预期收益严格高于上下文传递/协调/验收成本时才派单;复用已有合适负责人,用户禁止委派时不得创建。每次创建前记录业务理由、交付物、验收项、主代理工作、成本收益、精确路径和原负责人;只创建当前需要的最少数量,不预建空闲角色,不递归扩编或重复扫描。规模门槛200行/3文件/跨模块仅为必要条件,不能单独证明值得委派。主代理负责整合和完整验收,不把半成品当完成;预算抱怨不是停止指令。
|
|
99
|
+
3. 自报、回复送达和动作完成不等于工程交付验证。reported始终待验收;Swarm接受工程任务时复用Validator校验签名、有效期、计划/产物/任务绑定。无证据、伪造runner或失败检查不得成为绿色完成。
|
|
100
|
+
4. 原任务交接前保存检查点并释放旧锁;回程只发持久通知,宿主消费后重新核验基线、快照与写入权限。历史恢复结果不是新授权。技能不能自行唤醒未接入的IDE。
|
|
101
|
+
5. 心跳停止仅允许自动回收尚未开工的assigned任务;claimed/running进入执行结果待核对状态,禁止盲目重复执行。已回传、已验收、失败和取消任务不会被自动重派。服务器停滞回收同时保存会员通知,对话界面定期读取展示。
|
|
102
|
+
6. 预计长任务在预算初始化后、深读前提出一次精确自动续时策略;只有真实授权才自动续时。时间、文件数、token和写入权限分别计量;额度/次数耗尽、撤销和完成必须明确停止并说明下一步。读取预算不是付费充值,计时器由宿主运行。
|
|
103
|
+
7. 云端沙箱开关按调用会员读取;关闭时仅允许当前受审官方源码摘要在受控worker中直接执行,并记录executionIsolation。未知或修改过的源码明确要求sandbox,不伪造隔离结果;worker直接执行不是OS沙箱。
|
|
104
|
+
8. 使用技能前检查官方发布版本并自动升级可管理的安装副本与客户端;配置失败或升级失败停止并报告。通过configure的JSON标准输入导入一次账号凭据,后续项目/分支/任务共享;密钥不进入源码、URL或命令参数。显式环境覆盖必须是绝对路径。网页复制在点击时获取当前凭据,页面仅展示无密钥地址;已撤销密钥没有权限自动获取新密钥,需已认证网页重新同步一次。
|
|
105
|
+
|
|
106
|
+
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-time renewal requires bounded approval. Reuse account credentials and check managed skill updates before use; revoked credentials require an authenticated refresh.
|
|
107
|
+
|
|
108
|
+
Русский: сохраняйте исходную цель, проверяйте доказательства выполнения и возобновляйте работу из сохранённой точки. Не повторяйте операции с неизвестным результатом. Продление чтения требует ограниченного разрешения; ключи учётной записи используются повторно, обновления навыков проверяются перед вызовом.
|
|
109
|
+
|
|
110
|
+
## 账号共享凭据与自动更新
|
|
111
|
+
|
|
112
|
+
在已登录的能力市场复制安装入口,将内容粘贴给 IDE。页面只展示原地址,剪贴板会携带当前账号凭据。IDE 将四字段凭据 JSON 经标准输入交给 `npx cli-aimlock@latest configure`;不要放到命令参数、项目文件或日志中。一次配置供同一操作系统账号的所有项目、分支和任务使用,八个技能共享同一文件。
|
|
113
|
+
|
|
114
|
+
默认位置:macOS 为 `~/Library/Application Support/CLI.Tax/broker/credential.json`,Linux 为 `~/.local/share/CLI.Tax/broker/credential.json`,Windows 为 `%LOCALAPPDATA%\CLI.Tax\broker\credential.json`。显式 `CLITAX_BRAIN_CLIENT_TOKEN_FILE` 仍按绝对路径覆盖默认位置;迁移旧 IDE 配置时移除其过时覆盖,再使用账号共享文件。macOS/Linux 校验当前账号所有权和0600权限;Windows校验仅当前账号与SYSTEM可访问的ACL。
|
|
115
|
+
|
|
116
|
+
每次新技能调用先查询官方发布版本,精确版本下载并校验身份后自动使用;更新已托管的当前项目与账号技能目录,失败恢复旧目录,禁止覆盖 Git 跟踪源码或未托管内容。升级返回 `upgrade.reloadRequired` 和说明路径时,IDE 应读取更新后的 SKILL.md、核对本任务合同再继续。install/check同样自动更新,不需要每次人工发升级指令。查询不确定调用的原回执不升级、不重发操作。
|
|
117
|
+
|
|
118
|
+
升级不会清除账号凭据;各调用重新读取共享文件,因此重新同步一次密钥后所有任务使用新值。已撤销或失效的密钥不能为自己取得新权限,必须从已认证网页重新同步一次。两个不同操作系统账号不共享私密文件。
|
|
119
|
+
|
|
120
|
+
English: configure once using JSON stdin; all tasks under the same OS account reuse the credential. Each new invocation checks and updates the official package and managed documentation. Reload updated instructions when indicated. Revoked keys require a fresh authenticated copy.
|
|
121
|
+
|
|
122
|
+
Русский: настройте ключ один раз через JSON stdin для всех задач пользователя ОС. Перед новым вызовом пакет и управляемые инструкции обновляются автоматически. Отозванный ключ требует повторной синхронизации с авторизованной страницы.
|
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
## 公开 `reclaim` 操作
|
|
9
9
|
|
|
10
|
-
输入为 `tasks`、`taskId` 和可选 `reason`。任务不存在时返回 `RECLAIM_FAILED`;已是 `backlog` 时返回 `RECLAIM_NOOP
|
|
10
|
+
输入为 `tasks`、`taskId` 和可选 `reason`。任务不存在时返回 `RECLAIM_FAILED`;已是 `backlog` 时返回 `RECLAIM_NOOP`。仅assigned可恢复backlog;claimed/running被标记blocked并返回完整状态与RECLAIM_REQUIRES_RECONCILIATION,终态保持不变。
|
|
11
11
|
|
|
12
|
-
公开 `reclaim`
|
|
12
|
+
公开 `reclaim` 不校验调用角色,但已限制为尚未开工的assigned。调用方必须在权威层控制调用者和可回收状态,不得将当前路由写成已完成权限强制。
|
|
13
13
|
|
|
14
14
|
## 内部运维函数
|
|
15
15
|
|
|
16
16
|
- `buildAgents`:从 org JSON 生成带状态、最后心跳、miss 次数和进度字段的 agent 数组。
|
|
17
17
|
- `recordHeartbeat`:更新已存在 agent 的时间、清零 miss,并可把 dead/red 恢复为 green。
|
|
18
18
|
- `scanHeartbeats`:按 30 秒计一次 miss;1–2 次为 yellow,3 次及以上标记 worker dead。
|
|
19
|
-
- `reclaimTasks`:只回收指定 worker 名下 `assigned
|
|
19
|
+
- `reclaimTasks`:只回收指定 worker 名下 `assigned` 任务,设为backlog并记录inheritedFrom;已开始任务阻断等待核对。
|
|
20
20
|
- `replaceWorker`:从已有 agent 数组选择另一个 green worker,将回收任务重新设为 assigned,写入 `assignedBy: ops` 与继承信息。
|
|
21
21
|
|
|
22
22
|
## 调度边界
|