@kanonak-protocol/cli 1.3.0 → 1.4.0
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/dist/capabilities/CapabilityLoader.d.ts +29 -15
- package/dist/capabilities/CommandRegistrar.d.ts +6 -5
- package/dist/capabilities/DeploymentHandlerRegistry.d.ts +36 -23
- package/dist/capabilities/GenericCapabilityHandler.d.ts +17 -19
- package/dist/capabilities/SingleDocumentRepository.d.ts +28 -0
- package/dist/capabilities/UriHelpers.d.ts +66 -0
- package/dist/commands/capability.d.ts +3 -2
- package/dist/index.js +47 -49
- package/dist/skills/SkillDeploymentHandler.d.ts +9 -15
- package/dist/skills/SkillTransformer.d.ts +10 -11
- package/package.json +3 -3
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { KanonakParser } from '@kanonak-protocol/sdk';
|
|
2
|
+
import type { IKanonakDocumentRepository } from '@kanonak-protocol/sdk';
|
|
2
3
|
import { FileCache } from '../cache/FileCache.js';
|
|
4
|
+
import { EntityUri } from './UriHelpers.js';
|
|
5
|
+
/**
|
|
6
|
+
* Canonical URIs from the kanonak.org/capabilities ontology that the CLI
|
|
7
|
+
* dispatches against. Version is intentionally omitted from matching so
|
|
8
|
+
* the CLI accepts any backwards-compatible minor version.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CAPABILITIES_PUB = "kanonak.org";
|
|
11
|
+
export declare const CAPABILITIES_PKG = "capabilities";
|
|
12
|
+
export declare const CAPABILITY_CLASS_URI: EntityUri;
|
|
3
13
|
/**
|
|
4
14
|
* A loaded command argument definition.
|
|
5
15
|
*/
|
|
@@ -10,29 +20,26 @@ export interface LoadedArgument {
|
|
|
10
20
|
defaultValue?: string;
|
|
11
21
|
}
|
|
12
22
|
/**
|
|
13
|
-
* A loaded subcommand definition.
|
|
23
|
+
* A loaded subcommand definition. `actionKey` is the canonical URI of
|
|
24
|
+
* the Action this subcommand performs, formatted as
|
|
25
|
+
* `publisher/package/name` (version omitted). The command registrar
|
|
26
|
+
* dispatches by this key.
|
|
14
27
|
*/
|
|
15
28
|
export interface LoadedCommand {
|
|
16
29
|
subcommandName: string;
|
|
17
30
|
description: string;
|
|
18
31
|
arguments: LoadedArgument[];
|
|
19
|
-
|
|
20
|
-
* Name of the Action that this subcommand performs (e.g., "fetchAndDeploy",
|
|
21
|
-
* "listDeployed"). Extracted from the `performs` property on the Kanonak
|
|
22
|
-
* CapabilityCommand instance. Empty string when no action is specified — in
|
|
23
|
-
* that case the dispatcher falls back to interpreting the subcommand verb
|
|
24
|
-
* as a legacy action (add → fetchAndDeploy, list → listDeployed, etc.).
|
|
25
|
-
*/
|
|
26
|
-
actionName: string;
|
|
32
|
+
actionKey: string;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
35
|
* A fully loaded capability with its command structure.
|
|
36
|
+
* All identifier fields are canonical URI keys — not local names.
|
|
30
37
|
*/
|
|
31
38
|
export interface LoadedCapability {
|
|
32
39
|
commandName: string;
|
|
33
40
|
description: string;
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
managesTypeKey: string;
|
|
42
|
+
deploymentTargetKey: string;
|
|
36
43
|
commands: LoadedCommand[];
|
|
37
44
|
publisher: string;
|
|
38
45
|
package_: string;
|
|
@@ -41,7 +48,14 @@ export interface LoadedCapability {
|
|
|
41
48
|
/**
|
|
42
49
|
* Load all installed capabilities from the capability store.
|
|
43
50
|
*
|
|
44
|
-
* Reads capabilities.lock
|
|
45
|
-
*
|
|
51
|
+
* Reads `capabilities.lock`, looks up each capability's cached
|
|
52
|
+
* document, parses it through `KanonakObjectParser.parseKanonaks`
|
|
53
|
+
* against a `SingleDocumentRepository` that delegates import
|
|
54
|
+
* resolution to the given broader repo, then walks the resulting
|
|
55
|
+
* `SubjectKanonak.statement[]` tree to extract the Capability
|
|
56
|
+
* instance and its subcommands.
|
|
57
|
+
*
|
|
58
|
+
* Every identifier returned is a canonical URI key — alias handling
|
|
59
|
+
* happens inside the SDK, never in this file.
|
|
46
60
|
*/
|
|
47
|
-
export declare function loadInstalledCapabilities(fileCache: FileCache, parser: KanonakParser): Promise<LoadedCapability[]>;
|
|
61
|
+
export declare function loadInstalledCapabilities(fileCache: FileCache, parser: KanonakParser, repository: IKanonakDocumentRepository): Promise<LoadedCapability[]>;
|
|
@@ -2,10 +2,11 @@ import { Command } from 'commander';
|
|
|
2
2
|
import type { LoadedCapability } from './CapabilityLoader.js';
|
|
3
3
|
import type { GenericCapabilityHandler } from './GenericCapabilityHandler.js';
|
|
4
4
|
/**
|
|
5
|
-
* Register Commander.js commands dynamically from loaded capability
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Register Commander.js commands dynamically from loaded capability
|
|
6
|
+
* definitions. For each installed capability, create a command group
|
|
7
|
+
* named after its `commandName` property and attach each subcommand
|
|
8
|
+
* with its declared positional and option arguments. All routing is
|
|
9
|
+
* done through the Action URI each subcommand's `performs:` resolves
|
|
10
|
+
* to.
|
|
10
11
|
*/
|
|
11
12
|
export declare function registerCapabilityCommands(program: Command, capabilities: LoadedCapability[], handler: GenericCapabilityHandler): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IKanonakDocumentRepository, SubjectKanonak, Kanonak } from '@kanonak-protocol/sdk';
|
|
2
2
|
/**
|
|
3
3
|
* Information about a deployed instance.
|
|
4
4
|
*/
|
|
@@ -10,36 +10,49 @@ export interface DeployedInstance {
|
|
|
10
10
|
path: string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Context passed to a deployment handler for a `deploy` call.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* `instances` is the list of `SubjectKanonak`s (each one is an
|
|
16
|
+
* instance of the capability's `managesType`) to deploy. `allKanonaks`
|
|
17
|
+
* is the full parsed kanonak list from the source document — handlers
|
|
18
|
+
* use it to follow references (e.g. a skill's `license` reference to
|
|
19
|
+
* the actual `License` subject for its `spdxId`).
|
|
20
|
+
*
|
|
21
|
+
* `repository` is the broader repository (local cache + HTTP) that the
|
|
22
|
+
* handler can use to fetch entities from other packages when needed
|
|
23
|
+
* (e.g. a `Tool` instance defined in `agent-skills`).
|
|
24
|
+
*/
|
|
25
|
+
export interface DeploymentContext {
|
|
26
|
+
instances: SubjectKanonak[];
|
|
27
|
+
allKanonaks: Kanonak[];
|
|
28
|
+
repository: IKanonakDocumentRepository;
|
|
29
|
+
options: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Handler that deploys/undeploys instances of a managed type to the
|
|
33
|
+
* local system.
|
|
34
|
+
*
|
|
35
|
+
* Each `DeploymentTarget` instance in the `kanonak.org/capabilities`
|
|
36
|
+
* ontology has a matching handler in the `DeploymentHandlerRegistry`,
|
|
37
|
+
* keyed by the target's canonical URI string
|
|
38
|
+
* (`{publisher}/{package}/{name}`).
|
|
17
39
|
*/
|
|
18
40
|
export interface DeploymentHandler {
|
|
19
|
-
|
|
20
|
-
* Deploy instances from a Kanonak document to the local system.
|
|
21
|
-
*/
|
|
22
|
-
deploy(doc: KanonakDocument, instances: InstanceSearchResult[], options: Record<string, string>): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* Remove a deployed instance by name.
|
|
25
|
-
*/
|
|
41
|
+
deploy(context: DeploymentContext): Promise<void>;
|
|
26
42
|
undeploy(name: string, options: Record<string, string>): Promise<void>;
|
|
27
|
-
/**
|
|
28
|
-
* List all deployed instances.
|
|
29
|
-
*/
|
|
30
43
|
list(options: Record<string, string>): Promise<DeployedInstance[]>;
|
|
31
44
|
}
|
|
32
45
|
/**
|
|
33
|
-
* Registry that maps
|
|
46
|
+
* Registry that maps a DeploymentTarget URI key to its handler.
|
|
47
|
+
*
|
|
48
|
+
* The URI key is the canonical `{publisher}/{package}/{name}` string
|
|
49
|
+
* of a named DeploymentTarget instance (e.g.
|
|
50
|
+
* `kanonak.org/capabilities/AgentSkillDeployment`). Version is not
|
|
51
|
+
* part of the key: new compatible versions of the ontology continue
|
|
52
|
+
* to dispatch to the same handler.
|
|
34
53
|
*/
|
|
35
54
|
export declare class DeploymentHandlerRegistry {
|
|
36
55
|
private readonly handlers;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*/
|
|
40
|
-
register(targetType: string, handler: DeploymentHandler): void;
|
|
41
|
-
/**
|
|
42
|
-
* Get the handler for a deployment target type.
|
|
43
|
-
*/
|
|
44
|
-
get(targetType: string): DeploymentHandler | null;
|
|
56
|
+
register(targetUriKey: string, handler: DeploymentHandler): void;
|
|
57
|
+
get(targetUriKey: string): DeploymentHandler | null;
|
|
45
58
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { KanonakParser, PublisherIndex } from '@kanonak-protocol/sdk';
|
|
2
|
-
import type { AuthenticatedFetchFn } from '@kanonak-protocol/sdk';
|
|
2
|
+
import type { AuthenticatedFetchFn, IKanonakDocumentRepository } from '@kanonak-protocol/sdk';
|
|
3
3
|
import { FileCache } from '../cache/FileCache.js';
|
|
4
4
|
import type { LoadedCapability } from './CapabilityLoader.js';
|
|
5
5
|
import type { DeploymentHandlerRegistry } from './DeploymentHandlerRegistry.js';
|
|
6
6
|
/**
|
|
7
|
-
* Generic command handler that implements standard
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Generic command handler that implements the standard Action set
|
|
8
|
+
* (fetchAndDeploy, removeDeployed, listDeployed, updateDeployed,
|
|
9
|
+
* searchAvailable, showInfo) in a type-agnostic way.
|
|
10
|
+
*
|
|
11
|
+
* The handler is parameterized by the LoadedCapability — specifically
|
|
12
|
+
* its URI-keyed `managesTypeKey` and `deploymentTargetKey` — so the
|
|
13
|
+
* same implementation supports any capability whose ontology resolves
|
|
14
|
+
* cleanly through the SDK object model.
|
|
10
15
|
*/
|
|
11
16
|
export declare class GenericCapabilityHandler {
|
|
12
17
|
private readonly fileCache;
|
|
@@ -14,29 +19,22 @@ export declare class GenericCapabilityHandler {
|
|
|
14
19
|
private readonly publisherIndex;
|
|
15
20
|
private readonly fetchFn;
|
|
16
21
|
private readonly handlerRegistry;
|
|
17
|
-
|
|
22
|
+
private readonly repository;
|
|
23
|
+
private readonly objectParser;
|
|
24
|
+
constructor(fileCache: FileCache, parser: KanonakParser, publisherIndex: PublisherIndex, fetchFn: AuthenticatedFetchFn, handlerRegistry: DeploymentHandlerRegistry, repository: IKanonakDocumentRepository);
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
26
|
+
* Fetch a package and deploy every instance of the capability's
|
|
27
|
+
* managed type.
|
|
20
28
|
*/
|
|
21
29
|
add(capability: LoadedCapability, packageRef: string, options: Record<string, string>): Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Remove a deployed instance by name.
|
|
24
|
-
*/
|
|
25
30
|
remove(capability: LoadedCapability, name: string, options: Record<string, string>): Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
* List deployed instances.
|
|
28
|
-
*/
|
|
29
31
|
list(capability: LoadedCapability, options: Record<string, string>): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Update deployed instances to latest versions.
|
|
32
|
-
*/
|
|
33
32
|
update(capability: LoadedCapability, name: string | undefined, options: Record<string, string>): Promise<void>;
|
|
34
33
|
/**
|
|
35
|
-
* Search
|
|
34
|
+
* Search a publisher's package index for any package that contains
|
|
35
|
+
* instances of this capability's `managesType`.
|
|
36
36
|
*/
|
|
37
37
|
search(capability: LoadedCapability, options: Record<string, string>): Promise<void>;
|
|
38
|
-
/**
|
|
39
|
-
* Show details about a deployed instance.
|
|
40
|
-
*/
|
|
41
38
|
info(capability: LoadedCapability, name: string, options: Record<string, string>): Promise<void>;
|
|
39
|
+
private requireHandler;
|
|
42
40
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { KanonakDocument, Import, DocumentReference } from '@kanonak-protocol/types/document/models/types';
|
|
2
|
+
import type { IKanonakDocumentRepository } from '@kanonak-protocol/sdk';
|
|
3
|
+
/**
|
|
4
|
+
* A repository that exposes exactly one target document through
|
|
5
|
+
* `getAllDocumentsAsync` but delegates every import-resolution lookup
|
|
6
|
+
* (`getHighestCompatibleVersionAsync`, `getDocumentAsync`, etc.) to a
|
|
7
|
+
* broader backing repository.
|
|
8
|
+
*
|
|
9
|
+
* This is what lets `KanonakObjectParser.parseKanonaks` parse a single
|
|
10
|
+
* capability or skill document with full URI resolution across its
|
|
11
|
+
* transitive imports, without inheriting every other cached document's
|
|
12
|
+
* entities.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SingleDocumentRepository implements IKanonakDocumentRepository {
|
|
15
|
+
private readonly doc;
|
|
16
|
+
private readonly broader;
|
|
17
|
+
constructor(doc: KanonakDocument, broader: IKanonakDocumentRepository);
|
|
18
|
+
getAllDocumentsAsync(): Promise<KanonakDocument[]>;
|
|
19
|
+
getDocumentAsync(identifier: string): Promise<KanonakDocument | null>;
|
|
20
|
+
getDocumentsByNamespaceAsync(publisher: string, package_: string): Promise<KanonakDocument[]>;
|
|
21
|
+
getHighestCompatibleVersionAsync(publisher: string, import_: Import): Promise<KanonakDocument | null>;
|
|
22
|
+
saveDocumentAsync(): Promise<void>;
|
|
23
|
+
deleteDocumentAsync(): Promise<void>;
|
|
24
|
+
clearNamespaceAsync(): Promise<void>;
|
|
25
|
+
getAllDocumentReferencesAsync(): Promise<DocumentReference[]>;
|
|
26
|
+
getDocumentContentAsync(identifier: string): Promise<string | null>;
|
|
27
|
+
getDocumentUriAsync(identifier: string): Promise<string | null>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SubjectKanonak, EmbeddedKanonak, DefinedKanonak, Kanonak, KanonakUri } from '@kanonak-protocol/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical URI identity for an ontology entity.
|
|
4
|
+
*
|
|
5
|
+
* Version is intentionally omitted: the CLI compares against
|
|
6
|
+
* publisher + package + name so it can accept any backwards-compatible
|
|
7
|
+
* version of the ontology it was built against. If a new major version
|
|
8
|
+
* breaks semantics, a separate entry (or explicit version check) is
|
|
9
|
+
* added at that point.
|
|
10
|
+
*/
|
|
11
|
+
export interface EntityUri {
|
|
12
|
+
publisher: string;
|
|
13
|
+
package_: string;
|
|
14
|
+
name: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Convert a `KanonakUri` or `EntityUri` into a stable map key.
|
|
18
|
+
* Version is dropped.
|
|
19
|
+
*/
|
|
20
|
+
export declare function uriKey(uri: KanonakUri | EntityUri): string;
|
|
21
|
+
/**
|
|
22
|
+
* True when a resolved `KanonakUri` matches the given publisher + package
|
|
23
|
+
* + local name (version ignored).
|
|
24
|
+
*/
|
|
25
|
+
export declare function uriMatches(uri: KanonakUri, target: EntityUri): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* True when a `Kanonak` is a `DefinedKanonak` whose `type` statement's
|
|
28
|
+
* object URI matches the given class URI.
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasType(kanonak: Kanonak, target: EntityUri): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Filter a list of parsed kanonaks down to `SubjectKanonak`s whose type
|
|
33
|
+
* matches the given class URI.
|
|
34
|
+
*/
|
|
35
|
+
export declare function findSubjectsByType(kanonaks: Kanonak[], target: EntityUri): SubjectKanonak[];
|
|
36
|
+
/**
|
|
37
|
+
* Look up a `SubjectKanonak` by full URI across a list of kanonaks.
|
|
38
|
+
* Used for following a reference to its definition (e.g. to read a
|
|
39
|
+
* `License`'s `spdxId` or a `Tool`'s `label`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function findSubjectByUri(kanonaks: Kanonak[], uri: KanonakUri): SubjectKanonak | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Read a datatype property value (string, number, boolean) off a defined
|
|
44
|
+
* kanonak, by property URI. Returns `undefined` when the property is not
|
|
45
|
+
* set or not a datatype value.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getDatatypeValue(defined: DefinedKanonak, property: EntityUri): string | number | boolean | undefined;
|
|
48
|
+
export declare function getStringValue(defined: DefinedKanonak, property: EntityUri): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Read the target `KanonakUri` of an ObjectProperty reference off a
|
|
51
|
+
* defined kanonak, by property URI. Returns `undefined` when the
|
|
52
|
+
* property is not set or not a reference statement.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getReferenceUri(defined: DefinedKanonak, property: EntityUri): KanonakUri | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Read an embedded kanonak off a defined kanonak, by property URI.
|
|
57
|
+
* Returns `undefined` when the property is not set or not an embedded
|
|
58
|
+
* statement.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getEmbeddedValue(defined: DefinedKanonak, property: EntityUri): EmbeddedKanonak | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Read the list values off a ListStatement property. Each item is either
|
|
63
|
+
* a `ReferenceKanonak` (named reference) or an `EmbeddedKanonak` (inline).
|
|
64
|
+
* Returns an empty array when the property is unset or not a list.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getListValues(defined: DefinedKanonak, property: EntityUri): Kanonak[];
|
|
@@ -2,7 +2,8 @@ import { Command } from 'commander';
|
|
|
2
2
|
/**
|
|
3
3
|
* Create the built-in "capability" command group.
|
|
4
4
|
*
|
|
5
|
-
* This is the bootstrap mechanism — always available, not dynamically
|
|
6
|
-
* Used to install, remove, and list capabilities that extend
|
|
5
|
+
* This is the bootstrap mechanism — always available, not dynamically
|
|
6
|
+
* loaded. Used to install, remove, and list capabilities that extend
|
|
7
|
+
* the CLI.
|
|
7
8
|
*/
|
|
8
9
|
export declare function capabilityCommand(): Command;
|
package/dist/index.js
CHANGED
|
@@ -1,73 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
3
|
-
`).some(r=>r.trim()===
|
|
4
|
-
${
|
|
5
|
-
`)}else
|
|
6
|
-
`)}getPath(e,
|
|
7
|
-
${d}:`);for(let
|
|
8
|
-
${d}:`),console.log(" ERROR: Failed to parse document"),c++;continue}let
|
|
9
|
-
${
|
|
10
|
-
${a.length} file(s) validated. ${c} error(s), ${l} warning(s).`),c>0&&process.exit(1)}import{KanonakParser as
|
|
11
|
-
`;function
|
|
12
|
-
Installed ${Object.keys(i.packages).length} package(s) from lock file.`);return}let a=
|
|
13
|
-
Installed ${
|
|
2
|
+
import{Command as yo}from"commander";import{readFileSync as ho}from"fs";import{fileURLToPath as ko}from"url";import{dirname as bo,join as Po}from"path";import{KanonakParser as vo,PublisherIndex as wo,CredentialStore as So,createAuthenticatedFetch as $o}from"@kanonak-protocol/sdk";import{readFileSync as hn,statSync as kn}from"fs";import{resolve as bn,dirname as Pn}from"path";import{KanonakParser as vn,KanonakObjectValidator as wn,ValidationSeverity as be}from"@kanonak-protocol/sdk";import{readFileSync as ln,readdirSync as pn}from"fs";import{join as dn,dirname as un,basename as ke}from"path";import{InMemoryKanonakDocumentRepository as mn,FileSystemKanonakDocumentRepository as gn,HttpKanonakDocumentRepository as fn,getGlobalCachePath as yn}from"@kanonak-protocol/sdk";import{mkdirSync as tn,readFileSync as ye,writeFileSync as he,existsSync as te,appendFileSync as on}from"fs";import{join as rn,dirname as sn,isAbsolute as an}from"path";import{getGlobalCachePath as cn}from"@kanonak-protocol/sdk";var _=class{constructor(e=cn()){this.cacheDir=e;this.isProjectLocal=!an(e)}cacheDir;gitignoreChecked=!1;isProjectLocal;get(e,n,o){let r=this.getPath(e,n,o);return te(r)?ye(r,"utf-8"):null}put(e,n,o,r){let s=this.getPath(e,n,o);tn(sn(s),{recursive:!0}),he(s,r,"utf-8"),this.isProjectLocal&&this.ensureGitignore()}has(e,n,o){return te(this.getPath(e,n,o))}ensureGitignore(){if(this.gitignoreChecked)return;this.gitignoreChecked=!0;let e=".gitignore",n=this.cacheDir;if(te(e)){if(ye(e,"utf-8").split(`
|
|
3
|
+
`).some(r=>r.trim()===n||r.trim()===n+"/"))return;on(e,`
|
|
4
|
+
${n}/
|
|
5
|
+
`)}else he(e,`${n}/
|
|
6
|
+
`)}getPath(e,n,o){return rn(this.cacheDir,e,`${n}@${o}.kan.yml`)}};function B(t){let e=t;for(;;){let n=un(e);if(n===e)break;if(ke(e).includes(".")||ke(n).includes(".")){e=n;continue}break}return e}function W(t,e){for(let n of pn(t,{withFileTypes:!0})){let o=dn(t,n.name);n.isDirectory()&&n.name!=="node_modules"&&n.name!==".kanonak"?W(o,e):n.name.endsWith(".kan.yml")&&e.push(o)}}var oe=class{repos;constructor(...e){this.repos=e}async getHighestCompatibleVersionAsync(e,n){for(let o of this.repos)try{let r=await o.getHighestCompatibleVersionAsync(e,n);if(r)return r}catch{}return null}async getAllDocumentsAsync(){let e=[];for(let n of this.repos)try{e.push(...await n.getAllDocumentsAsync())}catch{}return e}async getDocumentAsync(e){for(let n of this.repos)try{let o=await n.getDocumentAsync(e);if(o)return o}catch{}return null}async getDocumentsByNamespaceAsync(e,n){for(let o of this.repos)try{let r=await o.getDocumentsByNamespaceAsync(e,n);if(r.length>0)return r}catch{}return[]}async saveDocumentAsync(e,n){await this.repos[0].saveDocumentAsync(e,n)}async deleteDocumentAsync(e){await this.repos[0].deleteDocumentAsync(e)}async clearNamespaceAsync(e,n){await this.repos[0].clearNamespaceAsync(e,n)}async getAllDocumentReferencesAsync(){return this.repos[0].getAllDocumentReferencesAsync()}async getDocumentContentAsync(e){for(let n of this.repos)try{let o=await n.getDocumentContentAsync(e);if(o)return o}catch{}return null}async getDocumentUriAsync(e){for(let n of this.repos)try{let o=await n.getDocumentUriAsync(e);if(o)return o}catch{}return null}};async function A(t,e){let n=[];W(t,n);let o=new mn(e);for(let a of n)try{let c=ln(a,"utf-8"),l=e.parse(c),d=l.metadata.namespace_;if(d){let u=`${d.publisher}/${d.package_}@${d.version}`;await o.saveDocumentAsync(l,u)}}catch{}let r=new gn(yn(),!0,e),s=new _,i=new fn({getFromCache:(a,c,l)=>s.get(a,c,l),onFetch:(a,c,l,d)=>s.put(a,c,l,d)});return new oe(o,r,i)}async function Pe(t){let e=bn(t),n=kn(e),o=n.isDirectory()?B(e):B(Pn(e)),r=new vn,s=new wn(r),i=await A(o,r),a=[];if(n.isDirectory()?W(e,a):a.push(e),a.length===0){console.log("No .kan.yml files found.");return}let c=0,l=0;for(let d of a){let u=hn(d,"utf-8"),p=r.parseWithErrors(u);if(p.errors&&p.errors.length>0){console.log(`
|
|
7
|
+
${d}:`);for(let m of p.errors)console.log(` ERROR (parse): ${m.message} [line ${m.line}:${m.column}]`),c++;continue}let g=p.document;if(!g){console.log(`
|
|
8
|
+
${d}:`),console.log(" ERROR: Failed to parse document"),c++;continue}let f=await s.validateAsync(g,i),y=f.errors.filter(m=>m.severity===be.Error),k=f.errors.filter(m=>m.severity===be.Warning);if(y.length>0||k.length>0){let m=g.metadata.namespace_,b=m?`${m.publisher}/${m.package_}@${m.version}`:d;console.log(`
|
|
9
|
+
${b}:`);for(let h of y)console.log(` ERROR: ${h.message}`),h.suggestion&&console.log(` -> ${h.suggestion}`);for(let h of k)console.log(` WARN: ${h.message}`)}c+=y.length,l+=k.length}console.log(`
|
|
10
|
+
${a.length} file(s) validated. ${c} error(s), ${l} warning(s).`),c>0&&process.exit(1)}import{KanonakParser as Dn,PublisherIndex as In,CredentialStore as An,createAuthenticatedFetch as Kn}from"@kanonak-protocol/sdk";import{readFileSync as Sn,writeFileSync as $n,existsSync as Rn}from"fs";import{createHash as Cn}from"crypto";import ve from"js-yaml";var re="kanonak.lock",_n=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
11
|
+
`;function we(){if(!Rn(re))return null;let t=Sn(re,"utf-8"),e=ve.load(t);return!e||typeof e!="object"||e.version!=="1"?null:{version:"1",lastUpdated:e.lastUpdated??new Date().toISOString(),packages:e.packages??{}}}function Se(t){t.lastUpdated=new Date().toISOString();let e={};for(let o of Object.keys(t.packages).sort())e[o]=t.packages[o];t.packages=e;let n=ve.dump(t,{lineWidth:-1,sortKeys:!1,quotingType:'"'});$n(re,_n+n,"utf-8")}function O(t){return`sha256:${Cn("sha256").update(t).digest("hex")}`}function U(t){let e=t.indexOf("@"),n,o=null;e!==-1?(n=t.substring(0,e),o=t.substring(e+1)||null):n=t;let r=n.indexOf("/");if(r===-1)return null;let s=n.substring(0,r),i=n.substring(r+1);return!s||!i?null:{publisher:s,packageName:i,version:o}}async function $e(t){let e=new _,n=new Dn,o=new An,r=Kn(o),s=new In({fetchFn:r}),i=we()??{version:"1",lastUpdated:new Date().toISOString(),packages:{}};if(!t){Object.keys(i.packages).length===0&&(console.error("No kanonak.lock file found or lock file is empty."),console.error("Usage: kanonak install {publisher}/{package}[@{version}]"),process.exit(1)),console.log(`Installing ${Object.keys(i.packages).length} package(s) from kanonak.lock...`);for(let[g,f]of Object.entries(i.packages)){let y=g.indexOf("/"),k=g.substring(0,y),m=g.substring(y+1),b=e.get(k,m,f.version);if(b)console.log(` ${g}@${f.version} (cached)`);else{let h=f.resolved,P=await r(h,k);if(!P.ok)throw new Error(`Failed to fetch ${h} (${P.status} ${P.statusText})`);b=await P.text(),e.put(k,m,f.version,b),console.log(` ${g}@${f.version}`)}}console.log(`
|
|
12
|
+
Installed ${Object.keys(i.packages).length} package(s) from lock file.`);return}let a=U(t);a||(console.error(`Invalid package reference: ${t}`),console.error("Expected format: {publisher}/{package}@{version} or {publisher}/{package}"),process.exit(1));let{publisher:c,packageName:l,version:d}=a,u=d??await s.getHighestVersion(c,l);u||(console.error(`Could not resolve version for ${c}/${l}`),process.exit(1)),console.log(`Installing ${c}/${l}@${u}...`);let p=new Set;await Re(c,l,u,e,n,s,r,p,i),Se(i),console.log(`
|
|
13
|
+
Installed ${p.size} package(s).`)}async function Re(t,e,n,o,r,s,i,a,c){let l=`${t}/${e}@${n}`;if(a.has(l))return;a.add(l);let d=`${t}/${e}`,u=o.get(t,e,n),p;if(u)console.log(` ${l} (cached)`),p=await s.getPackageUrl(t,e,n);else{p=await s.getPackageUrl(t,e,n);let y=await i(p,t);if(!y.ok)throw new Error(`Failed to fetch ${p} (${y.status} ${y.statusText})`);u=await y.text(),o.put(t,e,n,u),console.log(` ${l}`)}let g={},f=r.parse(u);if(f.metadata.imports)for(let[y,k]of Object.entries(f.metadata.imports))for(let m of k){let b=await s.resolveVersion(y,m);b?(g[`${y}/${m.packageName}`]=b,await Re(y,m.packageName,b,o,r,s,i,a,c)):console.error(` WARNING: Could not resolve ${y}/${m.packageName} ${m.package_}`)}c.packages[d]={version:n,resolved:p,integrity:O(u),dependencies:g}}import{readFileSync as xn}from"fs";import{resolve as En,dirname as Tn}from"path";import{KanonakParser as On}from"@kanonak-protocol/sdk";async function Ce(t){let e=En(t),n=xn(e,"utf-8"),o=new On,r=o.parse(n),s=r.metadata.namespace_,i=s?`${s.publisher}/${s.package_}@${s.version}`:t;if(console.log(i),!r.metadata.imports){console.log(" (no imports)");return}let a=B(Tn(e)),c=await A(a,o),l=new Set;for(let[d,u]of Object.entries(r.metadata.imports))for(let p of u)await _e(d,p,c,l," ")}async function _e(t,e,n,o,r){let s=`${t}/${e.packageName}`;if(o.has(s)){console.log(`${r}${s} (${e.package_}) [circular]`);return}o.add(s);let i;try{i=await n.getHighestCompatibleVersionAsync(t,e)}catch{console.log(`${r}${s} (${e.package_}) [fetch failed]`);return}if(!i){console.log(`${r}${s} (${e.package_}) [not found]`);return}let a=i.metadata.namespace_?.version?.toString()??"?";if(console.log(`${r}${t}/${e.packageName}@${a}`),i.metadata.imports)for(let[c,l]of Object.entries(i.metadata.imports))for(let d of l)await _e(c,d,n,o,r+" ")}import{createHash as jn,randomBytes as Ae}from"crypto";import{createServer as Ln}from"http";import{execFile as se}from"child_process";import{CredentialStore as Nn,generateDPoPKeyPair as Fn,createDPoPProof as De,serverSupportsDPoP as Hn}from"@kanonak-protocol/sdk";import{normalizeHost as Un}from"@kanonak-protocol/sdk";var K=class{cache=new Map;async discover(e){let n=Un(e);if(this.cache.has(n))return this.cache.get(n);let o=`https://${n}/.well-known/oauth-authorization-server`,r=await this.tryEndpoint(o);if(!r){let s=`https://${n}/.well-known/openid-configuration`;r=await this.tryEndpoint(s)}return this.cache.set(n,r),r}async supportsOAuth(e){return await this.discover(e)!==null}static supportsPkceS256(e){return e.codeChallengeMethodsSupported?.some(n=>n.toUpperCase()==="S256")??!1}static supportsDynamicRegistration(e){return!!e.registrationEndpoint}static supportsAuthorizationCode(e){return e.responseTypesSupported?.some(n=>n.toLowerCase()==="code")??!1}async tryEndpoint(e){let n;try{n=await fetch(e)}catch(r){let s=r instanceof Error?r.message:String(r);return s.includes("ENOTFOUND")||s.includes("ECONNREFUSED")?(console.error(` OAuth discovery: ${e} \u2014 host unreachable (${s})`),console.error(" If the server is behind a VPN, ensure you are connected.")):s.includes("CERT")||s.includes("SSL")||s.includes("TLS")?(console.error(` OAuth discovery: ${e} \u2014 TLS error (${s})`),console.error(" If using a custom CA certificate, set NODE_EXTRA_CA_CERTS=/path/to/ca.pem")):console.error(` OAuth discovery: ${e} \u2014 network error: ${s}`),null}if(!n.ok)return n.status===404||(n.status===403?(console.error(` OAuth discovery: ${e} \u2014 HTTP 403 Forbidden`),console.error(" Access may be blocked by a firewall, proxy, or WAF.")):n.status>=500&&(console.error(` OAuth discovery: ${e} \u2014 HTTP ${n.status} server error`),console.error(" The authorization server returned an internal error. Contact your IDP administrator."))),null;let o;try{o=await n.json()}catch{return console.error(` OAuth discovery: ${e} \u2014 response is not valid JSON`),console.error(" The endpoint may be returning HTML instead of JSON. Check IDP configuration."),null}return{issuer:z(o.issuer),authorizationEndpoint:z(o.authorization_endpoint),tokenEndpoint:z(o.token_endpoint),registrationEndpoint:z(o.registration_endpoint),revocationEndpoint:z(o.revocation_endpoint),scopesSupported:j(o.scopes_supported),responseTypesSupported:j(o.response_types_supported),grantTypesSupported:j(o.grant_types_supported),codeChallengeMethodsSupported:j(o.code_challenge_methods_supported),tokenEndpointAuthMethodsSupported:j(o.token_endpoint_auth_methods_supported),dpopSigningAlgValuesSupported:j(o.dpop_signing_alg_values_supported)}}};function z(t){return typeof t=="string"?t:null}function j(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"):null}var N=class{discovery;credentialStore;constructor(e,n){this.discovery=e??new K,this.credentialStore=n??new Nn}async authorize(e,n=[]){console.log(`Starting OAuth flow for ${e}...`);let o=await this.discovery.discover(e);if(!o)return $(`No OAuth discovery endpoint found for '${e}'.
|
|
14
14
|
The server must expose one of:
|
|
15
15
|
- https://${e}/.well-known/oauth-authorization-server (RFC 8414)
|
|
16
16
|
- https://${e}/.well-known/openid-configuration (OpenID Connect)
|
|
17
17
|
Verify the hostname is correct and HTTPS is configured.
|
|
18
|
-
If using a corporate proxy, ensure it is not blocking the discovery request.`);let r=[];if(o.authorizationEndpoint||r.push("authorization_endpoint"),o.tokenEndpoint||r.push("token_endpoint"),r.length>0)return
|
|
19
|
-
Contact the IDP administrator to ensure these are included in the discovery document.`);let s=
|
|
20
|
-
Ensure your Node.js installation supports EC P-256 curves.`)}}let{redirectUri:a,port:c,waitForCallback:l,close:d}=await
|
|
18
|
+
If using a corporate proxy, ensure it is not blocking the discovery request.`);let r=[];if(o.authorizationEndpoint||r.push("authorization_endpoint"),o.tokenEndpoint||r.push("token_endpoint"),r.length>0)return $(`OAuth metadata for '${e}' is missing required fields: ${r.join(", ")}.
|
|
19
|
+
Contact the IDP administrator to ensure these are included in the discovery document.`);let s=Hn(o.dpopSigningAlgValuesSupported),i=null;if(s){console.log(" Server supports DPoP (RFC 9449) \u2014 generating key pair...");try{i=Fn()}catch(R){return $(`Failed to generate DPoP key pair: ${L(R)}
|
|
20
|
+
Ensure your Node.js installation supports EC P-256 curves.`)}}let{redirectUri:a,port:c,waitForCallback:l,close:d}=await Wn(),u=await this.credentialStore.getCredential(e),p=u?.clientId??null,g=u?.clientSecret??null;if(!p&&o.registrationEndpoint){console.log(" Registering dynamic OAuth client (RFC 7591)...");let R=await this.registerClient(o.registrationEndpoint,a);if(!R.success)return d(),$(R.error);p=R.clientId,g=R.clientSecret??null}if(!p)return d(),$(`No OAuth client credentials for '${e}' and the server does not support dynamic client registration (no registration_endpoint in metadata).
|
|
21
21
|
You must pre-register a client with your IDP and configure the client_id.
|
|
22
22
|
Ask your IDP administrator to enable dynamic client registration (RFC 7591),
|
|
23
|
-
or add a registration_endpoint to the OAuth metadata.`);let
|
|
23
|
+
or add a registration_endpoint to the OAuth metadata.`);let f=Vn(),y=Bn(f),k=zn(),m=Gn(o.authorizationEndpoint,p,a,n,k,y);console.log(` Opening browser for authorization on port ${c}...`),console.log(" If the browser doesn't open, navigate to:"),console.log(` ${m}`),Xn(m);let b=await l();if(d(),!b)return $(`Authorization timed out after 5 minutes.
|
|
24
24
|
The browser authorization was not completed in time.
|
|
25
|
-
Re-run 'kanonak login ${e}' and complete the browser authorization promptly.`);if(!
|
|
25
|
+
Re-run 'kanonak login ${e}' and complete the browser authorization promptly.`);if(!b.code){let R=b.error?`: ${b.error}`:"";return $(`No authorization code received${R}.
|
|
26
26
|
The IDP may have rejected the authorization request.
|
|
27
|
-
Check the IDP admin console for error details.`)}if(
|
|
27
|
+
Check the IDP admin console for error details.`)}if(b.state!==k)return $(`Authorization state mismatch \u2014 possible CSRF attack.
|
|
28
28
|
The response did not match the expected session.
|
|
29
|
-
Re-run 'kanonak login ${e}' to start a fresh authorization flow.`);console.log(" Exchanging authorization code for tokens...");let
|
|
29
|
+
Re-run 'kanonak login ${e}' to start a fresh authorization flow.`);console.log(" Exchanging authorization code for tokens...");let h=await this.exchangeCode(o.tokenEndpoint,p,g,b.code,a,f,i);if(!h.success)return $(h.error);let P={clientId:p,clientSecret:g,accessToken:h.tokens.accessToken,refreshToken:h.tokens.refreshToken,expiresAt:h.tokens.expiresIn?new Date(Date.now()+h.tokens.expiresIn*1e3).toISOString():null,tokenEndpoint:o.tokenEndpoint,dpopKeyPair:i};return await this.credentialStore.store(e,P),console.log(` Successfully authenticated with ${e}`),s&&console.log(" DPoP proof-of-possession is active"),{success:!0,host:e}}async refresh(e){let n=await this.credentialStore.getCredential(e);if(!n)return $(`No stored credentials for '${e}'. Run 'kanonak login ${e}' to authenticate first.`);if(!n.refreshToken)return $(`No refresh token available for '${e}'.
|
|
30
30
|
The authorization server may not issue refresh tokens for this client.
|
|
31
|
-
Run 'kanonak login ${e}' to re-authenticate.`);if(!
|
|
32
|
-
The stored credential may be corrupted. Run 'kanonak logout ${e}' then 'kanonak login ${e}'.`);let o=
|
|
31
|
+
Run 'kanonak login ${e}' to re-authenticate.`);if(!n.clientId)return $(`No client ID in stored credentials for '${e}'.
|
|
32
|
+
The stored credential may be corrupted. Run 'kanonak logout ${e}' then 'kanonak login ${e}'.`);let o=n.tokenEndpoint;if(!o){let s=await this.discovery.discover(e);if(!s?.tokenEndpoint)return $(`Cannot find token endpoint for '${e}'.
|
|
33
33
|
The OAuth discovery endpoint may be unreachable.
|
|
34
|
-
Check network connectivity and run 'kanonak login ${e}' to re-authenticate.`);o=s.tokenEndpoint}let r=await this.refreshTokenRequest(o,
|
|
35
|
-
The refresh token may have expired. Run 'kanonak login ${e}' to re-authenticate.`)}async logout(e){let
|
|
36
|
-
You may not be logged in, or credentials may be stored in a different backend.`);let o=await this.discovery.discover(e);if(o?.revocationEndpoint&&
|
|
34
|
+
Check network connectivity and run 'kanonak login ${e}' to re-authenticate.`);o=s.tokenEndpoint}let r=await this.refreshTokenRequest(o,n.clientId,n.clientSecret,n.refreshToken,n.dpopKeyPair);return r.success?(n.accessToken=r.tokens.accessToken,r.tokens.refreshToken&&(n.refreshToken=r.tokens.refreshToken),n.expiresAt=r.tokens.expiresIn?new Date(Date.now()+r.tokens.expiresIn*1e3).toISOString():null,n.tokenEndpoint=o,await this.credentialStore.store(e,n),{success:!0,host:e}):$(`${r.error}
|
|
35
|
+
The refresh token may have expired. Run 'kanonak login ${e}' to re-authenticate.`)}async logout(e){let n=await this.credentialStore.getCredential(e);if(!n)return $(`No stored credentials for '${e}'.
|
|
36
|
+
You may not be logged in, or credentials may be stored in a different backend.`);let o=await this.discovery.discover(e);if(o?.revocationEndpoint&&n.accessToken&&n.clientId)try{await this.revokeToken(o.revocationEndpoint,n.accessToken,n.clientId)}catch{console.warn(` Warning: Token revocation failed for '${e}'. The token has been removed locally but may still be valid on the server until it expires.`)}return await this.credentialStore.remove(e),{success:!0,host:e}}async registerClient(e,n){let o;try{o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:"Kanonak CLI",redirect_uris:[n],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"})})}catch(i){return{success:!1,error:`Dynamic client registration request to ${e} failed: ${L(i)}
|
|
37
37
|
Ensure the IDP is reachable and HTTPS is properly configured.
|
|
38
|
-
If using a corporate proxy or custom CA, set NODE_EXTRA_CA_CERTS.`}}if(!o.ok){let i=await
|
|
38
|
+
If using a corporate proxy or custom CA, set NODE_EXTRA_CA_CERTS.`}}if(!o.ok){let i=await ie(o);return{success:!1,error:`Dynamic client registration (RFC 7591) failed.
|
|
39
39
|
Endpoint: ${e}
|
|
40
40
|
HTTP ${o.status}: ${i}
|
|
41
41
|
The IDP may not support dynamic registration for public clients.
|
|
42
42
|
Ask your IDP administrator to enable RFC 7591 support, or pre-register a client manually.`}}let r=await o.json(),s=r.client_id;return s?{success:!0,clientId:s,clientSecret:r.client_secret}:{success:!1,error:`Dynamic client registration response from ${e} did not contain a client_id.
|
|
43
|
-
The IDP response may be malformed. Contact your IDP administrator.`}}async exchangeCode(e,
|
|
44
|
-
The stored key pair may be corrupted. Run 'kanonak login' to re-authenticate.`}}let d;try{d=await fetch(e,{method:"POST",headers:l,body:c.toString()})}catch(
|
|
45
|
-
Ensure the token endpoint is reachable.`}}if(!d.ok){let
|
|
43
|
+
The IDP response may be malformed. Contact your IDP administrator.`}}async exchangeCode(e,n,o,r,s,i,a){let c=new URLSearchParams({grant_type:"authorization_code",client_id:n,code:r,redirect_uri:s,code_verifier:i});o&&c.set("client_secret",o);let l={"Content-Type":"application/x-www-form-urlencoded"};if(a)try{l.DPoP=De(a.privateKey,a.publicKey,"POST",e)}catch(u){return{success:!1,error:`Failed to create DPoP proof for token exchange: ${L(u)}
|
|
44
|
+
The stored key pair may be corrupted. Run 'kanonak login' to re-authenticate.`}}let d;try{d=await fetch(e,{method:"POST",headers:l,body:c.toString()})}catch(u){return{success:!1,error:`Token exchange request to ${e} failed: ${L(u)}
|
|
45
|
+
Ensure the token endpoint is reachable.`}}if(!d.ok){let u=await ie(d),p=Mn(u);return{success:!1,error:`Token exchange failed.
|
|
46
46
|
Endpoint: ${e}
|
|
47
|
-
HTTP ${d.status}: ${
|
|
48
|
-
${
|
|
49
|
-
Authenticated with ${
|
|
47
|
+
HTTP ${d.status}: ${u}`+(p?`
|
|
48
|
+
${p}`:"")}}return{success:!0,tokens:Ie(await d.json())}}async refreshTokenRequest(e,n,o,r,s){let i=new URLSearchParams({grant_type:"refresh_token",client_id:n,refresh_token:r});o&&i.set("client_secret",o);let a={"Content-Type":"application/x-www-form-urlencoded"};if(s)try{a.DPoP=De(s.privateKey,s.publicKey,"POST",e)}catch(l){return{success:!1,error:`Failed to create DPoP proof for token refresh: ${L(l)}`}}let c;try{c=await fetch(e,{method:"POST",headers:a,body:i.toString()})}catch(l){return{success:!1,error:`Token refresh request to ${e} failed: ${L(l)}`}}if(!c.ok){let l=await ie(c);return{success:!1,error:`Token refresh failed \u2014 HTTP ${c.status}: ${l}`}}return{success:!0,tokens:Ie(await c.json())}}async revokeToken(e,n,o){await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:n,client_id:o}).toString()})}};function $(t){return{success:!1,error:t}}function Ie(t){return{accessToken:t.access_token,refreshToken:t.refresh_token,expiresIn:typeof t.expires_in=="number"?t.expires_in:void 0}}function L(t){return t instanceof Error?t.message:String(t)}async function ie(t){try{return await t.text()}catch{return"(could not read response body)"}}function Mn(t){try{let e=JSON.parse(t);switch(e.error){case"invalid_grant":return"Hint: The authorization code may have expired or already been used. Re-run the login flow.";case"invalid_client":return"Hint: The client credentials were rejected. The client_id may be invalid or the client_secret may be wrong.";case"redirect_uri_mismatch":return"Hint: The redirect_uri does not match what was registered with the IDP.";case"unsupported_grant_type":return"Hint: The IDP does not support authorization_code grants. Contact your IDP administrator.";case"invalid_scope":return"Hint: One or more requested scopes are not allowed. Check the scopes configured on the IDP.";case"use_dpop_nonce":return"Hint: The server requires a DPoP nonce. This should be handled automatically \u2014 please report this as a bug.";default:return e.error_description?`IDP says: ${e.error_description}`:null}}catch{return null}}function Vn(){return Ae(32).toString("base64url")}function Bn(t){return jn("sha256").update(t).digest("base64url")}function zn(){return Ae(16).toString("base64url")}function Gn(t,e,n,o,r,s){let i=new URLSearchParams({client_id:e,response_type:"code",redirect_uri:n,scope:o.join(" "),state:r,code_challenge:s,code_challenge_method:"S256"});return`${t}?${i}`}var qn=300*1e3;async function Wn(){return new Promise(t=>{let e=Ln((r,s)=>{let i=new URL(r.url,"http://localhost"),a=i.searchParams.get("code")??void 0,c=i.searchParams.get("state")??void 0,l=i.searchParams.get("error")??i.searchParams.get("error_description")??void 0,d=l?Yn(l):null,u=d?`<html><body><h1>Authorization failed</h1><p>${d}</p></body></html>`:"<html><body><h1>Authorization successful!</h1><p>You can close this window.</p></body></html>";s.writeHead(200,{"Content-Type":"text/html"}),s.end(u),n({code:a,state:c,error:l})}),n,o=new Promise(r=>{n=r,setTimeout(()=>{r(null),e.close()},qn)});e.listen(0,"127.0.0.1",()=>{let r=e.address();t({redirectUri:`http://localhost:${r.port}/callback`,port:r.port,waitForCallback:()=>o,close:()=>e.close()})})})}function Yn(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Xn(t){try{process.platform==="win32"?se("cmd",["/c","start","",t]):process.platform==="darwin"?se("open",[t]):se("xdg-open",[t])}catch{console.log(" Could not open browser automatically. Please navigate to the URL above manually.")}}import{CredentialStore as Jn}from"@kanonak-protocol/sdk";async function Ke(t){try{let e=new K,n=new Jn,r=await new N(e,n).authorize(t);r.success?console.log(`
|
|
49
|
+
Authenticated with ${t}.`):(console.error(`
|
|
50
50
|
Authentication failed:
|
|
51
|
-
${r.error}`),process.exit(1))}catch(e){let
|
|
51
|
+
${r.error}`),process.exit(1))}catch(e){let n=e instanceof Error?e.message:String(e);console.error(`
|
|
52
52
|
Authentication failed unexpectedly:
|
|
53
|
-
${
|
|
54
|
-
If this persists, file an issue at https://github.com/kanonak-protocol/typescript/issues`),process.exit(1)}}import{CredentialStore as
|
|
53
|
+
${n}`),console.error(`
|
|
54
|
+
If this persists, file an issue at https://github.com/kanonak-protocol/typescript/issues`),process.exit(1)}}import{CredentialStore as Qn}from"@kanonak-protocol/sdk";async function xe(t){try{let e=new K,n=new Qn,r=await new N(e,n).logout(t);r.success?console.log(`Logged out from ${t}.`):(console.error(`
|
|
55
55
|
Logout failed:
|
|
56
|
-
${r.error}`),process.exit(1))}catch(e){let
|
|
56
|
+
${r.error}`),process.exit(1))}catch(e){let n=e instanceof Error?e.message:String(e);console.error(`
|
|
57
57
|
Logout failed unexpectedly:
|
|
58
|
-
${
|
|
59
|
-
`;function
|
|
60
|
-
Installed capability "${
|
|
61
|
-
`);for(let[
|
|
62
|
-
`);for(let
|
|
63
|
-
Updated ${a} instance(s).`)}async search(e,
|
|
64
|
-
`);let
|
|
65
|
-
`);for(let
|
|
66
|
-
`);return{name:e,description:t,skillMd:c}}function tn(n){if(!Array.isArray(n)||n.length===0)return"";let e=[];for(let t of n){if(!t||typeof t!="object")continue;let o=t.title,r=L(t.body);o&&e.push(`## ${o}`),r&&e.push(r)}return e.join(`
|
|
58
|
+
${n}`),process.exit(1)}}import{Command as Rt}from"commander";import{KanonakParser as Ct,KanonakObjectParser as _t,PublisherIndex as Dt,CredentialStore as It,createAuthenticatedFetch as At}from"@kanonak-protocol/sdk";import{readFileSync as Zn,writeFileSync as et,existsSync as nt}from"fs";import{join as tt}from"path";import Ee from"js-yaml";import{getGlobalCachePath as ot}from"@kanonak-protocol/sdk";var rt=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
59
|
+
`;function Te(){let t=ot();return tt(t,"..","capabilities.lock")}function F(){let t=Te();if(!nt(t))return{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}};let e=Zn(t,"utf-8"),n=Ee.load(e);return!n||typeof n!="object"||n.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}}:{version:"1",lastUpdated:n.lastUpdated??new Date().toISOString(),capabilities:n.capabilities??{}}}function ae(t){t.lastUpdated=new Date().toISOString();let e={};for(let o of Object.keys(t.capabilities).sort())e[o]=t.capabilities[o];t.capabilities=e;let n=Ee.dump(t,{lineWidth:-1,sortKeys:!1,quotingType:'"'});et(Te(),rt+n,"utf-8")}var C=class{constructor(e,n){this.doc=e;this.broader=n}doc;broader;async getAllDocumentsAsync(){return[this.doc]}async getDocumentAsync(e){return this.broader.getDocumentAsync(e)}async getDocumentsByNamespaceAsync(e,n){return this.broader.getDocumentsByNamespaceAsync(e,n)}async getHighestCompatibleVersionAsync(e,n){return this.broader.getHighestCompatibleVersionAsync(e,n)}async saveDocumentAsync(){throw new Error("SingleDocumentRepository is read-only")}async deleteDocumentAsync(){throw new Error("SingleDocumentRepository is read-only")}async clearNamespaceAsync(){throw new Error("SingleDocumentRepository is read-only")}async getAllDocumentReferencesAsync(){return[]}async getDocumentContentAsync(e){return this.broader.getDocumentContentAsync(e)}async getDocumentUriAsync(e){return this.broader.getDocumentUriAsync(e)}};import{KanonakObjectParser as ut,EmbeddedKanonak as He,ReferenceKanonak as Me}from"@kanonak-protocol/sdk";import{SubjectKanonak as Oe,DefinedKanonak as st,StringStatement as it,NumberStatement as at,BooleanStatement as ct,ReferenceStatement as Ue,EmbeddedStatement as xr,ListStatement as lt}from"@kanonak-protocol/sdk";function D(t){return`${t.publisher}/${t.package_}/${t.name}`}function ce(t,e){return t.publisher===e.publisher&&t.package_===e.package_&&t.name===e.name}function pt(t,e){if(!(t instanceof st))return!1;for(let n of t.statement)if(n.predicate?.subject?.name==="type"&&n instanceof Ue){let r=n.object;if(ce(r.subject,e))return!0}return!1}function T(t,e){let n=[];for(let o of t)o instanceof Oe&&pt(o,e)&&n.push(o);return n}function je(t,e){for(let n of t){if(!(n instanceof Oe)||n.name!==e.name)continue;if((n.namespace||"").startsWith(`${e.publisher}/${e.package_}@`))return n}}function le(t,e){for(let n of t.statement){let o=n.predicate;if(o?.subject&&ce(o.subject,e))return n}}function dt(t,e){let n=le(t,e);if(n&&(n instanceof it||n instanceof at||n instanceof ct))return n.object}function v(t,e){let n=dt(t,e);return typeof n=="string"?n:void 0}function H(t,e){let n=le(t,e);if(n instanceof Ue)return n.object.subject}function M(t,e){let n=le(t,e);return n instanceof lt?n.object??[]:[]}var w="kanonak.org",S="capabilities",pe={publisher:w,package_:S,name:"Capability"},mt={publisher:w,package_:S,name:"commandName"},Ve={publisher:w,package_:S,name:"description"},gt={publisher:w,package_:S,name:"managesType"},ft={publisher:w,package_:S,name:"deploymentTarget"},yt={publisher:w,package_:S,name:"hasCommand"},ht={publisher:w,package_:S,name:"subcommandName"},kt={publisher:w,package_:S,name:"performs"},bt={publisher:w,package_:S,name:"hasArgument"},Pt={publisher:w,package_:S,name:"argumentName"},Le={publisher:w,package_:S,name:"isRequired"},Ne={publisher:w,package_:S,name:"isOption"},vt={publisher:w,package_:S,name:"defaultValue"};async function Be(t,e,n){let o=F(),r=new ut,s=[];for(let[i,a]of Object.entries(o.capabilities)){let c=t.get(a.publisher,a.package_,a.version);if(!c){console.error(` WARNING: Cached content missing for capability "${i}" (${a.publisher}/${a.package_}@${a.version})`);continue}try{let l=e.parse(c),d=new C(l,n),u=await r.parseKanonaks(d),p=wt(u,a.publisher,a.package_,a.version);p?s.push(p):console.error(` WARNING: No Capability instance found in "${i}" (${a.publisher}/${a.package_}@${a.version})`)}catch(l){console.error(` WARNING: Failed to parse capability "${i}": ${l}`)}}return s}function wt(t,e,n,o){let r=T(t,pe);if(r.length===0)return;let s=r[0],i=v(s,mt)??"",a=v(s,Ve)??"",c=H(s,gt),l=H(s,ft),d=[];for(let u of M(s,yt)){let p=St(u,t);p&&d.push(p)}return{commandName:i,description:a,managesTypeKey:c?D(c):"",deploymentTargetKey:l?D(l):"",commands:d,publisher:e,package_:n,version:o}}function St(t,e){let n;if(t instanceof He)n=t;else if(t instanceof Me){let a=ze(e,t.subject.publisher,t.subject.package_,t.subject.name);a&&(n=a)}if(!n)return;let o=v(n,ht)??"",r=v(n,Ve)??"",s=H(n,kt),i=[];for(let a of M(n,bt)){let c=$t(a,e);c&&i.push(c)}return{subcommandName:o,description:r,arguments:i,actionKey:s?D(s):""}}function $t(t,e){let n;if(t instanceof He)n=t;else if(t instanceof Me){let c=ze(e,t.subject.publisher,t.subject.package_,t.subject.name);c&&(n=c)}if(!n)return;let o=v(n,Pt)??"",r=v(n,Le)==="true"||Fe(n,Le)===!0,s=v(n,Ne)==="true"||Fe(n,Ne)===!0,i=v(n,vt),a={argumentName:o,required:r,isOption:s};return i!==void 0&&(a.defaultValue=i),a}function Fe(t,e){for(let n of t.statement){let o=n,r=o.predicate;if(r?.subject&&r.subject.publisher===e.publisher&&r.subject.package_===e.package_&&r.subject.name===e.name)return typeof o.object=="boolean"?o.object:void 0}}function ze(t,e,n,o){for(let r of t){if(!("name"in r)||!("namespace"in r))continue;let s=r;if(s.name===o&&!(!s.namespace||typeof s.namespace!="string")&&s.namespace.startsWith(`${e}/${n}@`))return r}}var Kt={publisher:w,package_:S,name:"commandName"};function Ge(){let t=new Rt("capability").description("Manage Kanonak CLI capabilities (pluggable command groups)");return t.command("add <package>").description("Install a capability from a Kanonak publisher").action(async e=>{await xt(e)}),t.command("remove <name>").description("Remove an installed capability").action(async e=>{await Et(e)}),t.command("list").description("List installed capabilities").action(async()=>{await Tt()}),t}async function xt(t){let e=U(t);e||(console.error(`Invalid package reference: ${t}`),console.error("Expected format: {publisher}/{package}@{version} or {publisher}/{package}"),process.exit(1));let{publisher:n,packageName:o,version:r}=e,s=new _,i=new Ct,a=new _t,c=new It,l=At(c),d=new Dt({fetchFn:l}),u=r??await d.getHighestVersion(n,o);u||(console.error(`Could not resolve version for ${n}/${o}`),process.exit(1)),console.log(`Installing capability ${n}/${o}@${u}...`);let p=s.get(n,o,u);if(!p){let R=await d.getPackageUrl(n,o,u),q=await l(R,n);if(!q.ok)throw new Error(`Failed to fetch ${R} (${q.status} ${q.statusText})`);p=await q.text(),s.put(n,o,u,p)}let g=i.parse(p),f=await A(process.cwd(),i),y=new C(g,f),k=await a.parseKanonaks(y),m=T(k,pe);m.length===0&&(console.error(`Package ${n}/${o}@${u} does not contain a ${w}/${S}/Capability instance.`),process.exit(1));let b=m[0],h=v(b,Kt);h||(console.error(`Capability instance in ${n}/${o}@${u} has no commandName property.`),process.exit(1));let P=F();P.capabilities[h]={publisher:n,package_:o,version:u,resolved:await d.getPackageUrl(n,o,u),integrity:O(p)},ae(P),console.log(`
|
|
60
|
+
Installed capability "${h}".`),console.log(`Run "kanonak ${h} --help" to get started.`)}async function Et(t){let e=F();e.capabilities[t]||(console.error(`Capability "${t}" is not installed.`),process.exit(1));let n=e.capabilities[t];delete e.capabilities[t],ae(e),console.log(`Removed capability "${t}" (${n.publisher}/${n.package_}@${n.version}).`)}async function Tt(){let t=F(),e=Object.entries(t.capabilities);if(e.length===0){console.log("No capabilities installed."),console.log("Install one with: kanonak capability add {publisher}/{package}");return}console.log(`Installed capabilities:
|
|
61
|
+
`);for(let[n,o]of e)console.log(` ${n} ${o.publisher}/${o.package_}@${o.version}`)}import{Command as qe}from"commander";function V(t){return`${w}/${S}/${t}`}var de={[V("fetchAndDeploy")]:(t,e,n,o)=>t.add(e,n[0],o),[V("removeDeployed")]:(t,e,n,o)=>t.remove(e,n[0],o),[V("listDeployed")]:(t,e,n,o)=>t.list(e,o),[V("updateDeployed")]:(t,e,n,o)=>t.update(e,n[0],o),[V("searchAvailable")]:(t,e,n,o)=>t.search(e,o),[V("showInfo")]:(t,e,n,o)=>t.info(e,n[0],o)};function We(t,e,n){for(let o of e){let r=new qe(o.commandName).description(o.description.trim());for(let s of o.commands){let i=r.command(Ot(s));i.description(s.description);for(let a of s.arguments)if(a.isOption){let c=a.defaultValue!=null?`--${a.argumentName} [value]`:`--${a.argumentName} <value>`;i.option(c,"",a.defaultValue)}i.action(async(...a)=>{let c=Ut(a);await jt(n,o,s,a,c)})}t.addCommand(r)}}function Ot(t){let e=t.arguments.filter(n=>!n.isOption).map(n=>n.required?`<${n.argumentName}>`:`[${n.argumentName}]`);return[t.subcommandName,...e].join(" ")}function Ut(t){for(let e=t.length-1;e>=0;e--)if(t[e]&&typeof t[e]=="object"&&!(t[e]instanceof qe))return t[e];return{}}async function jt(t,e,n,o,r){let s=[];for(let a of o)if(typeof a=="string")s.push(a);else break;n.actionKey||(console.error(`Capability subcommand '${n.subcommandName}' has no resolved \`performs:\` Action. Every CapabilityCommand must declare a performs value pointing at a named Action instance from the capabilities ontology. Known actions: ${Object.keys(de).join(", ")}.`),process.exit(1));let i=de[n.actionKey];i||(console.error(`Capability subcommand '${n.subcommandName}' references unknown Action '${n.actionKey}'. Known actions: ${Object.keys(de).join(", ")}.`),process.exit(1)),await i(t,e,s,r)}import{KanonakObjectParser as Lt,PublisherIndex as ue,PublisherConfigResolver as Nt}from"@kanonak-protocol/sdk";var Y=class{constructor(e,n,o,r,s,i){this.fileCache=e;this.parser=n;this.publisherIndex=o;this.fetchFn=r;this.handlerRegistry=s;this.repository=i}fileCache;parser;publisherIndex;fetchFn;handlerRegistry;repository;objectParser=new Lt;async add(e,n,o){let r=U(n);r||(console.error(`Invalid package reference: ${n}`),console.error("Expected format: {publisher}/{package}@{version} or {publisher}/{package}"),process.exit(1));let{publisher:s,packageName:i,version:a}=r,c=a??await this.publisherIndex.getHighestVersion(s,i);c||(console.error(`Could not resolve version for ${s}/${i}`),process.exit(1));let l=this.fileCache.get(s,i,c);if(!l){let k=await this.publisherIndex.getPackageUrl(s,i,c),m=await this.fetchFn(k,s);if(!m.ok)throw new Error(`Failed to fetch ${k} (${m.status} ${m.statusText})`);l=await m.text(),this.fileCache.put(s,i,c,l)}let d=this.parser.parse(l),u=new C(d,this.repository),p=await this.objectParser.parseKanonaks(u),g=Ye(e.managesTypeKey);g||(console.error(`Capability "${e.commandName}" has no resolved managesType.`),process.exit(1));let f=T(p,g);f.length===0&&(console.error(`Package ${s}/${i}@${c} does not contain any ${D(g)} instances.`),process.exit(1));let y=this.handlerRegistry.get(e.deploymentTargetKey);y?await y.deploy({instances:f,allKanonaks:p,repository:this.repository,options:o}):console.log(`Cached ${s}/${i}@${c} (no deployment handler for "${e.deploymentTargetKey}")`)}async remove(e,n,o){await this.requireHandler(e).undeploy(n,o)}async list(e,n){let r=await this.requireHandler(e).list(n),s=e.managesTypeKey||"managed";if(r.length===0){console.log(`No ${s} instances installed.`);return}console.log(`Installed ${s} instances:
|
|
62
|
+
`);for(let i of r){let a=i.publisher?`${i.publisher}/${i.package_}@${i.version}`:"unmanaged";console.log(` ${i.name} (${a}) ${i.path}`)}}async update(e,n,o){let s=await this.requireHandler(e).list(o),i=n?s.filter(c=>c.name===n):s.filter(c=>c.publisher);if(i.length===0){console.log(n?`"${n}" not found.`:"No managed instances to update.");return}let a=0;for(let c of i){let l=await this.publisherIndex.getHighestVersion(c.publisher,c.package_);!l||l===c.version||(console.log(`Updating ${c.name}: ${c.version} \u2192 ${l}`),await this.add(e,`${c.publisher}/${c.package_}@${l}`,o),a++)}console.log(a===0?"All instances are up to date.":`
|
|
63
|
+
Updated ${a} instance(s).`)}async search(e,n){let o=n.publisher??"kanonak.org",r=Ye(e.managesTypeKey);if(!r){console.error("Capability has no resolved managesType; nothing to search for.");return}console.log(`Searching ${o} for ${D(r)} instances...
|
|
64
|
+
`);let s=new Nt,i=await s.getConfig(o),a=s.resolveIndexUrl(o,i),c=await this.fetchFn(a,o);if(!c.ok){console.error(`Failed to fetch package index from ${o}.`);return}let l=await c.text(),d=ue.parseIndex(l);if(d.size===0){console.error(`No packages found for publisher "${o}".`);return}let u=new Map;for(let[f,y]of d){let k=[...y].sort((m,b)=>{let h=ue.parseVersion(m),P=ue.parseVersion(b);return!h||!P?0:h.major!==P.major?P.major-h.major:h.minor!==P.minor?P.minor-h.minor:P.patch-h.patch});u.set(f,k[0])}let p=[],g=0;for(let[f,y]of u){let k=this.fileCache.get(o,f,y);if(!k)try{let m=await this.publisherIndex.getPackageUrl(o,f,y),b=await this.fetchFn(m,o);if(!b.ok)continue;k=await b.text(),this.fileCache.put(o,f,y,k)}catch{continue}try{let m=this.parser.parse(k),b=new C(m,this.repository),h=await this.objectParser.parseKanonaks(b),P=T(h,r);for(let R of P)p.push({publisher:o,package_:f,version:y,subject:R});g++}catch{continue}}if(p.length===0){console.log(`No ${D(r)} instances found across ${g} package(s).`);return}console.log(`Found ${p.length} ${D(r)} instance(s):
|
|
65
|
+
`);for(let f of p)console.log(` ${f.publisher}/${f.package_}@${f.version}`),console.log(` ${f.subject.name}`),console.log()}async info(e,n,o){let i=(await this.requireHandler(e).list(o)).find(a=>a.name===n);i||(console.error(`"${n}" is not installed.`),process.exit(1)),console.log(`Name: ${i.name}`),console.log(`Type: ${e.managesTypeKey||"n/a"}`),console.log(`Publisher: ${i.publisher||"unmanaged"}`),console.log(`Package: ${i.package_||"n/a"}`),console.log(`Version: ${i.version||"n/a"}`),console.log(`Path: ${i.path}`)}requireHandler(e){let n=this.handlerRegistry.get(e.deploymentTargetKey);return n||(console.error(`No deployment handler registered for "${e.deploymentTargetKey}". Ensure the capability's deploymentTarget resolves to a DeploymentTarget instance whose URI is registered in the CLI at startup.`),process.exit(1)),n}};function Ye(t){if(!t)return;let e=t.split("/");if(e.length===3)return{publisher:e[0],package_:e[1],name:e[2]}}var X=class{handlers=new Map;register(e,n){this.handlers.set(e,n)}get(e){return this.handlers.get(e)??null}};import{mkdirSync as co,rmSync as lo,existsSync as Z,readdirSync as po,writeFileSync as uo}from"fs";import{join as ee}from"path";import{KanonakObjectParser as mo,KanonakParser as go}from"@kanonak-protocol/sdk";import{SubjectKanonak as Xe,EmbeddedKanonak as Ft,ReferenceKanonak as Ht}from"@kanonak-protocol/sdk";var x="kanonak.org",E="agent-skills",Mt="kanonak.org",Vt="core-spdx";var Bt={publisher:x,package_:E,name:"name"},zt={publisher:x,package_:E,name:"description"},Gt={publisher:x,package_:E,name:"license"},qt={publisher:x,package_:E,name:"compatibility"},Wt={publisher:x,package_:E,name:"allowedTools"},Yt={publisher:x,package_:E,name:"hasSection"},Xt={publisher:x,package_:E,name:"title"},Jt={publisher:x,package_:E,name:"body"},Qt={publisher:Mt,package_:Vt,name:"spdxId"};async function Je(t,e,n,o,r){let s=[],i=new ge(n,o,r);for(let a of e){let c=await Zt(a,t,i);c&&s.push(c)}return s}async function Zt(t,e,n){let o=v(t,Bt),r=v(t,zt);if(!o||!r)return;let s=[];s.push(`name: ${o}`),s.push(`description: ${me(r)}`);let i=H(t,Gt);if(i){let p=await n.resolveSubject(i,e),g=p?v(p,Qt):void 0;s.push(`license: ${g??i.name}`)}let a=v(t,qt);a&&s.push(`compatibility: ${me(a)}`);let c=M(t,Wt);if(c.length>0){let p=await no(c,e,n);p&&s.push(`allowed-tools: ${p}`)}let l=[];for(let p of M(t,Yt)){let g=eo(p);g&&l.push(g)}let d=l.join(`
|
|
67
66
|
|
|
68
|
-
`)
|
|
69
|
-
${
|
|
70
|
-
\`\`\``)}else if(s==="ReferenceResource"||s.endsWith(".ReferenceResource")){let a=L(r.content);a&&t.push(a)}}return t.join(`
|
|
67
|
+
`),u=["---",...s,"---","",d].join(`
|
|
68
|
+
`);return{name:o,description:r,skillMd:u}}function eo(t){if(!(t instanceof Ft))return"";let e=v(t,Xt),n=v(t,Jt),o=[];return e&&o.push(`## ${e}`),n&&o.push(me(n)),o.join(`
|
|
71
69
|
|
|
72
|
-
`)}function
|
|
73
|
-
`;function
|
|
70
|
+
`)}async function no(t,e,n){let o=[];for(let r of t)r instanceof Ht&&o.push(r.subject.name);return o.join(" ")}function me(t){return t.trimEnd()}var ge=class{constructor(e,n,o){this.repository=e;this.parser=n;this.objectParser=o}repository;parser;objectParser;cache=new Map;async resolveSubject(e,n){let o=je(n,e);if(o)return o;let r=`${e.publisher}/${e.package_}`,s=this.cache.get(r);if(!s){let i=await this.repository.getDocumentsByNamespaceAsync(e.publisher,e.package_);if(i.length===0)return;let a=i[0],c=new C(a,this.repository);s=await this.objectParser.parseKanonaks(c),this.cache.set(r,s)}for(let i of s)if(i instanceof Xe&&i.name===e.name&&i.namespace&&i.namespace.startsWith(`${e.publisher}/${e.package_}@`))return i;for(let i of s)if(i instanceof Xe&&i.name===e.name)return i}};import{join as Qe}from"path";import{homedir as to}from"os";function G(t){let e=t.scope??"project",n=t.client??oo(),o=n?`.${n}`:".agents",r=e==="user"?to():process.cwd();return Qe(r,o,"skills")}function J(t,e){return Qe(G(e),t)}function oo(){if(process.env.CLAUDE_CODE||process.env.CLAUDE_PROJECT_DIR)return"claude";if(process.env.GEMINI_CLI)return"gemini";if(process.env.CODEX_CLI||process.env.OPENAI_CODEX)return"codex"}import{readFileSync as ro,writeFileSync as so,existsSync as io}from"fs";import{join as Ze}from"path";import en from"js-yaml";var nn="skills.lock",ao=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
71
|
+
`;function Q(t){let e=Ze(t,nn);if(!io(e))return{version:"1",lastUpdated:new Date().toISOString(),skills:{}};let n=ro(e,"utf-8"),o=en.load(n);return!o||typeof o!="object"||o.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),skills:{}}:{version:"1",lastUpdated:o.lastUpdated??new Date().toISOString(),skills:o.skills??{}}}function fe(t,e){e.lastUpdated=new Date().toISOString();let n={};for(let s of Object.keys(e.skills).sort())n[s]=e.skills[s];e.skills=n;let o=Ze(t,nn),r=en.dump(e,{lineWidth:-1,sortKeys:!1,quotingType:'"'});so(o,ao+r,"utf-8")}var ne=class{parser=new go;objectParser=new mo;async deploy(e){let{instances:n,allKanonaks:o,repository:r,options:s}=e;if(n.length===0){console.error("No Skill instances to deploy.");return}let i=await Je(o,n,r,this.parser,this.objectParser);if(i.length===0){console.error("No skills could be transformed from the package.");return}let a=G(s),c=Q(a),l=n[0].namespace,{publisher:d,package_:u,version:p}=fo(l);for(let g of i){let f=J(g.name,s),y=Z(ee(f,"SKILL.md"));co(f,{recursive:!0}),uo(ee(f,"SKILL.md"),g.skillMd,"utf-8"),c.skills[g.name]={publisher:d,package_:u,version:p,resolved:d?`kanonak://${d}/${u}@${p}`:"",integrity:O(g.skillMd)},console.log(` ${y?"Updated":"Installed"} skill "${g.name}" \u2192 ${f}`)}fe(a,c)}async undeploy(e,n){let o=J(e,n);Z(o)||(console.error(`Skill "${e}" is not installed at ${o}`),process.exit(1)),lo(o,{recursive:!0,force:!0});let r=G(n),s=Q(r);delete s.skills[e],fe(r,s),console.log(`Removed skill "${e}" from ${o}`)}async list(e){let n=G(e),o=Q(n),r=[];for(let[s,i]of Object.entries(o.skills))r.push({name:s,publisher:i.publisher,package_:i.package_,version:i.version,path:J(s,e)});if(Z(n))try{let s=po(n,{withFileTypes:!0});for(let i of s){if(!i.isDirectory()||o.skills[i.name])continue;let a=ee(n,i.name,"SKILL.md");Z(a)&&r.push({name:i.name,publisher:"",package_:"",version:"",path:ee(n,i.name)})}}catch{}return r}};function fo(t){if(!t)return{publisher:"",package_:"",version:""};let e=t.indexOf("@");if(e===-1)return{publisher:"",package_:"",version:""};let n=t.substring(0,e),o=t.substring(e+1),r=n.indexOf("/");return r===-1?{publisher:"",package_:"",version:""}:{publisher:n.substring(0,r),package_:n.substring(r+1),version:o}}var Ro=bo(ko(import.meta.url)),Co=JSON.parse(ho(Po(Ro,"..","package.json"),"utf-8")),I=new yo;I.name("kanonak").description("Kanonak Protocol CLI - Validate and resolve Kanonak ontology packages").version(Co.version);I.command("validate <path>").description("Validate .kan.yml file(s). Resolves imports via HTTP from publisher domains.").action(async t=>{await Pe(t)});I.command("install [package]").description("Install a package and its dependencies, or install all from kanonak.lock.").action(async t=>{await $e(t)});I.command("deps <path>").description("Show resolved dependency tree for a .kan.yml file.").action(async t=>{await Ce(t)});I.command("login <publisher>").description("Authenticate with a package publisher using OAuth 2.0.").action(async t=>{await Ke(t)});I.command("logout <publisher>").description("Revoke tokens and remove stored credentials for a publisher.").action(async t=>{await xe(t)});I.addCommand(Ge());async function _o(){try{let t=new _,e=new vo,n=new So,o=$o(n),r=new wo({fetchFn:o}),s=await A(process.cwd(),e),i=new X;i.register("kanonak.org/capabilities/AgentSkillDeployment",new ne);let a=await Be(t,e,s),c=new Y(t,e,r,o,i,s);We(I,a,c)}catch{}}await _o();I.parse();
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { DeploymentHandler, DeployedInstance } from '../capabilities/DeploymentHandlerRegistry.js';
|
|
1
|
+
import type { DeploymentContext, DeploymentHandler, DeployedInstance } from '../capabilities/DeploymentHandlerRegistry.js';
|
|
3
2
|
/**
|
|
4
|
-
* Deployment handler for AgentSkillDeployment
|
|
3
|
+
* Deployment handler for the `AgentSkillDeployment` DeploymentTarget.
|
|
5
4
|
*
|
|
6
|
-
* Transforms
|
|
7
|
-
* in agent discovery directories based on the
|
|
5
|
+
* Transforms `kanonak.org/agent-skills/Skill` instances into SKILL.md
|
|
6
|
+
* files and places them in agent discovery directories based on the
|
|
7
|
+
* client and scope options. All resolution (license → spdxId, tools →
|
|
8
|
+
* label) goes through the SDK object model.
|
|
8
9
|
*/
|
|
9
10
|
export declare class SkillDeploymentHandler implements DeploymentHandler {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
deploy(doc: KanonakDocument, instances: InstanceSearchResult[], options: Record<string, string>): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
* Remove a deployed skill by name.
|
|
16
|
-
*/
|
|
11
|
+
private readonly parser;
|
|
12
|
+
private readonly objectParser;
|
|
13
|
+
deploy(context: DeploymentContext): Promise<void>;
|
|
17
14
|
undeploy(name: string, options: Record<string, string>): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* List all deployed skills in the discovery directory.
|
|
20
|
-
*/
|
|
21
15
|
list(options: Record<string, string>): Promise<DeployedInstance[]>;
|
|
22
16
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { KanonakParser, KanonakObjectParser, SubjectKanonak, Kanonak } from '@kanonak-protocol/sdk';
|
|
2
|
+
import type { IKanonakDocumentRepository } from '@kanonak-protocol/sdk';
|
|
2
3
|
/**
|
|
3
|
-
* A skill transformed from Kanonak
|
|
4
|
+
* A skill transformed from Kanonak into SKILL.md content.
|
|
4
5
|
*/
|
|
5
6
|
export interface TransformedSkill {
|
|
6
7
|
name: string;
|
|
@@ -8,13 +9,11 @@ export interface TransformedSkill {
|
|
|
8
9
|
skillMd: string;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
|
-
* Transform
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @param doc - A parsed KanonakDocument (from KanonakParser.parse())
|
|
18
|
-
* @returns Array of transformed skills with their SKILL.md content
|
|
12
|
+
* Transform every `Skill` instance in a parsed kanonak list into
|
|
13
|
+
* SKILL.md content. References (`license`, `allowedTools`) are
|
|
14
|
+
* followed through the broader repository so the generated
|
|
15
|
+
* frontmatter uses canonical values from the target packages —
|
|
16
|
+
* e.g. `Apache-2.0` (the `License.spdxId`) rather than the local
|
|
17
|
+
* entity name `Apache-2-0`.
|
|
19
18
|
*/
|
|
20
|
-
export declare function
|
|
19
|
+
export declare function transformSkillsToSkillMd(allKanonaks: Kanonak[], skills: SubjectKanonak[], repository: IKanonakDocumentRepository, parser: KanonakParser, objectParser: KanonakObjectParser): Promise<TransformedSkill[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanonak-protocol/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Kanonak Protocol CLI - Validate and resolve Kanonak ontology packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"semantic-web"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@kanonak-protocol/sdk": "^1.
|
|
38
|
-
"@kanonak-protocol/types": "^1.
|
|
37
|
+
"@kanonak-protocol/sdk": "^1.4.0",
|
|
38
|
+
"@kanonak-protocol/types": "^1.4.0",
|
|
39
39
|
"commander": "^13.0.0",
|
|
40
40
|
"js-yaml": "^4.1.1"
|
|
41
41
|
},
|