@aopslabs/aops 0.3.33 → 0.3.34
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.
|
@@ -220,6 +220,9 @@ is canonical, so the cache is only ever pulled, never pushed back.
|
|
|
220
220
|
```bash
|
|
221
221
|
aops-cli sync status --project-slug aops --json
|
|
222
222
|
aops-cli sync pull --project-slug aops --apply --json
|
|
223
|
+
aops-cli sync pull --project-slug aops --only projectman --apply --json
|
|
224
|
+
aops-cli sync pull --project-slug aops --only agentspace --apply --json
|
|
225
|
+
aops-cli sync pull --hosted-project-slug aops --only hosted-skills --apply --json
|
|
223
226
|
aops-cli sync status --all-projects --json
|
|
224
227
|
aops-cli sync pull --all-projects --apply --json
|
|
225
228
|
```
|
|
@@ -238,6 +241,13 @@ Rules:
|
|
|
238
241
|
reported/skipped rather than treated as another project's cache.
|
|
239
242
|
4. Because the server is canonical, conflict/drift resolution is not part of the
|
|
240
243
|
pull: a refresh simply overwrites the local cache with current server state.
|
|
244
|
+
5. `sync pull` and `sync bootstrap` accept repeatable `--only` flags with
|
|
245
|
+
`projectman`, `agentspace`, `hosted-skills`, and `hosted-prompts`. Omitting
|
|
246
|
+
`--only` preserves the complete refresh. Prefer a scoped synchronous pull
|
|
247
|
+
when only one cache family changed so completion and errors remain visible.
|
|
248
|
+
6. Docman is intentionally not a sync partition. Refresh document mirrors with
|
|
249
|
+
`aops-cli doc mirror pull`; `--only docman` is rejected rather than silently
|
|
250
|
+
doing the wrong operation.
|
|
241
251
|
|
|
242
252
|
#### 13.1.3 Archive lifecycle
|
|
243
253
|
|
|
@@ -17,6 +17,26 @@ import { loadAopsRepoConfig, loadAopsRepoConfigReadOnly, readAopsRepoConfigReadO
|
|
|
17
17
|
import { hostedProjectKey, rebuildHostedWorkspace, syncHostedMirrorKind, } from '../utils/hosted-workspace.js';
|
|
18
18
|
import { GUIDE_PATHS } from '../utils/guide-paths.js';
|
|
19
19
|
import { writeFileWithRetry } from '../utils/transient-fs.js';
|
|
20
|
+
export const SYNC_PULL_PARTITIONS = [
|
|
21
|
+
'projectman',
|
|
22
|
+
'agentspace',
|
|
23
|
+
'hosted-skills',
|
|
24
|
+
'hosted-prompts',
|
|
25
|
+
];
|
|
26
|
+
export function resolveSyncPullPartitions(values = []) {
|
|
27
|
+
if (values.length === 0)
|
|
28
|
+
return [...SYNC_PULL_PARTITIONS];
|
|
29
|
+
const allowed = new Set(SYNC_PULL_PARTITIONS);
|
|
30
|
+
const selected = new Set();
|
|
31
|
+
for (const raw of values) {
|
|
32
|
+
const value = normalizeNonEmpty(raw)?.toLowerCase();
|
|
33
|
+
if (!value || !allowed.has(value)) {
|
|
34
|
+
throw new Error(`Unknown sync partition: ${raw || '(empty)'}. Expected one of: ${SYNC_PULL_PARTITIONS.join(', ')}.`);
|
|
35
|
+
}
|
|
36
|
+
selected.add(value);
|
|
37
|
+
}
|
|
38
|
+
return SYNC_PULL_PARTITIONS.filter((partition) => selected.has(partition));
|
|
39
|
+
}
|
|
20
40
|
function projectReportBase(project) {
|
|
21
41
|
return compactPayload({
|
|
22
42
|
projectSlug: normalizeNonEmpty(project.slug),
|
|
@@ -1091,18 +1111,22 @@ async function resolveHostedMirrorTargets(apiState, options, context) {
|
|
|
1091
1111
|
}
|
|
1092
1112
|
return [...deduped.values()];
|
|
1093
1113
|
}
|
|
1094
|
-
async function buildHostedMirrorItemsForProject(apiState, options, project) {
|
|
1114
|
+
async function buildHostedMirrorItemsForProject(apiState, options, project, selection = { skills: true, prompts: true }) {
|
|
1095
1115
|
const scopeId = normalizeNonEmpty(project.scopeId) ?? normalizeNonEmpty(project.projectId);
|
|
1096
1116
|
if (!scopeId)
|
|
1097
1117
|
return { skillItems: [], promptItems: [] };
|
|
1098
1118
|
const [skills, prompts] = await Promise.all([
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1119
|
+
selection.skills
|
|
1120
|
+
? toRecordArray(await invokeRead(apiState, options, 'agentspace.skill.list-skills', {
|
|
1121
|
+
filter: compactPayload({ scopeId, scopeResolution: 'cascade' }),
|
|
1122
|
+
options: { limit: 500 },
|
|
1123
|
+
}))
|
|
1124
|
+
: [],
|
|
1125
|
+
selection.prompts
|
|
1126
|
+
? toRecordArray(await invokeRead(apiState, options, 'agentspace.prompt.list-prompts', {
|
|
1127
|
+
filter: compactPayload({ scopeId, scopeResolution: 'cascade', limit: 500 }),
|
|
1128
|
+
}))
|
|
1129
|
+
: [],
|
|
1106
1130
|
]);
|
|
1107
1131
|
const skillItems = await Promise.all(skills.map(async (skill) => {
|
|
1108
1132
|
const currentVersionId = normalizeNonEmpty(skill.currentVersionId);
|
|
@@ -1224,24 +1248,34 @@ async function runSyncDiff(options) {
|
|
|
1224
1248
|
async function collectSyncPullData(options, command, context, apiState) {
|
|
1225
1249
|
const projectId = normalizeNonEmpty(context.projectId);
|
|
1226
1250
|
const scopeId = normalizeNonEmpty(context.scopeId) ?? projectId;
|
|
1251
|
+
const partitions = resolveSyncPullPartitions(options.only);
|
|
1252
|
+
const selected = new Set(partitions);
|
|
1253
|
+
const pullProjectman = selected.has('projectman');
|
|
1254
|
+
const pullAgentspace = selected.has('agentspace');
|
|
1255
|
+
const pullHostedSkills = selected.has('hosted-skills');
|
|
1256
|
+
const pullHostedPrompts = selected.has('hosted-prompts');
|
|
1227
1257
|
const pmPaths = resolveRepoFirstProjectmanPaths(context);
|
|
1228
1258
|
const agPaths = resolveRepoFirstAgentspacePaths(context);
|
|
1229
1259
|
const filesWritten = [];
|
|
1230
1260
|
const [boards, tasks, sprints, issues, feedback, reviewRequests, experiences, memories] = await Promise.all([
|
|
1231
|
-
invokeRead(apiState, options, 'projectman.kanban-board.list', compactPayload({ scopeId, project: projectId })),
|
|
1232
|
-
invokeRead(apiState, options, 'projectman.kanban-task.list', compactPayload({ scopeId, project: projectId })),
|
|
1233
|
-
invokeRead(apiState, options, 'projectman.sprint.list', compactPayload({ scopeId, project: projectId })),
|
|
1234
|
-
invokeRead(apiState, options, 'projectman.issue.list', compactPayload({ scopeId })),
|
|
1235
|
-
invokeRead(apiState, options, 'projectman.feedback.list', compactPayload({ scopeId })),
|
|
1236
|
-
invokeRead(apiState, options, 'projectman.review-request.list', compactPayload({ scopeId })),
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1261
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.kanban-board.list', compactPayload({ scopeId, project: projectId })) : [],
|
|
1262
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.kanban-task.list', compactPayload({ scopeId, project: projectId })) : [],
|
|
1263
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.sprint.list', compactPayload({ scopeId, project: projectId })) : [],
|
|
1264
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.issue.list', compactPayload({ scopeId })) : [],
|
|
1265
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.feedback.list', compactPayload({ scopeId })) : [],
|
|
1266
|
+
pullProjectman ? invokeRead(apiState, options, 'projectman.review-request.list', compactPayload({ scopeId })) : [],
|
|
1267
|
+
pullAgentspace
|
|
1268
|
+
? invokeRead(apiState, options, 'agentspace.experience-item.list-experience-items', {
|
|
1269
|
+
filter: compactPayload({ scopeId, scopeResolution: 'cascade' }),
|
|
1270
|
+
options: { limit: 500 },
|
|
1271
|
+
})
|
|
1272
|
+
: [],
|
|
1273
|
+
pullAgentspace
|
|
1274
|
+
? invokeRead(apiState, options, 'agentspace.memory-item.list-memory-items', {
|
|
1275
|
+
filter: compactPayload({ scopeId, scopeResolution: 'cascade', projectId }),
|
|
1276
|
+
options: { limit: 500 },
|
|
1277
|
+
})
|
|
1278
|
+
: [],
|
|
1245
1279
|
]);
|
|
1246
1280
|
for (const row of toRecordArray(boards)) {
|
|
1247
1281
|
const remoteId = normalizeNonEmpty(row.id);
|
|
@@ -1295,29 +1329,42 @@ async function collectSyncPullData(options, command, context, apiState) {
|
|
|
1295
1329
|
for (const row of toRecordArray(memories)) {
|
|
1296
1330
|
filesWritten.push(await writeMemorySeedFile({ repoRoot: context.repoRoot, dir: agPaths.memoryItems, remote: row, projectId, scopeId }));
|
|
1297
1331
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1332
|
+
if (pullHostedSkills || pullHostedPrompts) {
|
|
1333
|
+
const hostedMirrorTargets = await resolveHostedMirrorTargets(apiState, options, context);
|
|
1334
|
+
const touchedHostedProjectKeys = hostedMirrorTargets.map((target) => hostedProjectKey(target));
|
|
1335
|
+
const hostedSkillItems = [];
|
|
1336
|
+
const hostedPromptItems = [];
|
|
1337
|
+
for (const target of hostedMirrorTargets) {
|
|
1338
|
+
const mirrorItems = await buildHostedMirrorItemsForProject(apiState, options, target, {
|
|
1339
|
+
skills: pullHostedSkills,
|
|
1340
|
+
prompts: pullHostedPrompts,
|
|
1341
|
+
});
|
|
1342
|
+
hostedSkillItems.push(...mirrorItems.skillItems);
|
|
1343
|
+
hostedPromptItems.push(...mirrorItems.promptItems);
|
|
1344
|
+
}
|
|
1345
|
+
if (pullHostedSkills) {
|
|
1346
|
+
filesWritten.push(...await syncHostedMirrorKind(context.repoRoot, 'skill', hostedSkillItems, { touchedProjectKeys: touchedHostedProjectKeys }));
|
|
1347
|
+
}
|
|
1348
|
+
if (pullHostedPrompts) {
|
|
1349
|
+
filesWritten.push(...await syncHostedMirrorKind(context.repoRoot, 'prompt', hostedPromptItems, { touchedProjectKeys: touchedHostedProjectKeys }));
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (pullProjectman)
|
|
1353
|
+
await rebuildProjectmanViews(context);
|
|
1354
|
+
if (pullAgentspace) {
|
|
1355
|
+
const experienceItems = await readExperienceItems(agPaths.experienceItems);
|
|
1356
|
+
await rebuildExperienceWorkspace(context, experienceItems);
|
|
1357
|
+
const memoryItems = await readLocalMemoryEntries(agPaths.memoryItems);
|
|
1358
|
+
await rebuildLocalMemoryWorkspace({
|
|
1359
|
+
...context,
|
|
1360
|
+
items: memoryItems,
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
if (pullHostedSkills || pullHostedPrompts)
|
|
1364
|
+
filesWritten.push(...await rebuildHostedWorkspace(context.repoRoot));
|
|
1318
1365
|
await ensureGitignore(context.repoRoot);
|
|
1319
|
-
const statePath = await writeState(context.repoRoot, { lastPull: { command, filesWritten } });
|
|
1320
|
-
return { filesWritten, statePath };
|
|
1366
|
+
const statePath = await writeState(context.repoRoot, { lastPull: { command, partitions, filesWritten } });
|
|
1367
|
+
return { partitions, filesWritten, statePath };
|
|
1321
1368
|
}
|
|
1322
1369
|
async function runSyncPullOrBootstrap(options, command) {
|
|
1323
1370
|
try {
|
|
@@ -1901,6 +1948,10 @@ function applyAllProjectsOption(cmd) {
|
|
|
1901
1948
|
cmd.option('--all-projects', 'Run the sync command once per repo-config project and report project-level results without fail-fast');
|
|
1902
1949
|
return cmd;
|
|
1903
1950
|
}
|
|
1951
|
+
function applySyncPullPartitionOptions(cmd) {
|
|
1952
|
+
cmd.option('--only <partition>', `Limit pull/bootstrap to one partition; repeat as needed (${SYNC_PULL_PARTITIONS.join(', ')})`, collectRepeatedOption, []);
|
|
1953
|
+
return cmd;
|
|
1954
|
+
}
|
|
1904
1955
|
export function makeSyncCommand() {
|
|
1905
1956
|
const cmd = new Command('sync').description('Server-first sync commands: refresh the read-only local cache of Projectman/Agentspace state plus hosted prompt/skill mirrors');
|
|
1906
1957
|
applySyncSelectionOptions(applyAllProjectsOption(applySyncOptions(cmd.command('status')
|
|
@@ -1909,21 +1960,21 @@ export function makeSyncCommand() {
|
|
|
1909
1960
|
applySyncSelectionOptions(applyAllProjectsOption(applySyncOptions(cmd.command('diff')
|
|
1910
1961
|
.description('Show local records that are not synced')
|
|
1911
1962
|
.action(async (options) => runSyncDiff(options)))));
|
|
1912
|
-
applyAllProjectsOption(applySyncOptions(cmd.command('pull')
|
|
1963
|
+
applySyncPullPartitionOptions(applyAllProjectsOption(applySyncOptions(cmd.command('pull')
|
|
1913
1964
|
.description('Pull hosted Projectman/Agentspace state into the read-only local cache and refresh read-only prompt/skill mirrors')
|
|
1914
1965
|
.option('--apply', 'Write pulled records into the local cache workspace')
|
|
1915
1966
|
.option('--hosted-project-id <id>', 'Also mirror hosted read-only prompts/skills for another project id', collectRepeatedOption, [])
|
|
1916
1967
|
.option('--hosted-project-name <name>', 'Also mirror hosted read-only prompts/skills for another project name', collectRepeatedOption, [])
|
|
1917
1968
|
.option('--hosted-project-slug <slug>', 'Also mirror hosted read-only prompts/skills for another project slug', collectRepeatedOption, [])
|
|
1918
|
-
.action(async (options) => runSyncPullOrBootstrap(options, 'sync.pull'))));
|
|
1919
|
-
applyAllProjectsOption(applySyncOptions(cmd.command('bootstrap')
|
|
1969
|
+
.action(async (options) => runSyncPullOrBootstrap(options, 'sync.pull')))));
|
|
1970
|
+
applySyncPullPartitionOptions(applyAllProjectsOption(applySyncOptions(cmd.command('bootstrap')
|
|
1920
1971
|
.description('Seed the read-only local cache from hosted server state and refresh read-only prompt/skill mirrors')
|
|
1921
1972
|
.option('--from-server', 'Bootstrap from hosted AOPS server state')
|
|
1922
1973
|
.option('--apply', 'Write bootstrapped records into the local cache workspace')
|
|
1923
1974
|
.option('--hosted-project-id <id>', 'Also mirror hosted read-only prompts/skills for another project id', collectRepeatedOption, [])
|
|
1924
1975
|
.option('--hosted-project-name <name>', 'Also mirror hosted read-only prompts/skills for another project name', collectRepeatedOption, [])
|
|
1925
1976
|
.option('--hosted-project-slug <slug>', 'Also mirror hosted read-only prompts/skills for another project slug', collectRepeatedOption, [])
|
|
1926
|
-
.action(async (options) => runSyncPullOrBootstrap(options, 'sync.bootstrap'))));
|
|
1977
|
+
.action(async (options) => runSyncPullOrBootstrap(options, 'sync.bootstrap')))));
|
|
1927
1978
|
applySyncSelectionOptions(applySyncOptions(cmd.command('sidecar')
|
|
1928
1979
|
.description('Run a localhost-bound cockpit sidecar that exposes read-only local cache status/diff on the client machine')
|
|
1929
1980
|
.option('--host <host>', 'Bind host for the sidecar HTTP server', DEFAULT_SIDECAR_HOST)
|
|
@@ -1950,6 +2001,7 @@ export function makeSyncCommand() {
|
|
|
1950
2001
|
'aops-cli sync pull --apply --json',
|
|
1951
2002
|
'aops-cli sync pull --all-projects --apply --json',
|
|
1952
2003
|
'aops-cli sync pull --apply --hosted-project-slug aops --json',
|
|
2004
|
+
'aops-cli sync pull --only hosted-skills --apply --hosted-project-slug aops --json',
|
|
1953
2005
|
'aops-cli sync resolve --path .aops-cache/projectman/sprints/demo.md --prefer remote --json',
|
|
1954
2006
|
],
|
|
1955
2007
|
guide: GUIDE_PATHS.operator,
|
|
@@ -1962,6 +2014,8 @@ export function makeSyncCommand() {
|
|
|
1962
2014
|
'Board/task/sprint selection expands to related Projectman records and linked Agentspace memory/experience context. `--record` matches local path, localId, remoteId, slug, name, or title.',
|
|
1963
2015
|
'`--all-projects` runs status/diff/pull/bootstrap once per repo-config project (server-first; no authoring-mode split), skips only projects without a localRoot in a multi-project repo, and reports project-level errors without fail-fast.',
|
|
1964
2016
|
'`sync pull` and `sync bootstrap` are project-level merge/mirror commands; use --hosted-project-* there only to refresh hosted prompt/skill mirrors.',
|
|
2017
|
+
'`sync pull|bootstrap --only <partition>` limits work to projectman, agentspace, hosted-skills, or hosted-prompts; repeat --only to combine partitions. Omitting it preserves the full refresh.',
|
|
2018
|
+
'Docman is intentionally separate: use `aops-cli doc mirror pull`, not a sync --only partition.',
|
|
1965
2019
|
'Hosted writes are the source of truth, so the local cache never replays back. If a cache file drifts, re-run `sync pull` to refresh it from the server; `sync resolve --prefer remote` re-adopts a single record from hosted state.',
|
|
1966
2020
|
'Browser/cockpit surfaces must never assume the remote aops-server machine has the repo. Use `aops-cli sync sidecar --allow-origin <cockpit-origin>` on the client machine to expose a localhost-only read-only status/diff bridge.',
|
|
1967
2021
|
'`localId` is UUIDv4. `remoteId` is filled after hosted sync/bootstrap.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aopslabs/aops",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AOPS CLI and terminal setup application.",
|
|
6
6
|
"aopsDockerServerVersion": "0.2.17",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"@openai/codex-sdk": "0.149.0",
|
|
41
41
|
"@anthropic-ai/claude-agent-sdk": "0.3.241",
|
|
42
42
|
"@opencode-ai/sdk": "1.18.21",
|
|
43
|
-
"@aopslabs/aops-
|
|
43
|
+
"@aopslabs/aops-host-registration": "0.2.3",
|
|
44
44
|
"@aopslabs/aops-pg-bootstrap": "0.2.9",
|
|
45
|
-
"@aopslabs/aops-
|
|
46
|
-
"@aopslabs/api-client": "0.2.3",
|
|
45
|
+
"@aopslabs/aops-server": "0.2.28",
|
|
47
46
|
"@aopslabs/aops-agent-runtime": "0.1.1",
|
|
48
|
-
"@aopslabs/aops-
|
|
47
|
+
"@aopslabs/aops-runtime-config": "0.2.3",
|
|
48
|
+
"@aopslabs/api-client": "0.2.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "25.3.1",
|