@open-agent-toolkit/cli 0.2.63 → 0.2.64
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/tool-packs.md +83 -0
- package/assets/docs/contributing/skills.md +42 -7
- package/assets/docs/provider-sync/manifest-and-drift.md +105 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/create-agnostic-skill/SKILL.md +9 -7
- package/assets/skills/create-agnostic-skill/references/skill-template.md +9 -7
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -0
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -0
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -0
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +1 -0
- package/assets/skills/oat-project-pr-final/SKILL.md +281 -6
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -0
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -0
- package/dist/agents/canonical/resolve.d.ts.map +1 -1
- package/dist/agents/canonical/resolve.js +25 -4
- package/dist/commands/doctor/index.d.ts +7 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +10 -2
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +68 -36
- package/dist/commands/shared/frontmatter.d.ts +51 -0
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +118 -6
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +5 -3
- package/dist/commands/sync/index.d.ts +14 -0
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +21 -0
- package/dist/commands/tools/info/index.d.ts.map +1 -1
- package/dist/commands/tools/info/index.js +14 -1
- package/dist/commands/tools/info/info-tool.d.ts +12 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +54 -6
- package/dist/commands/tools/info/skill-views.d.ts +86 -0
- package/dist/commands/tools/info/skill-views.d.ts.map +1 -0
- package/dist/commands/tools/info/skill-views.js +383 -0
- package/dist/commands/tools/install/index.d.ts.map +1 -1
- package/dist/commands/tools/install/index.js +3 -25
- package/dist/commands/tools/list/list-tools.d.ts +2 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +23 -4
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +35 -42
- package/dist/commands/tools/remove/remove-tools.d.ts +3 -2
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +14 -4
- package/dist/commands/tools/shared/auto-sync.d.ts +12 -1
- package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
- package/dist/commands/tools/shared/auto-sync.js +20 -4
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +12 -1
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/format-pack-inventory.js +48 -6
- package/dist/commands/tools/shared/in-process-sync.d.ts +11 -0
- package/dist/commands/tools/shared/in-process-sync.d.ts.map +1 -0
- package/dist/commands/tools/shared/in-process-sync.js +79 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts +78 -2
- package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-evidence.js +96 -2
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +11 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-lifecycle-outcome.js +24 -3
- package/dist/commands/tools/shared/pack-provider-evidence.d.ts +73 -0
- package/dist/commands/tools/shared/pack-provider-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-provider-evidence.js +161 -0
- package/dist/commands/tools/shared/provider-context.d.ts +88 -0
- package/dist/commands/tools/shared/provider-context.d.ts.map +1 -0
- package/dist/commands/tools/shared/provider-context.js +77 -0
- package/dist/commands/tools/shared/provider-reachability.d.ts +93 -0
- package/dist/commands/tools/shared/provider-reachability.d.ts.map +1 -0
- package/dist/commands/tools/shared/provider-reachability.js +358 -0
- package/dist/commands/tools/shared/sync-evidence.d.ts +57 -0
- package/dist/commands/tools/shared/sync-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/sync-evidence.js +147 -0
- package/dist/commands/tools/update/index.d.ts +0 -4
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +22 -40
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +23 -8
- package/dist/config/json.d.ts +36 -0
- package/dist/config/json.d.ts.map +1 -1
- package/dist/config/json.js +135 -3
- package/dist/drift/index.d.ts +2 -0
- package/dist/drift/index.d.ts.map +1 -1
- package/dist/drift/index.js +1 -0
- package/dist/drift/skill-view-diagnostic.d.ts +197 -0
- package/dist/drift/skill-view-diagnostic.d.ts.map +1 -0
- package/dist/drift/skill-view-diagnostic.js +343 -0
- package/dist/validation/skills.d.ts +1 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +181 -19
- package/package.json +2 -2
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { DEFAULT_SYNC_CONFIG, loadSyncConfig } from '../../../config/index.js';
|
|
3
|
+
import { resolveProviderScopeContext, userAgentMaterializationCoverage, } from '../../../providers/shared/registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the config-aware provider context for one scope, keeping the
|
|
6
|
+
* failure.
|
|
7
|
+
*
|
|
8
|
+
* Never throws: provider reachability is additive evidence on a lifecycle or
|
|
9
|
+
* inventory result, so a sync config that cannot be read must not fail an
|
|
10
|
+
* install that already succeeded. The error is returned instead of swallowed so
|
|
11
|
+
* a caller that can report it honestly is able to.
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveScopeProviderContextOutcome(input) {
|
|
14
|
+
const load = input.dependencies?.loadSyncConfig ??
|
|
15
|
+
((configPath) => loadSyncConfig(configPath, DEFAULT_SYNC_CONFIG));
|
|
16
|
+
const resolve = input.dependencies?.resolveProviderScopeContext ??
|
|
17
|
+
resolveProviderScopeContext;
|
|
18
|
+
try {
|
|
19
|
+
const config = await load(join(input.scopeRoot, '.oat', 'sync', 'config.json'));
|
|
20
|
+
return {
|
|
21
|
+
status: 'resolved',
|
|
22
|
+
context: await resolve({
|
|
23
|
+
scope: input.scope,
|
|
24
|
+
scopeRoot: input.scopeRoot,
|
|
25
|
+
config,
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return { status: 'failed', error };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the config-aware provider context for one scope.
|
|
35
|
+
*
|
|
36
|
+
* Returns `null` rather than throwing: provider reachability is additive
|
|
37
|
+
* evidence on a lifecycle or inventory result, so a missing or unreadable
|
|
38
|
+
* sync config must degrade to "no provider evidence" instead of failing an
|
|
39
|
+
* install that already succeeded. Callers that need to tell a failure apart
|
|
40
|
+
* from a legitimately empty result use
|
|
41
|
+
* {@link resolveScopeProviderContextOutcome} instead.
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveScopeProviderContext(input) {
|
|
44
|
+
const outcome = await resolveScopeProviderContextOutcome(input);
|
|
45
|
+
return outcome.status === 'resolved' ? outcome.context : null;
|
|
46
|
+
}
|
|
47
|
+
/** Resolves provider contexts for every scope with a known root. */
|
|
48
|
+
export async function resolveProviderScopeContexts(input) {
|
|
49
|
+
const entries = Object.entries(input.scopeRoots).filter((entry) => Boolean(entry[1]));
|
|
50
|
+
const contexts = await Promise.all(entries.map(([scope, scopeRoot]) => resolveScopeProviderContext({
|
|
51
|
+
scope,
|
|
52
|
+
scopeRoot,
|
|
53
|
+
...(input.dependencies ? { dependencies: input.dependencies } : {}),
|
|
54
|
+
})));
|
|
55
|
+
return contexts.filter((context) => context !== null);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolves the provider surface every pack inventory renderer needs.
|
|
59
|
+
*
|
|
60
|
+
* `list`, `info`, `status`, and `doctor` all report the same packs, so they
|
|
61
|
+
* must agree on whether user-scope managed roles are materialized. Sharing
|
|
62
|
+
* this resolution is what keeps `list`/`info` from reporting unmaterialized
|
|
63
|
+
* user agents that `status`/`doctor` correctly suppress.
|
|
64
|
+
*/
|
|
65
|
+
export async function resolvePackProviderSurface(input) {
|
|
66
|
+
const contexts = await resolveProviderScopeContexts(input);
|
|
67
|
+
const userContext = contexts.find(({ scope }) => scope === 'user');
|
|
68
|
+
return {
|
|
69
|
+
contexts,
|
|
70
|
+
userAgentCoverage: userContext
|
|
71
|
+
? userAgentMaterializationCoverage({
|
|
72
|
+
registrations: userContext.registrations,
|
|
73
|
+
activeProviders: userContext.activeProviders,
|
|
74
|
+
})
|
|
75
|
+
: 'none',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { SyncOperationResult } from '../../../engine/engine.types.js';
|
|
2
|
+
import type { MaterializationOperationResult } from '../../../providers/shared/materialization-extension.js';
|
|
3
|
+
import type { ManagedContentKind, ProviderCatalogRefreshPolicy, ProviderContentCapability, ProviderScopeContext } from '../../../providers/shared/registry.js';
|
|
4
|
+
import type { ConcreteScope } from '../../../shared/types.js';
|
|
5
|
+
import type { ProviderReachabilityEvidence } from './pack-evidence.js';
|
|
6
|
+
/**
|
|
7
|
+
* Maps config-aware provider registry state onto per-pack reachability
|
|
8
|
+
* evidence.
|
|
9
|
+
*
|
|
10
|
+
* Per DR-260831 the reachability decision is registry- and config-derived:
|
|
11
|
+
* activation comes from `ProviderScopeContext.activation` (sync config plus
|
|
12
|
+
* adapter detection), capability and refresh policy come from the registered
|
|
13
|
+
* `ProviderContentCapability` rows, and projection/materialization come from
|
|
14
|
+
* the sync run's own operation results. The mapper never probes the
|
|
15
|
+
* filesystem for a provider directory to decide that a provider is reachable.
|
|
16
|
+
*
|
|
17
|
+
* The one absence signal that originates outside the registry is
|
|
18
|
+
* `unmaterializedAssets`: the pack inventory's list of managed assets that
|
|
19
|
+
* have no provider materialization. That list is an inventory observation
|
|
20
|
+
* about assets, not a reachability claim about a provider — the mapper still
|
|
21
|
+
* decides *which* providers are responsible for it from registry capability
|
|
22
|
+
* alone, and attributes it to none when no active provider supports the
|
|
23
|
+
* scope and content kind.
|
|
24
|
+
*/
|
|
25
|
+
/** A sync operation result narrowed to the fields reachability needs. */
|
|
26
|
+
export type ProviderSyncOperationEvidence = Pick<SyncOperationResult, 'provider' | 'contentKind' | 'asset' | 'status'> & {
|
|
27
|
+
scope: ConcreteScope;
|
|
28
|
+
failure?: string;
|
|
29
|
+
};
|
|
30
|
+
/** A materialization-extension result narrowed the same way. */
|
|
31
|
+
export type ProviderExtensionOperationEvidence = Pick<MaterializationOperationResult, 'provider' | 'status'> & {
|
|
32
|
+
scope: ConcreteScope;
|
|
33
|
+
contentKind?: ManagedContentKind;
|
|
34
|
+
path?: string;
|
|
35
|
+
/**
|
|
36
|
+
* `role` targets one managed entry; `config` targets the provider's shared
|
|
37
|
+
* aggregate configuration, which every managed role for that provider
|
|
38
|
+
* depends on.
|
|
39
|
+
*/
|
|
40
|
+
target?: string;
|
|
41
|
+
failure?: string;
|
|
42
|
+
};
|
|
43
|
+
/** Optional per-provider refresh-policy override observed by a sync run. */
|
|
44
|
+
export type ProviderRefreshPolicyLookup = (input: {
|
|
45
|
+
provider: string;
|
|
46
|
+
scope: ConcreteScope;
|
|
47
|
+
contentKind: ManagedContentKind;
|
|
48
|
+
}) => ProviderCatalogRefreshPolicy | undefined;
|
|
49
|
+
export interface ProviderReachabilityInput {
|
|
50
|
+
/** Config-aware activation, detection, and registrations for one scope. */
|
|
51
|
+
providerScopeContext: ProviderScopeContext;
|
|
52
|
+
/** Content kinds the pack actually places at this scope, with their assets. */
|
|
53
|
+
assets: Readonly<Partial<Record<ManagedContentKind, readonly string[]>>>;
|
|
54
|
+
/** Registry capability rows; defaults to each registration's own rows. */
|
|
55
|
+
capabilities?: readonly ProviderContentCapability[];
|
|
56
|
+
/** Core sync operation results for this scope, when a sync ran. */
|
|
57
|
+
syncOperationResults?: readonly ProviderSyncOperationEvidence[];
|
|
58
|
+
/** Materialization-extension results for this scope, when a sync ran. */
|
|
59
|
+
extensionResults?: readonly ProviderExtensionOperationEvidence[];
|
|
60
|
+
/** Overrides the registry catalog-refresh policy with observed advice. */
|
|
61
|
+
refreshPolicy?: ProviderRefreshPolicyLookup;
|
|
62
|
+
/**
|
|
63
|
+
* `lifecycle` means a sync actually ran and its results are authoritative.
|
|
64
|
+
* `inventory` is a read-only surface that observed no sync.
|
|
65
|
+
*/
|
|
66
|
+
mode: 'lifecycle' | 'inventory';
|
|
67
|
+
/**
|
|
68
|
+
* `false` records that auto-sync was disabled or skipped: no projection or
|
|
69
|
+
* materialization claim can be made, so no diagnostic is produced.
|
|
70
|
+
*/
|
|
71
|
+
syncRan?: boolean;
|
|
72
|
+
/** Managed assets the pack inventory reports as unmaterialized. */
|
|
73
|
+
unmaterializedAssets?: Readonly<Partial<Record<ManagedContentKind, readonly string[]>>>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Projects one reachability row per active-or-registered provider and per
|
|
77
|
+
* content kind the pack places at this scope.
|
|
78
|
+
*/
|
|
79
|
+
export declare function projectProviderReachability(input: ProviderReachabilityInput): ProviderReachabilityEvidence[];
|
|
80
|
+
/**
|
|
81
|
+
* Re-projects existing reachability rows with a sync run's observed results.
|
|
82
|
+
*
|
|
83
|
+
* Activation and capability were already derived from the registry before the
|
|
84
|
+
* sync ran and do not change; only projection, materialization, visibility,
|
|
85
|
+
* and recovery are refined. Inactive or unsupported rows are returned
|
|
86
|
+
* untouched.
|
|
87
|
+
*/
|
|
88
|
+
export declare function applySyncEvidence(providers: readonly ProviderReachabilityEvidence[], input: {
|
|
89
|
+
syncRan: boolean;
|
|
90
|
+
syncOperationResults?: readonly ProviderSyncOperationEvidence[];
|
|
91
|
+
extensionResults?: readonly ProviderExtensionOperationEvidence[];
|
|
92
|
+
}): ProviderReachabilityEvidence[];
|
|
93
|
+
//# sourceMappingURL=provider-reachability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-reachability.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/shared/provider-reachability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,6CAA6C,CAAC;AAClG,OAAO,KAAK,EACV,kBAAkB,EAClB,4BAA4B,EAC5B,yBAAyB,EAEzB,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAEV,4BAA4B,EAG7B,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;GAkBG;AAEH,yEAAyE;AACzE,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAC9C,mBAAmB,EACnB,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,CAChD,GAAG;IACF,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,kCAAkC,GAAG,IAAI,CACnD,8BAA8B,EAC9B,UAAU,GAAG,QAAQ,CACtB,GAAG;IACF,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,2BAA2B,GAAG,CAAC,KAAK,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,kBAAkB,CAAC;CACjC,KAAK,4BAA4B,GAAG,SAAS,CAAC;AAE/C,MAAM,WAAW,yBAAyB;IACxC,2EAA2E;IAC3E,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,+EAA+E;IAC/E,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACzE,0EAA0E;IAC1E,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACpD,mEAAmE;IACnE,oBAAoB,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAChE,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,SAAS,kCAAkC,EAAE,CAAC;IACjE,0EAA0E;IAC1E,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C;;;OAGG;IACH,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,oBAAoB,CAAC,EAAE,QAAQ,CAC7B,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CACvD,CAAC;CACH;AAwHD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,yBAAyB,GAC/B,4BAA4B,EAAE,CAmJhC;AA2GD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,4BAA4B,EAAE,EAClD,KAAK,EAAE;IACL,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAChE,gBAAgB,CAAC,EAAE,SAAS,kCAAkC,EAAE,CAAC;CAClE,GACA,4BAA4B,EAAE,CAwChC"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
const CONTENT_KIND_ORDER = [
|
|
2
|
+
'skill',
|
|
3
|
+
'agent',
|
|
4
|
+
'rule',
|
|
5
|
+
'directory',
|
|
6
|
+
];
|
|
7
|
+
function projectionModeFor(capability) {
|
|
8
|
+
return (capability.projectionModes.find((mode) => mode !== 'unsupported') ?? null);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Maps a registered catalog-refresh policy onto visibility evidence.
|
|
12
|
+
*
|
|
13
|
+
* The switch is exhaustive with a `never` default rather than a fall-through:
|
|
14
|
+
* a policy state added later must fail to compile instead of silently
|
|
15
|
+
* becoming "needs a new session", which would be advice this repository never
|
|
16
|
+
* sourced.
|
|
17
|
+
*/
|
|
18
|
+
function visibilityFor(policy, provider) {
|
|
19
|
+
switch (policy.state) {
|
|
20
|
+
case 'unknown':
|
|
21
|
+
return { state: 'unknown', reason: policy.reason };
|
|
22
|
+
case 'live':
|
|
23
|
+
return {
|
|
24
|
+
state: 'live',
|
|
25
|
+
reason: `${provider} reads the projected content without a refresh`,
|
|
26
|
+
};
|
|
27
|
+
case 'manual-refresh':
|
|
28
|
+
return {
|
|
29
|
+
state: 'manual-refresh',
|
|
30
|
+
reason: `${provider} needs a catalog refresh before the change is visible`,
|
|
31
|
+
};
|
|
32
|
+
case 'restart-required':
|
|
33
|
+
return {
|
|
34
|
+
state: 'restart-required',
|
|
35
|
+
reason: `${provider} needs a new session before the change is visible`,
|
|
36
|
+
};
|
|
37
|
+
default: {
|
|
38
|
+
const unhandled = policy;
|
|
39
|
+
throw new Error(`Unhandled provider catalog refresh state: ${JSON.stringify(unhandled)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function syncRecovery(scope) {
|
|
44
|
+
return {
|
|
45
|
+
code: 'run-sync',
|
|
46
|
+
command: `oat sync --scope ${scope}`,
|
|
47
|
+
message: `Run sync for ${scope} scope to materialize provider views.`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function recoveryFor(input) {
|
|
51
|
+
if (input.activationState === 'inactive') {
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
code: 'enable-provider',
|
|
55
|
+
message: `Enable ${input.provider} in the ${input.scope} sync config to project content to it.`,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
if (input.support !== 'supported')
|
|
60
|
+
return [];
|
|
61
|
+
if (input.materialization === 'failed') {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
code: 'retry-sync',
|
|
65
|
+
command: `oat sync --scope ${input.scope}`,
|
|
66
|
+
message: `Re-run sync for ${input.provider} and inspect the reported failure.`,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
if (input.materialization === 'missing')
|
|
71
|
+
return [syncRecovery(input.scope)];
|
|
72
|
+
if (!input.projected)
|
|
73
|
+
return [];
|
|
74
|
+
if (input.visibility === 'manual-refresh') {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
code: 'refresh-provider-catalog',
|
|
78
|
+
message: `Refresh the ${input.provider} catalog to see the updated content.`,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
if (input.visibility === 'restart-required') {
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
code: 'restart-provider-session',
|
|
86
|
+
message: `Start a new ${input.provider} session to see the updated content.`,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
if (input.visibility === 'unknown') {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
code: 'verify-provider-visibility',
|
|
94
|
+
message: `Verify in ${input.provider} that the content is listed; no sourced refresh contract is registered.`,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Projects one reachability row per active-or-registered provider and per
|
|
102
|
+
* content kind the pack places at this scope.
|
|
103
|
+
*/
|
|
104
|
+
export function projectProviderReachability(input) {
|
|
105
|
+
const { providerScopeContext: context } = input;
|
|
106
|
+
const scope = context.scope;
|
|
107
|
+
const active = new Set(context.activeProviders);
|
|
108
|
+
const kinds = CONTENT_KIND_ORDER.filter((kind) => (input.assets[kind]?.length ?? 0) > 0);
|
|
109
|
+
const evidence = [];
|
|
110
|
+
for (const registration of context.registrations) {
|
|
111
|
+
const provider = registration.adapter.name;
|
|
112
|
+
const activation = context.activation.find((candidate) => candidate.provider === provider);
|
|
113
|
+
const activationState = active.has(provider) ? 'active' : 'inactive';
|
|
114
|
+
const capabilities = input.capabilities ?? registration.capabilities;
|
|
115
|
+
for (const contentKind of kinds) {
|
|
116
|
+
const assets = input.assets[contentKind] ?? [];
|
|
117
|
+
const capability = capabilities.find((candidate) => candidate.scope === scope && candidate.contentKind === contentKind);
|
|
118
|
+
const support = capability?.support ?? 'unknown';
|
|
119
|
+
const projectionModes = capability?.projectionModes ?? [];
|
|
120
|
+
const capabilityReason = capability === undefined
|
|
121
|
+
? `${provider} registers no ${scope} ${contentKind} capability`
|
|
122
|
+
: (capability.unsupportedReason ??
|
|
123
|
+
`${provider} projects ${scope} ${contentKind} content via ${projectionModes.join(', ') || 'no mode'}`);
|
|
124
|
+
let materialization;
|
|
125
|
+
if (activationState === 'inactive' || support !== 'supported') {
|
|
126
|
+
materialization = {
|
|
127
|
+
state: 'not-applicable',
|
|
128
|
+
detail: activationState === 'inactive'
|
|
129
|
+
? `${provider} is inactive for ${scope} scope`
|
|
130
|
+
: capabilityReason,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
else if (input.mode === 'lifecycle' && input.syncRan === false) {
|
|
134
|
+
materialization = {
|
|
135
|
+
state: 'not-applicable',
|
|
136
|
+
detail: 'No sync ran for this operation',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
else if (input.mode === 'lifecycle') {
|
|
140
|
+
materialization = observedMaterialization({
|
|
141
|
+
provider,
|
|
142
|
+
scope,
|
|
143
|
+
contentKind,
|
|
144
|
+
assets,
|
|
145
|
+
syncOperationResults: input.syncOperationResults ?? [],
|
|
146
|
+
extensionResults: input.extensionResults ?? [],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const unmaterialized = input.unmaterializedAssets?.[contentKind] ?? [];
|
|
151
|
+
materialization =
|
|
152
|
+
unmaterialized.length > 0
|
|
153
|
+
? {
|
|
154
|
+
state: 'missing',
|
|
155
|
+
detail: `${provider} has no ${scope} ${contentKind} materialization for ${unmaterialized.length} managed asset(s)`,
|
|
156
|
+
}
|
|
157
|
+
: {
|
|
158
|
+
// A read-only surface observed no sync. The pack inventory can
|
|
159
|
+
// report a positive absence, but its silence is not evidence
|
|
160
|
+
// that the provider view exists: claiming `materialized` here
|
|
161
|
+
// would assert reachability nothing established, which is
|
|
162
|
+
// exactly what DR-260831 forbids. Silence stays a non-claim.
|
|
163
|
+
state: 'not-applicable',
|
|
164
|
+
detail: `No sync was observed for ${provider} ${scope} ${contentKind} content`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const policy = input.refreshPolicy?.({ provider, scope, contentKind }) ??
|
|
168
|
+
capability?.catalogRefresh ?? {
|
|
169
|
+
state: 'unknown',
|
|
170
|
+
reason: `No ${provider} ${scope} ${contentKind} refresh contract is registered`,
|
|
171
|
+
};
|
|
172
|
+
const reachable = activationState === 'active' && support === 'supported';
|
|
173
|
+
// Projection and materialization already collapse to `not-applicable`
|
|
174
|
+
// for a provider that is inactive or cannot carry this content kind.
|
|
175
|
+
// Visibility follows them: refresh or restart advice about a provider
|
|
176
|
+
// that is not even active reads as guidance the user should act on.
|
|
177
|
+
const visibility = reachable
|
|
178
|
+
? visibilityFor(policy, provider)
|
|
179
|
+
: {
|
|
180
|
+
state: 'not-applicable',
|
|
181
|
+
reason: activationState === 'inactive'
|
|
182
|
+
? `${provider} is inactive for ${scope} scope, so it has no catalog visibility`
|
|
183
|
+
: `${provider} does not project ${scope} ${contentKind} content, so it has no catalog visibility`,
|
|
184
|
+
};
|
|
185
|
+
const projected = materialization.state === 'materialized';
|
|
186
|
+
const projectionState = activationState === 'inactive' || support !== 'supported'
|
|
187
|
+
? 'not-applicable'
|
|
188
|
+
: projected
|
|
189
|
+
? 'projected'
|
|
190
|
+
: materialization.state === 'missing'
|
|
191
|
+
? 'absent'
|
|
192
|
+
: 'not-applicable';
|
|
193
|
+
evidence.push({
|
|
194
|
+
provider,
|
|
195
|
+
scope,
|
|
196
|
+
contentKind,
|
|
197
|
+
assets: materialization.state === 'missing'
|
|
198
|
+
? (input.unmaterializedAssets?.[contentKind] ?? assets)
|
|
199
|
+
: assets,
|
|
200
|
+
activation: {
|
|
201
|
+
state: activationState,
|
|
202
|
+
source: activation?.source ?? 'undetected-unset',
|
|
203
|
+
reason: activation?.reason ??
|
|
204
|
+
`${provider} has no recorded activation evidence`,
|
|
205
|
+
},
|
|
206
|
+
capability: {
|
|
207
|
+
support,
|
|
208
|
+
projectionModes,
|
|
209
|
+
reason: capabilityReason,
|
|
210
|
+
},
|
|
211
|
+
projection: {
|
|
212
|
+
state: projectionState,
|
|
213
|
+
mode: projectionState === 'not-applicable' || capability === undefined
|
|
214
|
+
? null
|
|
215
|
+
: projectionModeFor(capability),
|
|
216
|
+
},
|
|
217
|
+
materialization,
|
|
218
|
+
visibility,
|
|
219
|
+
recovery: recoveryFor({
|
|
220
|
+
provider,
|
|
221
|
+
scope,
|
|
222
|
+
activationState,
|
|
223
|
+
support,
|
|
224
|
+
materialization: materialization.state,
|
|
225
|
+
visibility: visibility.state,
|
|
226
|
+
projected,
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return evidence;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Materialization state observed from a sync run's own operation results.
|
|
235
|
+
*
|
|
236
|
+
* A sync that ran and planned nothing for this provider and content kind is
|
|
237
|
+
* not evidence of a missing projection, so it stays a non-claim rather than
|
|
238
|
+
* becoming a warning that would flip a healthy pack evidence block to
|
|
239
|
+
* `partial`.
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* Canonical identity of an asset path.
|
|
243
|
+
*
|
|
244
|
+
* Sync operation results identify an asset by its canonical entry name, while
|
|
245
|
+
* a pack inventory row carries the asset's filesystem path. Comparing the
|
|
246
|
+
* final path segment is what keeps one pack's operation from being attributed
|
|
247
|
+
* to a different pack that merely shares a content kind.
|
|
248
|
+
*/
|
|
249
|
+
function canonicalAssetName(path) {
|
|
250
|
+
return (path.split('/').pop() ?? path).replace(/\.[^.]+$/, '');
|
|
251
|
+
}
|
|
252
|
+
function operationBelongsToAssets(identity, assetNames) {
|
|
253
|
+
// An operation with no usable identity cannot be attributed to a specific
|
|
254
|
+
// pack, so it is deliberately not counted for any of them.
|
|
255
|
+
if (identity === undefined || identity === '')
|
|
256
|
+
return false;
|
|
257
|
+
return assetNames.has(canonicalAssetName(identity));
|
|
258
|
+
}
|
|
259
|
+
function observedMaterialization(input) {
|
|
260
|
+
const { provider, scope, contentKind } = input;
|
|
261
|
+
const assetNames = new Set(input.assets.map(canonicalAssetName));
|
|
262
|
+
const operations = [
|
|
263
|
+
...input.syncOperationResults.filter((result) => result.provider === provider &&
|
|
264
|
+
result.scope === scope &&
|
|
265
|
+
result.contentKind === contentKind &&
|
|
266
|
+
operationBelongsToAssets(result.asset, assetNames)),
|
|
267
|
+
...input.extensionResults.filter((result) => result.provider === provider &&
|
|
268
|
+
result.scope === scope &&
|
|
269
|
+
(result.contentKind === undefined ||
|
|
270
|
+
result.contentKind === contentKind) &&
|
|
271
|
+
// A `config` target is the provider's shared aggregate file, which is
|
|
272
|
+
// not any one pack's asset but which every managed role for this
|
|
273
|
+
// provider depends on. It is therefore attributed provider-wide
|
|
274
|
+
// rather than dropped; a `role` target must still match this row's
|
|
275
|
+
// own assets so one pack's failure cannot be reported against another.
|
|
276
|
+
(result.target === 'config' ||
|
|
277
|
+
operationBelongsToAssets(result.path, assetNames))),
|
|
278
|
+
];
|
|
279
|
+
const failed = operations.filter((operation) => operation.status === 'failed');
|
|
280
|
+
if (failed.length > 0) {
|
|
281
|
+
return {
|
|
282
|
+
state: 'failed',
|
|
283
|
+
detail: `${provider} ${scope} ${contentKind} materialization failed: ${failed
|
|
284
|
+
.map((operation) => operation.failure)
|
|
285
|
+
.filter((failure) => Boolean(failure))
|
|
286
|
+
.join('; ') || 'no failure detail reported'}`,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
// Status is classified explicitly rather than as "anything but failed":
|
|
290
|
+
// `missing`, `unsupported`, `planned`, and `unknown` are not evidence that
|
|
291
|
+
// the provider view now exists.
|
|
292
|
+
const succeeded = operations.filter((operation) => operation.status === 'changed' || operation.status === 'current');
|
|
293
|
+
if (succeeded.length > 0) {
|
|
294
|
+
return {
|
|
295
|
+
state: 'materialized',
|
|
296
|
+
detail: `${succeeded.length} ${provider} ${scope} ${contentKind} operation(s) succeeded`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const missing = operations.filter((operation) => operation.status === 'missing');
|
|
300
|
+
if (missing.length > 0) {
|
|
301
|
+
return {
|
|
302
|
+
state: 'missing',
|
|
303
|
+
detail: `${provider} has no ${scope} ${contentKind} projection for ${missing.length} asset(s)`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
state: 'not-applicable',
|
|
308
|
+
detail: `No ${provider} ${scope} ${contentKind} operation was required`,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Re-projects existing reachability rows with a sync run's observed results.
|
|
313
|
+
*
|
|
314
|
+
* Activation and capability were already derived from the registry before the
|
|
315
|
+
* sync ran and do not change; only projection, materialization, visibility,
|
|
316
|
+
* and recovery are refined. Inactive or unsupported rows are returned
|
|
317
|
+
* untouched.
|
|
318
|
+
*/
|
|
319
|
+
export function applySyncEvidence(providers, input) {
|
|
320
|
+
if (!input.syncRan)
|
|
321
|
+
return [...providers];
|
|
322
|
+
return providers.map((evidence) => {
|
|
323
|
+
if (evidence.activation.state === 'inactive' ||
|
|
324
|
+
evidence.capability.support !== 'supported') {
|
|
325
|
+
return evidence;
|
|
326
|
+
}
|
|
327
|
+
const materialization = observedMaterialization({
|
|
328
|
+
provider: evidence.provider,
|
|
329
|
+
scope: evidence.scope,
|
|
330
|
+
contentKind: evidence.contentKind,
|
|
331
|
+
assets: evidence.assets,
|
|
332
|
+
syncOperationResults: input.syncOperationResults ?? [],
|
|
333
|
+
extensionResults: input.extensionResults ?? [],
|
|
334
|
+
});
|
|
335
|
+
const projected = materialization.state === 'materialized';
|
|
336
|
+
return {
|
|
337
|
+
...evidence,
|
|
338
|
+
projection: {
|
|
339
|
+
state: projected
|
|
340
|
+
? 'projected'
|
|
341
|
+
: materialization.state === 'missing'
|
|
342
|
+
? 'absent'
|
|
343
|
+
: 'not-applicable',
|
|
344
|
+
mode: evidence.projection.mode,
|
|
345
|
+
},
|
|
346
|
+
materialization,
|
|
347
|
+
recovery: recoveryFor({
|
|
348
|
+
provider: evidence.provider,
|
|
349
|
+
scope: evidence.scope,
|
|
350
|
+
activationState: evidence.activation.state,
|
|
351
|
+
support: evidence.capability.support,
|
|
352
|
+
materialization: materialization.state,
|
|
353
|
+
visibility: evidence.visibility.state,
|
|
354
|
+
projected,
|
|
355
|
+
}),
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ManagedContentKind, ProviderCatalogRefreshPolicy } from '../../../providers/shared/registry.js';
|
|
2
|
+
import type { ConcreteScope } from '../../../shared/types.js';
|
|
3
|
+
import type { ProviderExtensionOperationEvidence, ProviderSyncOperationEvidence } from './provider-reachability.js';
|
|
4
|
+
/**
|
|
5
|
+
* Per-provider refresh advice as the sync run reports it.
|
|
6
|
+
*
|
|
7
|
+
* Structurally mirrors the `providerRefreshAdvice` entries in the sync JSON
|
|
8
|
+
* payload. The tools lifecycle consumes that payload; it never reshapes it.
|
|
9
|
+
*/
|
|
10
|
+
export interface SyncProviderRefreshAdvice {
|
|
11
|
+
scope: ConcreteScope;
|
|
12
|
+
provider: string;
|
|
13
|
+
contentKind: ManagedContentKind;
|
|
14
|
+
visibility?: {
|
|
15
|
+
state?: string;
|
|
16
|
+
reason?: string;
|
|
17
|
+
policy?: ProviderCatalogRefreshPolicy;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Normalized evidence from one sync run at one scope. */
|
|
21
|
+
export interface SyncRunEvidence {
|
|
22
|
+
scope: ConcreteScope;
|
|
23
|
+
ran: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The run's aggregate failure count from `summary.failed`.
|
|
26
|
+
*
|
|
27
|
+
* Carried separately because a sync can fail without throwing and without
|
|
28
|
+
* emitting a per-operation `failed` result: a rejected collection counts as
|
|
29
|
+
* a failure but is not a planned operation.
|
|
30
|
+
*/
|
|
31
|
+
failedOperations: number;
|
|
32
|
+
operationResults: readonly ProviderSyncOperationEvidence[];
|
|
33
|
+
extensionResults: readonly ProviderExtensionOperationEvidence[];
|
|
34
|
+
refreshAdvice: readonly SyncProviderRefreshAdvice[];
|
|
35
|
+
}
|
|
36
|
+
export declare function emptySyncRunEvidence(scope: ConcreteScope): SyncRunEvidence;
|
|
37
|
+
/**
|
|
38
|
+
* Normalizes a captured sync JSON payload into reachability inputs.
|
|
39
|
+
*
|
|
40
|
+
* Defensive rather than trusting: the payload shape is owned by the sync
|
|
41
|
+
* command, so an absent or renamed field degrades to "no evidence" instead of
|
|
42
|
+
* throwing inside a lifecycle operation that already succeeded.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeSyncEvidence(scope: ConcreteScope, payload: unknown): SyncRunEvidence;
|
|
45
|
+
/**
|
|
46
|
+
* Builds a refresh-policy lookup from observed sync advice.
|
|
47
|
+
*
|
|
48
|
+
* Returns `undefined` for a provider the run reported nothing about, so the
|
|
49
|
+
* mapper falls back to the registered registry policy rather than inventing
|
|
50
|
+
* one.
|
|
51
|
+
*/
|
|
52
|
+
export declare function refreshPolicyFromAdvice(advice: readonly SyncProviderRefreshAdvice[]): (input: {
|
|
53
|
+
provider: string;
|
|
54
|
+
scope: ConcreteScope;
|
|
55
|
+
contentKind: ManagedContentKind;
|
|
56
|
+
}) => ProviderCatalogRefreshPolicy | undefined;
|
|
57
|
+
//# sourceMappingURL=sync-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-evidence.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/shared/sync-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,4BAA4B,EAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EACV,kCAAkC,EAClC,6BAA6B,EAC9B,MAAM,yBAAyB,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,kBAAkB,CAAC;IAChC,UAAU,CAAC,EAAE;QACX,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,4BAA4B,CAAC;KACvC,CAAC;CACH;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;IACb;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC3D,gBAAgB,EAAE,SAAS,kCAAkC,EAAE,CAAC;IAChE,aAAa,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACrD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,CAS1E;AAgBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,OAAO,GACf,eAAe,CA0HjB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,yBAAyB,EAAE,GAC3C,CAAC,KAAK,EAAE;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,kBAAkB,CAAC;CACjC,KAAK,4BAA4B,GAAG,SAAS,CAQ7C"}
|