@almyty/skills 1.3.0 → 1.5.1
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/client.d.ts +7 -0
- package/dist/client.js +12 -1
- package/dist/index.js +2 -2
- package/dist/installer.d.ts +12 -0
- package/dist/installer.js +35 -3
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -7,7 +7,14 @@ export interface GatewayInfo {
|
|
|
7
7
|
id: string;
|
|
8
8
|
name: string;
|
|
9
9
|
type: string;
|
|
10
|
+
/** The gateway's address, e.g. /petstore; the install ref resolves by it first. */
|
|
11
|
+
endpoint?: string;
|
|
10
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* The gateway segment of an install ref: the address slug, else the name
|
|
15
|
+
* as a clean slug (a-z0-9, single dashes), the two forms the server resolves.
|
|
16
|
+
*/
|
|
17
|
+
export declare function gatewayRefSlug(gw: Pick<GatewayInfo, 'name' | 'endpoint'>): string;
|
|
11
18
|
export interface ParsedRef {
|
|
12
19
|
type: 'gateway' | 'skill' | 'search' | 'uuid';
|
|
13
20
|
orgSlug?: string;
|
package/dist/client.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gateway segment of an install ref: the address slug, else the name
|
|
3
|
+
* as a clean slug (a-z0-9, single dashes), the two forms the server resolves.
|
|
4
|
+
*/
|
|
5
|
+
export function gatewayRefSlug(gw) {
|
|
6
|
+
const fromEndpoint = (gw.endpoint ?? '').replace(/^\/+/, '');
|
|
7
|
+
if (fromEndpoint)
|
|
8
|
+
return fromEndpoint;
|
|
9
|
+
return (gw.name ?? '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|(?<!-)-+$/g, '');
|
|
10
|
+
}
|
|
1
11
|
export function parseRef(ref) {
|
|
2
12
|
if (!ref)
|
|
3
13
|
return { type: 'search', raw: ref };
|
|
@@ -91,6 +101,7 @@ export class AlmytyClient {
|
|
|
91
101
|
id: gw.id,
|
|
92
102
|
name: gw.name,
|
|
93
103
|
type: gw.type,
|
|
104
|
+
endpoint: gw.endpoint ?? undefined,
|
|
94
105
|
}));
|
|
95
106
|
}
|
|
96
107
|
async searchSkills(query) {
|
|
@@ -173,7 +184,7 @@ export class AlmytyClient {
|
|
|
173
184
|
}
|
|
174
185
|
if (trimmed.length > room)
|
|
175
186
|
trimmed = trimmed.slice(-room);
|
|
176
|
-
return `${gw}-${trimmed.replace(
|
|
187
|
+
return `${gw}-${trimmed.replace(/^-+|(?<!-)-+$/g, '')}`;
|
|
177
188
|
};
|
|
178
189
|
const match = allTools.find((gt) => {
|
|
179
190
|
const t = gt.tool || gt;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolveCredentials } from './auth.js';
|
|
3
|
-
import { AlmytyClient, parseRef } from './client.js';
|
|
3
|
+
import { AlmytyClient, gatewayRefSlug, parseRef } from './client.js';
|
|
4
4
|
import { getAllTargets } from './agents.js';
|
|
5
5
|
import { installSkills, removeSkills, listInstalledSkills } from './installer.js';
|
|
6
6
|
import { loadConfig, resolveTargets } from './config.js';
|
|
@@ -101,7 +101,7 @@ async function main() {
|
|
|
101
101
|
}
|
|
102
102
|
console.log('\nYour gateways:\n');
|
|
103
103
|
for (const gw of gateways) {
|
|
104
|
-
const slug = gw
|
|
104
|
+
const slug = gatewayRefSlug(gw);
|
|
105
105
|
console.log(` ${gw.name}`);
|
|
106
106
|
console.log(` Type: ${gw.type}`);
|
|
107
107
|
console.log(` Use: npx @almyty/skills install <org>/${slug}`);
|
package/dist/installer.d.ts
CHANGED
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { SkillFile } from './client.js';
|
|
13
13
|
import type { AgentTarget } from './agents.js';
|
|
14
|
+
/** Marker line we expect inside every SKILL.md frontmatter we wrote.
|
|
15
|
+
* The leading run stops at a line break: as `^\s*` in a multiline regex
|
|
16
|
+
* every line start rescanned all the blank lines after it, and 100 KB of
|
|
17
|
+
* them in a SKILL.md (its content is the gateway's) took seven seconds.
|
|
18
|
+
* A match that crossed blank lines also matches from the last of them. */
|
|
19
|
+
export declare const ALMYTY_MARKER: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* A SKILL.md is a page of markdown; anything bigger than this is not a
|
|
22
|
+
* skill the backend generated and is refused rather than written into an
|
|
23
|
+
* editor's config.
|
|
24
|
+
*/
|
|
25
|
+
export declare const MAX_SKILL_BYTES: number;
|
|
14
26
|
export interface InstallResult {
|
|
15
27
|
agent: string;
|
|
16
28
|
skillsDir: string;
|
package/dist/installer.js
CHANGED
|
@@ -9,13 +9,17 @@
|
|
|
9
9
|
* and `installed` now reads the `metadata.author: almyty` line
|
|
10
10
|
* already present in every SKILL.md we generate.
|
|
11
11
|
*/
|
|
12
|
-
import { writeFileSync, mkdirSync, existsSync, readdirSync, rmSync, readFileSync } from 'fs';
|
|
12
|
+
import { writeFileSync, mkdirSync, existsSync, readdirSync, rmSync, readFileSync, lstatSync } from 'fs';
|
|
13
13
|
import { join, resolve, sep } from 'path';
|
|
14
14
|
/** Legacy prefix from <=v1.0.9 — older installs still use this dir
|
|
15
15
|
* name. Kept here so `remove`/`installed` continue to find them. */
|
|
16
16
|
const LEGACY_SKILL_PREFIX = 'almyty-';
|
|
17
|
-
/** Marker line we expect inside every SKILL.md frontmatter we wrote.
|
|
18
|
-
|
|
17
|
+
/** Marker line we expect inside every SKILL.md frontmatter we wrote.
|
|
18
|
+
* The leading run stops at a line break: as `^\s*` in a multiline regex
|
|
19
|
+
* every line start rescanned all the blank lines after it, and 100 KB of
|
|
20
|
+
* them in a SKILL.md (its content is the gateway's) took seven seconds.
|
|
21
|
+
* A match that crossed blank lines also matches from the last of them. */
|
|
22
|
+
export const ALMYTY_MARKER = /^[^\S\n\r\u2028\u2029]*author:\s*almyty\s*$/m;
|
|
19
23
|
/**
|
|
20
24
|
* Skill names come from the backend/gateway and are used to build
|
|
21
25
|
* filesystem paths under skillsDir. A malicious or compromised gateway
|
|
@@ -40,6 +44,26 @@ function isInside(baseDir, candidate) {
|
|
|
40
44
|
const target = resolve(candidate);
|
|
41
45
|
return target === base || target.startsWith(base + sep);
|
|
42
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* A SKILL.md is a page of markdown; anything bigger than this is not a
|
|
49
|
+
* skill the backend generated and is refused rather than written into an
|
|
50
|
+
* editor's config.
|
|
51
|
+
*/
|
|
52
|
+
export const MAX_SKILL_BYTES = 1024 * 1024;
|
|
53
|
+
/**
|
|
54
|
+
* True when the path exists and is a symbolic link. The name check and
|
|
55
|
+
* `isInside` work on the path string; a link already sitting at
|
|
56
|
+
* `<skillsDir>/<name>` (or at its SKILL.md) would still send the write
|
|
57
|
+
* somewhere else, so links are refused rather than followed.
|
|
58
|
+
*/
|
|
59
|
+
function isSymlink(path) {
|
|
60
|
+
try {
|
|
61
|
+
return lstatSync(path).isSymbolicLink();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
43
67
|
/**
|
|
44
68
|
* Strip the legacy `name: almyty-<x>` line in the SKILL.md frontmatter
|
|
45
69
|
* to `name: <x>` so the agent-visible identifier is the skill's own
|
|
@@ -100,6 +124,14 @@ export function installSkills(skills, target, options = {}) {
|
|
|
100
124
|
console.warn(`Skipping skill whose path escapes the skills directory: ${skill.name}`);
|
|
101
125
|
continue;
|
|
102
126
|
}
|
|
127
|
+
if (isSymlink(skillDir) || isSymlink(skillFile)) {
|
|
128
|
+
console.warn(`Skipping skill whose path is a symbolic link: ${skill.name}`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (typeof skill.content !== 'string' || Buffer.byteLength(skill.content, 'utf-8') > MAX_SKILL_BYTES) {
|
|
132
|
+
console.warn(`Skipping skill with missing or oversized content: ${skill.name}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
103
135
|
if (existsSync(skillFile))
|
|
104
136
|
overwritten++;
|
|
105
137
|
if (dryRun) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almyty/skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"author": "almyty",
|
|
36
36
|
"license": "Apache-2.0",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@almyty/client": "^1.
|
|
38
|
+
"@almyty/client": "^1.5.0",
|
|
39
39
|
"@clack/prompts": "^1.2.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|