ali-skills 0.0.2 → 0.0.3

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/add.js CHANGED
@@ -3,7 +3,7 @@ import pc from 'picocolors';
3
3
  import { existsSync } from 'fs';
4
4
  import { homedir } from 'os';
5
5
  import { sep } from 'path';
6
- import { parseSource, getOwnerRepo, parseOwnerRepo, isRepoPrivate } from './source-parser.js';
6
+ import { parseSource, getOwnerRepo, parseOwnerRepo, isRepoPrivate, isInternalGitUrl, } from './source-parser.js';
7
7
  import { searchMultiselect, cancelSymbol } from './prompts/search-multiselect.js';
8
8
  // Helper to check if a value is a cancel symbol (works with both clack and our custom prompts)
9
9
  const isCancelled = (value) => typeof value === 'symbol';
@@ -27,7 +27,6 @@ import { track, setVersion, fetchAuditData, } from './telemetry.js';
27
27
  import { wellKnownProvider } from './providers/index.js';
28
28
  import { addSkillToLock, fetchSkillFolderHash, getGitHubToken, isPromptDismissed, dismissPrompt, getLastSelectedAgents, saveSelectedAgents, } from './skill-lock.js';
29
29
  import { addSkillToLocalLock, computeSkillFolderHash } from './local-lock.js';
30
- import packageJson from '../package.json' with { type: 'json' };
31
30
  export function initTelemetry(version) {
32
31
  setVersion(version);
33
32
  }
@@ -308,8 +307,6 @@ async function selectAgentsInteractive(options) {
308
307
  }
309
308
  return selected;
310
309
  }
311
- const version = packageJson.version;
312
- setVersion(version);
313
310
  /**
314
311
  * Handle skills from a well-known endpoint (RFC 8615).
315
312
  * Discovers skills from /.well-known/skills/index.json
@@ -1172,15 +1169,45 @@ export async function runAdd(args, options = {}) {
1172
1169
  // breaking restore for private repos that require SSH authentication.
1173
1170
  const isSSH = parsed.url.startsWith('git@');
1174
1171
  const lockSource = isSSH ? parsed.url : normalizedSource;
1172
+ const isInternalSource = isInternalGitUrl(parsed.url);
1173
+ const telemetrySourceType = isInternalSource ? 'internal-git' : parsed.type;
1175
1174
  // Only track if we have a valid remote source and it's not a private repo
1176
1175
  if (normalizedSource) {
1177
- const ownerRepo = parseOwnerRepo(normalizedSource);
1178
- if (ownerRepo) {
1179
- // Check if repo is private - skip telemetry for private repos
1180
- const isPrivate = await isRepoPrivate(ownerRepo.owner, ownerRepo.repo);
1181
- // Only send telemetry if repo is public (isPrivate === false)
1182
- // If we can't determine (null), err on the side of caution and skip telemetry
1183
- if (isPrivate === false) {
1176
+ if (isInternalSource) {
1177
+ // Internal repositories: do not send to public skills telemetry.
1178
+ // The telemetry router in telemetry.ts will route sourceType=internal-* to
1179
+ // the internal placeholder hook.
1180
+ track({
1181
+ event: 'install',
1182
+ source: normalizedSource,
1183
+ skills: selectedSkills.map((s) => s.name).join(','),
1184
+ agents: targetAgents.join(','),
1185
+ ...(installGlobally && { global: '1' }),
1186
+ skillFiles: JSON.stringify(skillFiles),
1187
+ sourceType: telemetrySourceType,
1188
+ });
1189
+ }
1190
+ else {
1191
+ const ownerRepo = parseOwnerRepo(normalizedSource);
1192
+ if (ownerRepo) {
1193
+ // Check if repo is private - skip telemetry for private repos
1194
+ const isPrivate = await isRepoPrivate(ownerRepo.owner, ownerRepo.repo);
1195
+ // Only send telemetry if repo is public (isPrivate === false)
1196
+ // If we can't determine (null), err on the side of caution and skip telemetry
1197
+ if (isPrivate === false) {
1198
+ track({
1199
+ event: 'install',
1200
+ source: normalizedSource,
1201
+ skills: selectedSkills.map((s) => s.name).join(','),
1202
+ agents: targetAgents.join(','),
1203
+ ...(installGlobally && { global: '1' }),
1204
+ skillFiles: JSON.stringify(skillFiles),
1205
+ sourceType: telemetrySourceType,
1206
+ });
1207
+ }
1208
+ }
1209
+ else {
1210
+ // If we can't parse owner/repo, still send telemetry (for non-GitHub sources)
1184
1211
  track({
1185
1212
  event: 'install',
1186
1213
  source: normalizedSource,
@@ -1188,20 +1215,10 @@ export async function runAdd(args, options = {}) {
1188
1215
  agents: targetAgents.join(','),
1189
1216
  ...(installGlobally && { global: '1' }),
1190
1217
  skillFiles: JSON.stringify(skillFiles),
1218
+ sourceType: telemetrySourceType,
1191
1219
  });
1192
1220
  }
1193
1221
  }
1194
- else {
1195
- // If we can't parse owner/repo, still send telemetry (for non-GitHub sources)
1196
- track({
1197
- event: 'install',
1198
- source: normalizedSource,
1199
- skills: selectedSkills.map((s) => s.name).join(','),
1200
- agents: targetAgents.join(','),
1201
- ...(installGlobally && { global: '1' }),
1202
- skillFiles: JSON.stringify(skillFiles),
1203
- });
1204
- }
1205
1222
  }
1206
1223
  // Add to skill lock file for update tracking (only for global installs)
1207
1224
  if (successful.length > 0 && installGlobally && normalizedSource) {
@@ -1,4 +1,31 @@
1
1
  import { isAbsolute, resolve } from 'path';
2
+ const INTERNAL_GIT_HOSTS = new Set(['code.alibaba-inc.com', 'gitlab.alibaba-inc.com']);
3
+ const INTERNAL_GIT_CANONICAL_HOST = 'gitlab.alibaba-inc.com';
4
+ const INTERNAL_GIT_CANONICAL_PROTOCOL = 'http';
5
+ function toInternalCanonicalGitUrl(repoPath) {
6
+ const cleanPath = repoPath.replace(/^\/+/, '').replace(/\.git$/, '').replace(/\/+$/, '');
7
+ return `${INTERNAL_GIT_CANONICAL_PROTOCOL}://${INTERNAL_GIT_CANONICAL_HOST}/${cleanPath}.git`;
8
+ }
9
+ export function isInternalGitHost(hostname) {
10
+ return INTERNAL_GIT_HOSTS.has(hostname.toLowerCase());
11
+ }
12
+ export function isInternalGitUrl(input) {
13
+ // SSH style: git@code.alibaba-inc.com:group/repo.git
14
+ const sshMatch = input.match(/^git@([^:]+):.+$/);
15
+ if (sshMatch && isInternalGitHost(sshMatch[1])) {
16
+ return true;
17
+ }
18
+ if (!input.startsWith('http://') && !input.startsWith('https://')) {
19
+ return false;
20
+ }
21
+ try {
22
+ const parsed = new URL(input);
23
+ return isInternalGitHost(parsed.hostname);
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
2
29
  /**
3
30
  * Extract owner/repo (or group/subgroup/repo for GitLab) from a parsed source
4
31
  * for lockfile tracking and telemetry.
@@ -173,9 +200,12 @@ export function parseSource(input) {
173
200
  if (gitlabTreeWithPathMatch) {
174
201
  const [, protocol, hostname, repoPath, ref, subpath] = gitlabTreeWithPathMatch;
175
202
  if (hostname !== 'github.com' && repoPath) {
203
+ const normalizedUrl = isInternalGitHost(hostname)
204
+ ? toInternalCanonicalGitUrl(repoPath)
205
+ : `${protocol}://${hostname}/${repoPath.replace(/\.git$/, '')}.git`;
176
206
  return {
177
207
  type: 'gitlab',
178
- url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, '')}.git`,
208
+ url: normalizedUrl,
179
209
  ref,
180
210
  subpath: subpath ? sanitizeSubpath(subpath) : subpath,
181
211
  };
@@ -186,9 +216,12 @@ export function parseSource(input) {
186
216
  if (gitlabTreeMatch) {
187
217
  const [, protocol, hostname, repoPath, ref] = gitlabTreeMatch;
188
218
  if (hostname !== 'github.com' && repoPath) {
219
+ const normalizedUrl = isInternalGitHost(hostname)
220
+ ? toInternalCanonicalGitUrl(repoPath)
221
+ : `${protocol}://${hostname}/${repoPath.replace(/\.git$/, '')}.git`;
189
222
  return {
190
223
  type: 'gitlab',
191
- url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, '')}.git`,
224
+ url: normalizedUrl,
192
225
  ref,
193
226
  };
194
227
  }
@@ -207,6 +240,21 @@ export function parseSource(input) {
207
240
  };
208
241
  }
209
242
  }
243
+ // Internal Git host URL (e.g., https://code.alibaba-inc.com/group/repo/)
244
+ // Treat as GitLab-style repository URL for clone/discovery compatibility.
245
+ const internalGitRepoMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
246
+ if (internalGitRepoMatch) {
247
+ const [, _protocol, hostname, repoPath] = internalGitRepoMatch;
248
+ if (repoPath && isInternalGitHost(hostname)) {
249
+ // Must have at least owner/repo (one slash)
250
+ if (repoPath.includes('/')) {
251
+ return {
252
+ type: 'gitlab',
253
+ url: toInternalCanonicalGitUrl(repoPath),
254
+ };
255
+ }
256
+ }
257
+ }
210
258
  // GitHub shorthand: owner/repo, owner/repo/path/to/skill, or owner/repo@skill-name
211
259
  // Exclude paths that start with . or / to avoid matching local paths
212
260
  // First check for @skill syntax: owner/repo@skill-name
@@ -258,6 +306,9 @@ function isWellKnownUrl(input) {
258
306
  if (excludedHosts.includes(parsed.hostname)) {
259
307
  return false;
260
308
  }
309
+ if (isInternalGitHost(parsed.hostname)) {
310
+ return false;
311
+ }
261
312
  // Don't match URLs that look like git repos (should be handled by git type)
262
313
  if (input.endsWith('.git')) {
263
314
  return false;
package/dist/telemetry.js CHANGED
@@ -1,6 +1,21 @@
1
1
  const TELEMETRY_URL = 'https://add-skill.vercel.sh/t';
2
2
  const AUDIT_URL = 'https://add-skill.vercel.sh/audit';
3
+ const INTERNAL_TELEMETRY_SOURCE_PREFIX = 'internal-';
3
4
  let cliVersion = null;
5
+ function shouldRouteToInternalTelemetry(data) {
6
+ // Internal telemetry routing hook:
7
+ // Any event tagged with sourceType starting with "internal-" is considered
8
+ // an intranet source and must not be sent to public skills telemetry.
9
+ return 'sourceType' in data && typeof data.sourceType === 'string'
10
+ ? data.sourceType.startsWith(INTERNAL_TELEMETRY_SOURCE_PREFIX)
11
+ : false;
12
+ }
13
+ function trackInternalPlaceholder(_data) {
14
+ // TODO(internal-telemetry):
15
+ // Implement intranet telemetry reporting endpoint here.
16
+ // Keep this as no-op for now so internal repository installs are not reported
17
+ // to public skills.sh telemetry.
18
+ }
4
19
  function isCI() {
5
20
  return !!(process.env.CI ||
6
21
  process.env.GITHUB_ACTIONS ||
@@ -46,6 +61,10 @@ export async function fetchAuditData(source, skillSlugs, timeoutMs = 3000) {
46
61
  export function track(data) {
47
62
  if (!isEnabled())
48
63
  return;
64
+ if (shouldRouteToInternalTelemetry(data)) {
65
+ trackInternalPlaceholder(data);
66
+ return;
67
+ }
49
68
  try {
50
69
  const params = new URLSearchParams();
51
70
  // Add version
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "ali-skills",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "ali-skills": "./bin/cli.mjs",
8
- "skills": "./bin/cli.mjs",
9
- "add-skill": "./bin/cli.mjs"
8
+ "skills": "./bin/cli.mjs"
10
9
  },
11
10
  "files": [
12
11
  "dist",