@synap-core/cli 1.13.0 → 1.13.1-next.20261102.4b45534
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 +13 -3
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.js +12 -2
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/capability.d.ts +86 -0
- package/dist/commands/capability.js +117 -30
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +23 -2
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.js +28 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys-request.d.ts +48 -0
- package/dist/commands/keys-request.js +236 -0
- package/dist/commands/keys-request.js.map +1 -0
- package/dist/commands/keys-request.test.d.ts +1 -0
- package/dist/commands/keys-request.test.js +176 -0
- package/dist/commands/keys-request.test.js.map +1 -0
- package/dist/commands/knowledge.d.ts +6 -0
- package/dist/commands/knowledge.js +9 -1
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/market.d.ts +74 -6
- package/dist/commands/market.js +162 -53
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -1
- package/dist/commands/mcp.js +14 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pods.js +6 -2
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +7 -1
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/providers.d.ts +22 -6
- package/dist/commands/providers.js +39 -32
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +36 -10
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/status.js +12 -6
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.js +13 -3
- package/dist/commands/statusline.backoff.test.js.map +1 -1
- package/dist/commands/statusline.d.ts +104 -9
- package/dist/commands/statusline.js +392 -148
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/statusline.line2.test.d.ts +15 -0
- package/dist/commands/statusline.line2.test.js +226 -0
- package/dist/commands/statusline.line2.test.js.map +1 -0
- package/dist/commands/statusline.needs-you.test.d.ts +1 -0
- package/dist/commands/statusline.needs-you.test.js +17 -0
- package/dist/commands/statusline.needs-you.test.js.map +1 -0
- package/dist/commands/statusline.now.test.d.ts +10 -0
- package/dist/commands/statusline.now.test.js +250 -0
- package/dist/commands/statusline.now.test.js.map +1 -0
- package/dist/commands/tracks.d.ts +1 -0
- package/dist/commands/tracks.js +4 -0
- package/dist/commands/tracks.js.map +1 -1
- package/dist/commands/upload.js +12 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/wait.d.ts +124 -0
- package/dist/commands/wait.js +384 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/wait.test.d.ts +8 -0
- package/dist/commands/wait.test.js +308 -0
- package/dist/commands/wait.test.js.map +1 -0
- package/dist/commands/worker-github-app.d.ts +63 -0
- package/dist/commands/worker-github-app.js +161 -0
- package/dist/commands/worker-github-app.js.map +1 -0
- package/dist/commands/worker-github-app.test.d.ts +1 -0
- package/dist/commands/worker-github-app.test.js +102 -0
- package/dist/commands/worker-github-app.test.js.map +1 -0
- package/dist/commands/worker-github-broker.d.ts +39 -0
- package/dist/commands/worker-github-broker.js +106 -0
- package/dist/commands/worker-github-broker.js.map +1 -0
- package/dist/commands/worker-github-broker.test.d.ts +1 -0
- package/dist/commands/worker-github-broker.test.js +90 -0
- package/dist/commands/worker-github-broker.test.js.map +1 -0
- package/dist/commands/worker-runs.test.d.ts +1 -0
- package/dist/commands/worker-runs.test.js +150 -0
- package/dist/commands/worker-runs.test.js.map +1 -0
- package/dist/commands/worker-worktrees.d.ts +34 -0
- package/dist/commands/worker-worktrees.js +87 -0
- package/dist/commands/worker-worktrees.js.map +1 -0
- package/dist/commands/worker.d.ts +192 -5
- package/dist/commands/worker.js +467 -45
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +269 -3
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +59 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/agents-config.d.ts +9 -0
- package/dist/lib/agents-config.js +20 -0
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +20 -1
- package/dist/lib/atomic-file.js +100 -2
- package/dist/lib/atomic-file.js.map +1 -1
- package/dist/lib/auth.d.ts +25 -0
- package/dist/lib/auth.js +49 -9
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/job-server.js +4 -0
- package/dist/lib/box/job-server.js.map +1 -1
- package/dist/lib/bridge-invocation.test.d.ts +1 -0
- package/dist/lib/bridge-invocation.test.js +75 -0
- package/dist/lib/bridge-invocation.test.js.map +1 -0
- package/dist/lib/bridge-status.d.ts +144 -0
- package/dist/lib/bridge-status.js +227 -0
- package/dist/lib/bridge-status.js.map +1 -0
- package/dist/lib/bridge-status.test.d.ts +6 -0
- package/dist/lib/bridge-status.test.js +197 -0
- package/dist/lib/bridge-status.test.js.map +1 -0
- package/dist/lib/client-header.test.js +19 -1
- package/dist/lib/client-header.test.js.map +1 -1
- package/dist/lib/conversation-headers.d.ts +67 -2
- package/dist/lib/conversation-headers.js +122 -6
- package/dist/lib/conversation-headers.js.map +1 -1
- package/dist/lib/conversation-headers.test.d.ts +1 -0
- package/dist/lib/conversation-headers.test.js +78 -0
- package/dist/lib/conversation-headers.test.js.map +1 -0
- package/dist/lib/credential-class.d.ts +12 -1
- package/dist/lib/credential-class.js +20 -0
- package/dist/lib/credential-class.js.map +1 -1
- package/dist/lib/device-connect.d.ts +45 -0
- package/dist/lib/device-connect.js +60 -0
- package/dist/lib/device-connect.js.map +1 -1
- package/dist/lib/goal-words.js +6 -3
- package/dist/lib/goal-words.js.map +1 -1
- package/dist/lib/hub-client.d.ts +59 -1
- package/dist/lib/hub-client.js +130 -36
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/key-locations.d.ts +3 -3
- package/dist/lib/key-locations.js +3 -39
- package/dist/lib/key-locations.js.map +1 -1
- package/dist/lib/machine-instance.d.ts +7 -5
- package/dist/lib/machine-instance.js +7 -5
- package/dist/lib/machine-instance.js.map +1 -1
- package/dist/lib/mcp-bridge.d.ts +43 -0
- package/dist/lib/mcp-bridge.js +199 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +194 -2
- package/dist/lib/mcp-bridge.test.js.map +1 -1
- package/dist/lib/mcp-call.d.ts +5 -1
- package/dist/lib/mcp-call.js +7 -6
- package/dist/lib/mcp-call.js.map +1 -1
- package/dist/lib/mcp-connector-url.test.d.ts +1 -0
- package/dist/lib/mcp-connector-url.test.js +13 -0
- package/dist/lib/mcp-connector-url.test.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +16 -0
- package/dist/lib/mcp-snippets.js +20 -2
- package/dist/lib/mcp-snippets.js.map +1 -1
- package/dist/lib/now-line.d.ts +66 -0
- package/dist/lib/now-line.js +193 -0
- package/dist/lib/now-line.js.map +1 -0
- package/dist/lib/now-line.test.d.ts +5 -0
- package/dist/lib/now-line.test.js +177 -0
- package/dist/lib/now-line.test.js.map +1 -0
- package/dist/lib/package-updates.d.ts +66 -0
- package/dist/lib/package-updates.js +76 -0
- package/dist/lib/package-updates.js.map +1 -0
- package/dist/lib/pod.d.ts +0 -4
- package/dist/lib/pod.js +0 -7
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.js +19 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -1
- package/dist/lib/safe-open.d.ts +5 -3
- package/dist/lib/safe-open.js +9 -8
- package/dist/lib/safe-open.js.map +1 -1
- package/dist/lib/self-host.d.ts +17 -0
- package/dist/lib/self-host.js +24 -0
- package/dist/lib/self-host.js.map +1 -0
- package/dist/lib/targets.d.ts +31 -0
- package/dist/lib/targets.js +100 -104
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/term.d.ts +24 -0
- package/dist/lib/term.js +52 -0
- package/dist/lib/term.js.map +1 -0
- package/dist/lib/term.test.d.ts +1 -0
- package/dist/lib/term.test.js +29 -0
- package/dist/lib/term.test.js.map +1 -0
- package/dist/lib/transcript-index.d.ts +94 -0
- package/dist/lib/transcript-index.js +321 -0
- package/dist/lib/transcript-index.js.map +1 -0
- package/dist/lib/transcript-index.test.d.ts +10 -0
- package/dist/lib/transcript-index.test.js +200 -0
- package/dist/lib/transcript-index.test.js.map +1 -0
- package/dist/lib/wait-help.d.ts +11 -0
- package/dist/lib/wait-help.js +25 -0
- package/dist/lib/wait-help.js.map +1 -0
- package/dist/lib/wait-state.d.ts +57 -0
- package/dist/lib/wait-state.js +114 -0
- package/dist/lib/wait-state.js.map +1 -0
- package/package.json +2 -2
- package/skills/synap/SKILL.md +71 -38
- package/skills/synap/code-skills.md +15 -1
- package/skills/synap/concepts.md +36 -26
- package/skills/synap/document-embeds.md +15 -1
- package/skills/synap/focus-sessions.md +4 -4
- package/skills/synap/from-intent.md +2 -2
- package/skills/synap/governance-rules.md +2 -0
- package/skills/synap/reflexes.md +4 -4
- package/skills/synap/work-model.md +1 -1
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +2 -2
- package/skills/synap-app/overview.md +2 -2
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +4 -1
- package/skills/synap-schema/intent-vocabulary-ssot.md +4 -1
- package/dist/commands/template.d.ts +0 -42
- package/dist/commands/template.js +0 -191
- package/dist/commands/template.js.map +0 -1
- package/dist/lib/browser-auth.d.ts +0 -35
- package/dist/lib/browser-auth.js +0 -170
- package/dist/lib/browser-auth.js.map +0 -1
- package/dist/lib/openclaw.d.ts +0 -32
- package/dist/lib/openclaw.js +0 -165
- package/dist/lib/openclaw.js.map +0 -1
- package/dist/lib/seed.d.ts +0 -13
- package/dist/lib/seed.js +0 -135
- package/dist/lib/seed.js.map +0 -1
package/dist/commands/market.js
CHANGED
|
@@ -26,18 +26,21 @@ import chalk from "chalk";
|
|
|
26
26
|
import { assembleCatalog, computeTemplateUpdates, isHashVersion, listPublicTemplates, listWorkspaceTemplates, getWorkspaceTemplate, mergeCatalog, toPackageDefinition, layerTemplateGraph, bundledEdgesOf, groupByDomain, } from "@synap-core/workspace-templates";
|
|
27
27
|
import { log } from "../utils/logger.js";
|
|
28
28
|
import { resolveHubConfig, hubPost, renderHubError, HubError } from "../lib/hub-client.js";
|
|
29
|
+
import { classifyActiveCredential } from "../lib/credential-class.js";
|
|
29
30
|
import { fetchProjects } from "../lib/project.js";
|
|
30
31
|
import { writeGovernance } from "../lib/capture-lane.js";
|
|
31
32
|
import { renderNextSteps, FLOW } from "../lib/next-steps.js";
|
|
32
33
|
import { getStoredToken, isTokenLocallyExpired } from "../lib/auth.js";
|
|
33
34
|
import prompts from "prompts";
|
|
34
35
|
import { fetchPublicBrowseRows, fetchMyRows, fetchAvailableSlugs, fetchRetiredWorkspaceSlugs, fetchPackageDefinition, fetchPackageRecord, hydrateMineCategories, } from "../lib/cp-packages.js";
|
|
35
|
-
import { fetchInstalledSlugs, fetchInstalledTemplates, fetchInstalledInventory,
|
|
36
|
+
import { fetchInstalledSlugs, fetchInstalledTemplates, fetchInstalledInventory, installedRowsToTemplates, fetchWorkspaceAttachments, fetchWorkspaceAttachmentsStrict, verifyStampLanded, INSTALLED_KINDS } from "../lib/installed.js";
|
|
36
37
|
import { isSuite } from "../lib/suite.js";
|
|
37
38
|
import { TEMPLATE_STEP_LABELS, classifyTemplateStep, proposalsDoor, stepFromApplyState, templateStepGlyph, templateStepPhrase, } from "../lib/template-steps.js";
|
|
38
39
|
import { capabilityAdd } from "./capability.js";
|
|
39
40
|
import { bundledTemplatesVersion } from "../lib/bundle-version.js";
|
|
40
41
|
import { HubNetworkError } from "../lib/hub-client.js";
|
|
42
|
+
import { checksFromStandings, fetchPackageStandings, StandingsUnavailableError, } from "../lib/package-updates.js";
|
|
43
|
+
import { podAdminOrigin } from "../lib/safe-open.js";
|
|
41
44
|
import { parseDataChanges, dataChangesFromRefusal, consentNeedsPerson, dataChangeRefusalMessage, printDataChanges, parseAcceptList, decideConsent, consentRerunCommand, consentRefusalLines, lifecycleNotice, overlayNotice, overlayNeedsAttention, } from "../lib/template-lifecycle.js";
|
|
42
45
|
// Same longer budget launch uses — an apply writes profiles/views/entities.
|
|
43
46
|
const APPLY_TIMEOUT_MS = 120_000;
|
|
@@ -167,8 +170,8 @@ const KIND_HEADINGS = {
|
|
|
167
170
|
// (`GET /installed`) calls the same thing `automation`. Both tokens reach
|
|
168
171
|
// `kindHeading()` — `market --list` speaks CP, `market installed` speaks pod —
|
|
169
172
|
// so both are mapped, to the ONE plural the registry says.
|
|
170
|
-
workflow: "
|
|
171
|
-
automation: "
|
|
173
|
+
workflow: "Automations",
|
|
174
|
+
automation: "Automations",
|
|
172
175
|
view: "Views",
|
|
173
176
|
cell: "Cards",
|
|
174
177
|
workspace: "Spaces",
|
|
@@ -1260,18 +1263,48 @@ function stampVersion(pkg, slug, version) {
|
|
|
1260
1263
|
const meta = pkg._meta ?? {};
|
|
1261
1264
|
return { ...pkg, _meta: { ...meta, slug, ...(version ? { version } : {}) } };
|
|
1262
1265
|
}
|
|
1263
|
-
|
|
1266
|
+
/**
|
|
1267
|
+
* The slugs with an update that `synap market update <slug>` can apply (a
|
|
1268
|
+
* kind with a CLI updater, for this key). Next-step suggestions use it, so
|
|
1269
|
+
* they never offer a command that would only refuse.
|
|
1270
|
+
*/
|
|
1271
|
+
export function cliUpdatableSlugs(checks, opts = {}) {
|
|
1272
|
+
return checks
|
|
1273
|
+
.filter((c) => {
|
|
1274
|
+
if (!c.updateAvailable)
|
|
1275
|
+
return false;
|
|
1276
|
+
const door = updateDoorFor(c.kind, opts.agentKey);
|
|
1277
|
+
return door !== null && door !== "pod-admin";
|
|
1278
|
+
})
|
|
1279
|
+
.map((c) => c.slug);
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* `agentKey`: the pod refuses a pod-wide config reconcile to any agent
|
|
1283
|
+
* credential ("An agent credential cannot trigger a pod-wide config
|
|
1284
|
+
* reconcile", 403), so for such a key a view / skill / automation update is
|
|
1285
|
+
* the person's act, like a capability: its door is `"pod-admin"`.
|
|
1286
|
+
*/
|
|
1287
|
+
export function updateDoorFor(kind, agentKey = false) {
|
|
1264
1288
|
switch (kind ?? "workspace") {
|
|
1265
1289
|
case "workspace":
|
|
1266
1290
|
return "packages-apply";
|
|
1267
1291
|
case "view":
|
|
1268
1292
|
case "skill":
|
|
1269
1293
|
case "automation":
|
|
1270
|
-
return "configs-reconcile";
|
|
1294
|
+
return agentKey ? "pod-admin" : "configs-reconcile";
|
|
1271
1295
|
default:
|
|
1272
1296
|
return null;
|
|
1273
1297
|
}
|
|
1274
1298
|
}
|
|
1299
|
+
/** The status of an update left for the person to apply in pod admin (not a failure). */
|
|
1300
|
+
export const APPLY_IN_POD_ADMIN = "apply-in-pod-admin";
|
|
1301
|
+
/** The one row for a package this key cannot update: the person applies it in pod admin. */
|
|
1302
|
+
function applyInPodAdminResult(c, podUrl, json) {
|
|
1303
|
+
if (!json) {
|
|
1304
|
+
log.warn(`${c.slug} is a ${c.kind ?? "workspace"} — to apply in pod admin. ${podUrl ? marketUpdatesDoor(podUrl) : "Apply it yourself from pod admin → Marketplace → Updates."} Nothing was written.`);
|
|
1305
|
+
}
|
|
1306
|
+
return { slug: c.slug, outcome: APPLY_IN_POD_ADMIN, status: APPLY_IN_POD_ADMIN, warnings: [] };
|
|
1307
|
+
}
|
|
1275
1308
|
/**
|
|
1276
1309
|
* Per-installed-slug drift check. SERVER-FIRST: when the pod runs the
|
|
1277
1310
|
* server-side TemplateHealth code (Hub `/workspaces` returns `drifted`/
|
|
@@ -1341,7 +1374,7 @@ export function dedupeBySlug(checks) {
|
|
|
1341
1374
|
* pod's idempotent-create path, which reconciles when the pod build supports
|
|
1342
1375
|
* it (see the `applyOnePackage` caveat below for what "created" means here).
|
|
1343
1376
|
*/
|
|
1344
|
-
async function applyOnePackage(entry, cat, cfg, opts, applyTimeoutMs = APPLY_TIMEOUT_MS, extra = {}) {
|
|
1377
|
+
export async function applyOnePackage(entry, cat, cfg, opts, applyTimeoutMs = APPLY_TIMEOUT_MS, extra = {}) {
|
|
1345
1378
|
if (cat.lockedSlugs.has(entry.slug)) {
|
|
1346
1379
|
const tier = cat.tierBySlug.get(entry.slug)?.requiredTier;
|
|
1347
1380
|
if (!opts.json)
|
|
@@ -1366,14 +1399,28 @@ async function applyOnePackage(entry, cat, cfg, opts, applyTimeoutMs = APPLY_TIM
|
|
|
1366
1399
|
try {
|
|
1367
1400
|
const res = (await hubPost("/packages/apply", pkg, cfg, applyTimeoutMs));
|
|
1368
1401
|
s?.stop();
|
|
1402
|
+
// A key that may not apply a workspace package on its own (a computer
|
|
1403
|
+
// key's grant leaves out `workspace.update`) files the update for the
|
|
1404
|
+
// person's approval: 202 `{status:"proposed", proposalId, reviewUrl}`.
|
|
1405
|
+
// Waiting, never failed — it applies when the person approves it.
|
|
1369
1406
|
if (writeGovernance(res) === "proposed") {
|
|
1370
1407
|
const proposalId = res.proposalId ? String(res.proposalId) : undefined;
|
|
1408
|
+
const reviewUrl = approvalLink(cfg.podUrl, proposalId, res.reviewUrl);
|
|
1371
1409
|
if (!opts.json) {
|
|
1372
|
-
log.info(`${entry.name} — update
|
|
1373
|
-
if (
|
|
1374
|
-
log.hint(`
|
|
1410
|
+
log.info(`${entry.name} — update filed for your approval (not applied yet).`);
|
|
1411
|
+
if (reviewUrl)
|
|
1412
|
+
log.hint(`Approve it: ${reviewUrl}`);
|
|
1413
|
+
else if (proposalId)
|
|
1414
|
+
log.hint(`Approve it: synap open proposal ${proposalId}`);
|
|
1375
1415
|
}
|
|
1376
|
-
return {
|
|
1416
|
+
return {
|
|
1417
|
+
slug: entry.slug,
|
|
1418
|
+
outcome: "proposed",
|
|
1419
|
+
status: "proposed",
|
|
1420
|
+
warnings: [],
|
|
1421
|
+
...(proposalId ? { proposalId } : {}),
|
|
1422
|
+
...(reviewUrl ? { reviewUrl } : {}),
|
|
1423
|
+
};
|
|
1377
1424
|
}
|
|
1378
1425
|
const ws = res.workspace;
|
|
1379
1426
|
const dependencies = res.dependencies;
|
|
@@ -1431,7 +1478,7 @@ async function applyOnePackage(entry, cat, cfg, opts, applyTimeoutMs = APPLY_TIM
|
|
|
1431
1478
|
if (refusal)
|
|
1432
1479
|
log.warn(refusal);
|
|
1433
1480
|
else
|
|
1434
|
-
renderHubError(e);
|
|
1481
|
+
renderHubError(e, { personDoor: marketUpdatesDoor(cfg.podUrl) });
|
|
1435
1482
|
}
|
|
1436
1483
|
return {
|
|
1437
1484
|
slug: entry.slug,
|
|
@@ -1442,6 +1489,29 @@ async function applyOnePackage(entry, cat, cfg, opts, applyTimeoutMs = APPLY_TIM
|
|
|
1442
1489
|
};
|
|
1443
1490
|
}
|
|
1444
1491
|
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Where the person approves a filed update: pod admin's proposal page, the
|
|
1494
|
+
* approval door (`/proposal/<id>`), when the pod-admin origin derives from the
|
|
1495
|
+
* pod's; else the link the pod sent (`reviewUrl`); else nothing.
|
|
1496
|
+
*/
|
|
1497
|
+
export function approvalLink(podUrl, proposalId, reviewUrl) {
|
|
1498
|
+
const admin = podAdminOrigin(podUrl);
|
|
1499
|
+
if (admin && proposalId)
|
|
1500
|
+
return `${admin}/proposal/${encodeURIComponent(proposalId)}`;
|
|
1501
|
+
return typeof reviewUrl === "string" && reviewUrl.length > 0 ? reviewUrl : undefined;
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Where the person applies a package update a limited key may not apply
|
|
1505
|
+
* (a computer key's grant leaves out `workspace.update`, by design: applying a
|
|
1506
|
+
* workspace package is the person's act). Pod admin's Marketplace, Updates
|
|
1507
|
+
* tab, named by URL when the pod-admin origin derives from the pod's.
|
|
1508
|
+
*/
|
|
1509
|
+
export function marketUpdatesDoor(podUrl) {
|
|
1510
|
+
const admin = podAdminOrigin(podUrl);
|
|
1511
|
+
return admin
|
|
1512
|
+
? `Apply it yourself from pod admin → Marketplace → Updates: ${admin}/marketplace/updates`
|
|
1513
|
+
: "Apply it yourself from pod admin → Marketplace → Updates.";
|
|
1514
|
+
}
|
|
1445
1515
|
/**
|
|
1446
1516
|
* Ask the pod's write-free preview door (`POST /packages/preflight`, the same
|
|
1447
1517
|
* body `/packages/apply` takes) which data changes an update would run.
|
|
@@ -1466,6 +1536,9 @@ async function reconcileOutcome(c, report, opts) {
|
|
|
1466
1536
|
r = await report;
|
|
1467
1537
|
}
|
|
1468
1538
|
catch (e) {
|
|
1539
|
+
// The pod's own refusal of an agent credential is the person's door, not a failure.
|
|
1540
|
+
if (e instanceof HubError && e.status === 403 && `${e.message} ${e.rawBody}`.toLowerCase().includes("agent credential"))
|
|
1541
|
+
return applyInPodAdminResult(c, opts.podUrl, opts.json);
|
|
1469
1542
|
if (!opts.json) {
|
|
1470
1543
|
log.error(`${c.slug} update failed`);
|
|
1471
1544
|
renderHubError(e);
|
|
@@ -1509,26 +1582,28 @@ async function reconcileOutcome(c, report, opts) {
|
|
|
1509
1582
|
}
|
|
1510
1583
|
export async function marketUpdate(slugsArg, opts) {
|
|
1511
1584
|
const applyTimeoutMs = parseApplyTimeoutMs(opts.timeout);
|
|
1512
|
-
// STRICT fetch: distinguish "pod unreachable" (transient — don't claim the
|
|
1513
|
-
// pod is empty, that reads as data loss) from "genuinely nothing installed".
|
|
1514
1585
|
const cat = await buildMarketCatalog();
|
|
1515
|
-
|
|
1516
|
-
//
|
|
1517
|
-
//
|
|
1518
|
-
//
|
|
1519
|
-
//
|
|
1520
|
-
|
|
1586
|
+
// THE POD'S ANSWER, never the CLI's: which installed package has an update
|
|
1587
|
+
// is derived once on the pod, for every kind, with one rule (Hub
|
|
1588
|
+
// `GET /package-updates` → `installed[]`), and pod admin's Updates tab reads
|
|
1589
|
+
// the same list. A pod that cannot answer is said out loud, never replaced
|
|
1590
|
+
// by a verdict computed here (that fork is how the CLI said "content
|
|
1591
|
+
// changed" while pod admin said nothing, 2 Nov 2026).
|
|
1592
|
+
let standings;
|
|
1521
1593
|
try {
|
|
1522
|
-
|
|
1523
|
-
installedTemplates = installedRowsToTemplates(inv.rows);
|
|
1524
|
-
degraded = inv.degraded;
|
|
1594
|
+
standings = await fetchPackageStandings(await resolveHubConfig(opts));
|
|
1525
1595
|
}
|
|
1526
1596
|
catch (e) {
|
|
1527
1597
|
// Unreachable ≠ success — a script checking `$?` (or `&&`-chaining) must see
|
|
1528
1598
|
// failure, not exit 0, even though the JSON payload also carries the reason.
|
|
1529
1599
|
process.exitCode = 1;
|
|
1600
|
+
const unavailable = e instanceof StandingsUnavailableError ? e.reason : null;
|
|
1530
1601
|
if (opts.json) {
|
|
1531
|
-
console.log(JSON.stringify({ checks: [], loggedIn: cat.loggedIn, error: "pod-unreachable" }, null, 2));
|
|
1602
|
+
console.log(JSON.stringify({ checks: [], loggedIn: cat.loggedIn, error: unavailable ?? "pod-unreachable" }, null, 2));
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (e instanceof StandingsUnavailableError) {
|
|
1606
|
+
log.error(e.message);
|
|
1532
1607
|
return;
|
|
1533
1608
|
}
|
|
1534
1609
|
log.error("Couldn't reach your pod to read what's installed — not saying it's empty.");
|
|
@@ -1536,26 +1611,21 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1536
1611
|
log.hint("Check the pod is up / you're on the right --pod, then retry.");
|
|
1537
1612
|
return;
|
|
1538
1613
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
if (
|
|
1614
|
+
// Kept in the JSON for readers of the older shape: the pod's list covers
|
|
1615
|
+
// every kind, so it is never the workspace-only view of an old pod.
|
|
1616
|
+
const degraded = false;
|
|
1617
|
+
if (standings.length === 0) {
|
|
1543
1618
|
if (opts.json) {
|
|
1544
|
-
console.log(JSON.stringify({ checks: [], loggedIn: cat.loggedIn, degraded
|
|
1619
|
+
console.log(JSON.stringify({ checks: [], loggedIn: cat.loggedIn, degraded }, null, 2));
|
|
1545
1620
|
return;
|
|
1546
1621
|
}
|
|
1547
1622
|
log.warn("No installed packages found on this pod.");
|
|
1548
|
-
if (degradedNote)
|
|
1549
|
-
log.dim(degradedNote);
|
|
1550
1623
|
log.hint("Install one: synap market install <slug>");
|
|
1551
1624
|
return;
|
|
1552
1625
|
}
|
|
1553
|
-
//
|
|
1554
|
-
//
|
|
1555
|
-
|
|
1556
|
-
// `task-views-pack` four times and the "N up to date" tally counted it four
|
|
1557
|
-
// times. `market installed` has always deduped the same way.
|
|
1558
|
-
let checks = dedupeBySlug(computeUpdates(installedTemplates, cat));
|
|
1626
|
+
// One check per slug: the pod answers per package (slug + kind); a slug
|
|
1627
|
+
// installed as two kinds is updated per kind by its own door, but listed once.
|
|
1628
|
+
let checks = dedupeBySlug(checksFromStandings(standings));
|
|
1559
1629
|
const entryBySlug = new Map(cat.entries.map((e) => [e.slug, e]));
|
|
1560
1630
|
const explicitSlugs = !!slugsArg && slugsArg.length > 0;
|
|
1561
1631
|
let unknownSlugs = [];
|
|
@@ -1612,6 +1682,7 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1612
1682
|
// Asked of the pod's write-free preview door BEFORE anything is shown, so the
|
|
1613
1683
|
// person reads what will happen to their data before the template diff.
|
|
1614
1684
|
let cfg;
|
|
1685
|
+
let agentKey = false;
|
|
1615
1686
|
const previews = new Map();
|
|
1616
1687
|
if (withUpdates.length > 0) {
|
|
1617
1688
|
try {
|
|
@@ -1621,6 +1692,12 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1621
1692
|
renderHubError(e);
|
|
1622
1693
|
process.exit(1);
|
|
1623
1694
|
}
|
|
1695
|
+
// Which door applies a view / skill / automation depends on the key: the
|
|
1696
|
+
// pod refuses the pod-wide reconcile to any agent credential. Asked only
|
|
1697
|
+
// when such an update exists, through the one credential-class read.
|
|
1698
|
+
if (withUpdates.some((c) => updateDoorFor(c.kind) === "configs-reconcile")) {
|
|
1699
|
+
agentKey = (await classifyActiveCredential(cfg)) === "agent";
|
|
1700
|
+
}
|
|
1624
1701
|
for (const c of withUpdates) {
|
|
1625
1702
|
// The data-change preview is the workspace door's (`/packages/preflight`).
|
|
1626
1703
|
if (updateDoorFor(c.kind) !== "packages-apply")
|
|
@@ -1663,6 +1740,8 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1663
1740
|
}
|
|
1664
1741
|
log.heading(" Installed packages");
|
|
1665
1742
|
log.blank();
|
|
1743
|
+
// The slug column fits the longest slug (capped), with at least 2 spaces after it.
|
|
1744
|
+
const slugW = Math.min(Math.max(28, ...checks.map((c) => c.slug.length + 2)), 46);
|
|
1666
1745
|
for (const c of checks) {
|
|
1667
1746
|
// ONE label door. This was a hand-rolled second copy that diverged from
|
|
1668
1747
|
// `installedStatusLabel` in two ways that both LIED: it had no cache-cold
|
|
@@ -1673,14 +1752,12 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1673
1752
|
const label = retiredSlugs.has(c.slug)
|
|
1674
1753
|
? chalk.yellow("retired") + chalk.dim(` · synap templates retire ${c.slug}`)
|
|
1675
1754
|
: installedStatusLabel(c, entryBySlug.get(c.slug)?.isPrivate ?? false, cat.loggedIn);
|
|
1676
|
-
console.log(" " + chalk.cyan(c.slug.padEnd(
|
|
1755
|
+
console.log(" " + chalk.cyan(clip(c.slug, slugW - 2).padEnd(slugW)) + label);
|
|
1677
1756
|
}
|
|
1678
1757
|
log.blank();
|
|
1679
1758
|
if (noVersionInfo.length > 0) {
|
|
1680
1759
|
log.dim(`${noVersionInfo.length} installed without a version stamp — can't check ${noVersionInfo.length === 1 ? "it" : "them"} for updates.`);
|
|
1681
1760
|
}
|
|
1682
|
-
if (degradedNote)
|
|
1683
|
-
log.dim(degradedNote);
|
|
1684
1761
|
}
|
|
1685
1762
|
// What the JSON carries for the new facts: per slug, the pod's data changes
|
|
1686
1763
|
// (`null` = this pod sent none: an older pod) and the template lifecycle.
|
|
@@ -1722,7 +1799,17 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1722
1799
|
return;
|
|
1723
1800
|
}
|
|
1724
1801
|
if (!shouldApply) {
|
|
1725
|
-
|
|
1802
|
+
// Next steps offer only what this command can run: a capability update
|
|
1803
|
+
// (no CLI updater) is the person's act in pod admin, named once below.
|
|
1804
|
+
const steps = FLOW.afterMarketUpdateCheck(cliUpdatableSlugs(withUpdates, { agentKey }));
|
|
1805
|
+
// Same predicate as the apply loop: no CLI door for this kind, or a
|
|
1806
|
+
// pod-admin door for this key (an agent key's skill/view/automation).
|
|
1807
|
+
const personApplies = withUpdates
|
|
1808
|
+
.filter((c) => {
|
|
1809
|
+
const door = updateDoorFor(c.kind, agentKey);
|
|
1810
|
+
return door === null || door === "pod-admin";
|
|
1811
|
+
})
|
|
1812
|
+
.map((c) => c.slug);
|
|
1726
1813
|
if (opts.json) {
|
|
1727
1814
|
console.log(JSON.stringify({
|
|
1728
1815
|
checks,
|
|
@@ -1730,6 +1817,9 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1730
1817
|
unknownSlugs,
|
|
1731
1818
|
applied: [],
|
|
1732
1819
|
nextSteps: steps,
|
|
1820
|
+
...(personApplies.length > 0
|
|
1821
|
+
? { appliedInPodAdmin: { slugs: personApplies, where: marketUpdatesDoor(cfg.podUrl) } }
|
|
1822
|
+
: {}),
|
|
1733
1823
|
dataChanges: dataChangesJson,
|
|
1734
1824
|
templateLifecycle: lifecycleJson,
|
|
1735
1825
|
}, null, 2));
|
|
@@ -1739,6 +1829,9 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1739
1829
|
if ([...previews.values()].some((pv) => (pv.changes?.length ?? 0) > 0)) {
|
|
1740
1830
|
log.hint("Updates with data changes ask before they run. To agree up front: --accept-data-changes <opKey,…> (or --yes for all listed)");
|
|
1741
1831
|
}
|
|
1832
|
+
if (personApplies.length > 0) {
|
|
1833
|
+
log.hint(`${personApplies.join(", ")}: not updated by this command. ${marketUpdatesDoor(cfg.podUrl)}`);
|
|
1834
|
+
}
|
|
1742
1835
|
renderNextSteps(steps);
|
|
1743
1836
|
return;
|
|
1744
1837
|
}
|
|
@@ -1748,7 +1841,13 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1748
1841
|
// at most once, lazily, and read each slug's outcome from its report.
|
|
1749
1842
|
let configsReport;
|
|
1750
1843
|
for (const c of withUpdates) {
|
|
1751
|
-
const door = updateDoorFor(c.kind);
|
|
1844
|
+
const door = updateDoorFor(c.kind, agentKey);
|
|
1845
|
+
// A capability update (and, for an agent key, a view / skill / automation)
|
|
1846
|
+
// is the person's act in pod admin: not a failure.
|
|
1847
|
+
if (door === "pod-admin" || (door === null && c.kind === "capability")) {
|
|
1848
|
+
results.push(applyInPodAdminResult(c, cfg?.podUrl, opts.json));
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1752
1851
|
if (door === null) {
|
|
1753
1852
|
if (!opts.json)
|
|
1754
1853
|
log.warn(`${c.slug} is a ${c.kind} — the pod has no per-package update for this kind, so it was not updated. Nothing was written.`);
|
|
@@ -1757,7 +1856,7 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1757
1856
|
}
|
|
1758
1857
|
if (door === "configs-reconcile") {
|
|
1759
1858
|
configsReport ??= hubPost("/configs/reconcile", {}, cfg, applyTimeoutMs);
|
|
1760
|
-
results.push(await reconcileOutcome(c, configsReport, { json: opts.json }));
|
|
1859
|
+
results.push(await reconcileOutcome(c, configsReport, { json: opts.json, podUrl: cfg?.podUrl }));
|
|
1761
1860
|
continue;
|
|
1762
1861
|
}
|
|
1763
1862
|
const entry = entryBySlug.get(c.slug);
|
|
@@ -1834,9 +1933,11 @@ export async function marketUpdate(slugsArg, opts) {
|
|
|
1834
1933
|
* already-current, the `*-with-failures` and `"stamp-contradiction"` statuses
|
|
1835
1934
|
* get their OWN bucket — a partial capability-seed failure or a stamp that
|
|
1836
1935
|
* silently didn't take is never folded into a clean "updated" count.
|
|
1837
|
-
* `"timed-out"` is broken out from the generic `"failed"` bucket;
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1936
|
+
* `"timed-out"` is broken out from the generic `"failed"` bucket;
|
|
1937
|
+
* `"proposed"`/`"awaiting-approval"` count as waiting for the person's
|
|
1938
|
+
* approval (never failed); everything else
|
|
1939
|
+
* (locked/fetch-failed/not-in-catalog/no-workspace/unknown) rolls into
|
|
1940
|
+
* "failed".
|
|
1840
1941
|
*/
|
|
1841
1942
|
export function summarizeApplyResults(results) {
|
|
1842
1943
|
let updated = 0;
|
|
@@ -1846,6 +1947,7 @@ export function summarizeApplyResults(results) {
|
|
|
1846
1947
|
let failed = 0;
|
|
1847
1948
|
let proposed = 0;
|
|
1848
1949
|
let needsConsent = 0;
|
|
1950
|
+
let toApplyInPodAdmin = 0;
|
|
1849
1951
|
for (const r of results) {
|
|
1850
1952
|
switch (r.status) {
|
|
1851
1953
|
case "installed":
|
|
@@ -1880,17 +1982,23 @@ export function summarizeApplyResults(results) {
|
|
|
1880
1982
|
case "needs-consent":
|
|
1881
1983
|
needsConsent++;
|
|
1882
1984
|
break;
|
|
1985
|
+
// The person's act (pod admin → Marketplace → Updates): nothing failed.
|
|
1986
|
+
case APPLY_IN_POD_ADMIN:
|
|
1987
|
+
toApplyInPodAdmin++;
|
|
1988
|
+
break;
|
|
1883
1989
|
default:
|
|
1884
1990
|
failed++;
|
|
1885
1991
|
}
|
|
1886
1992
|
}
|
|
1887
1993
|
const parts = [`${updated} updated`, `${unchanged} already current`];
|
|
1888
1994
|
if (proposed > 0)
|
|
1889
|
-
parts.push(`${proposed}
|
|
1995
|
+
parts.push(`${proposed} waiting for your approval`);
|
|
1890
1996
|
// The vocabulary's `unfinished` ("Not finished"): one state, one name on
|
|
1891
1997
|
// every surface — never "completed with failures" (review2 UX §1 #2).
|
|
1892
1998
|
if (withFailures > 0)
|
|
1893
1999
|
parts.push(`${withFailures} ${templateStepPhrase("unfinished")}`);
|
|
2000
|
+
if (toApplyInPodAdmin > 0)
|
|
2001
|
+
parts.push(`${toApplyInPodAdmin} to apply in pod admin`);
|
|
1894
2002
|
if (failed > 0)
|
|
1895
2003
|
parts.push(`${failed} failed`);
|
|
1896
2004
|
if (timedOut > 0)
|
|
@@ -1899,9 +2007,9 @@ export function summarizeApplyResults(results) {
|
|
|
1899
2007
|
parts.push(`${needsConsent} waiting for your agreement to data changes`);
|
|
1900
2008
|
const clean = withFailures === 0 && failed === 0 && timedOut === 0 && needsConsent === 0;
|
|
1901
2009
|
// Proposed isn't a failure (never ⚠) but isn't live yet (not a bare ✓) —
|
|
1902
|
-
// a distinct "→
|
|
1903
|
-
const marker = !clean ? "⚠" : proposed > 0 ? "→" : "✓";
|
|
1904
|
-
const color = !clean ? chalk.yellow : proposed > 0 ? chalk.cyan : chalk.green;
|
|
2010
|
+
// a distinct "→ waiting for your approval" marker keeps the summary honest.
|
|
2011
|
+
const marker = !clean ? "⚠" : proposed > 0 || toApplyInPodAdmin > 0 ? "→" : "✓";
|
|
2012
|
+
const color = !clean ? chalk.yellow : proposed > 0 || toApplyInPodAdmin > 0 ? chalk.cyan : chalk.green;
|
|
1905
2013
|
return {
|
|
1906
2014
|
updated,
|
|
1907
2015
|
unchanged,
|
|
@@ -1910,6 +2018,7 @@ export function summarizeApplyResults(results) {
|
|
|
1910
2018
|
timedOut,
|
|
1911
2019
|
proposed,
|
|
1912
2020
|
needsConsent,
|
|
2021
|
+
toApplyInPodAdmin,
|
|
1913
2022
|
line: color(`${marker} ${parts.join(" · ")}`),
|
|
1914
2023
|
};
|
|
1915
2024
|
}
|
|
@@ -2389,7 +2498,7 @@ export async function marketInstalled(opts) {
|
|
|
2389
2498
|
const entryBySlug = new Map(cat.entries.map((e) => [e.slug, e]));
|
|
2390
2499
|
if (opts.layers) {
|
|
2391
2500
|
const graph = await buildInstallationGraph(installedTemplates, cat, checks);
|
|
2392
|
-
const steps = FLOW.afterMarketInstalledCheck(rows
|
|
2501
|
+
const steps = FLOW.afterMarketInstalledCheck(cliUpdatableSlugs(rows));
|
|
2393
2502
|
if (opts.json) {
|
|
2394
2503
|
console.log(JSON.stringify({
|
|
2395
2504
|
nodes: [...graph.nodes.values()],
|
|
@@ -2416,7 +2525,7 @@ export async function marketInstalled(opts) {
|
|
|
2416
2525
|
}
|
|
2417
2526
|
if (opts.tree) {
|
|
2418
2527
|
const { packages, leafRows } = await buildCompositionTrees(rows, cat, installedTemplates, workspaceCountBySlug);
|
|
2419
|
-
const steps = FLOW.afterMarketInstalledCheck(rows
|
|
2528
|
+
const steps = FLOW.afterMarketInstalledCheck(cliUpdatableSlugs(rows));
|
|
2420
2529
|
if (opts.json) {
|
|
2421
2530
|
console.log(JSON.stringify({
|
|
2422
2531
|
packages,
|
|
@@ -2476,7 +2585,7 @@ export async function marketInstalled(opts) {
|
|
|
2476
2585
|
return;
|
|
2477
2586
|
}
|
|
2478
2587
|
if (opts.json) {
|
|
2479
|
-
const steps = FLOW.afterMarketInstalledCheck(rows
|
|
2588
|
+
const steps = FLOW.afterMarketInstalledCheck(cliUpdatableSlugs(rows));
|
|
2480
2589
|
console.log(JSON.stringify({
|
|
2481
2590
|
rows: rows.map((c) => {
|
|
2482
2591
|
const entry = entryBySlug.get(c.slug);
|
|
@@ -2594,7 +2703,7 @@ export async function marketInstalled(opts) {
|
|
|
2594
2703
|
log.dim(degradedNote);
|
|
2595
2704
|
if (!cat.loggedIn)
|
|
2596
2705
|
log.dim("Not logged in — private template updates aren't visible. Run: synap login");
|
|
2597
|
-
renderNextSteps(FLOW.afterMarketInstalledCheck(rows
|
|
2706
|
+
renderNextSteps(FLOW.afterMarketInstalledCheck(cliUpdatableSlugs(rows)));
|
|
2598
2707
|
}
|
|
2599
2708
|
/** Classify one workspace's attachment, reusing `computeUpdates`' drift verdict for the stamped case. */
|
|
2600
2709
|
function classifyAttachment(ws, check) {
|