@hasna/skills 0.5.10 → 0.6.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.
Files changed (50) hide show
  1. package/README.md +95 -53
  2. package/bin/index.js +3467 -1460
  3. package/bin/mcp.js +2638 -1713
  4. package/bin/migrate.js +110 -7
  5. package/bin/server.js +5487 -216
  6. package/bin/worker.js +372 -17
  7. package/dist/admin-contract.d.ts +13 -13
  8. package/dist/admin-contract.js +13 -13
  9. package/dist/cli/commands/agent-integration.d.ts +2 -0
  10. package/dist/cli/commands/context.d.ts +19 -0
  11. package/dist/cli/commands/install.d.ts +1 -0
  12. package/dist/cli/commands/profile-sync.d.ts +10 -0
  13. package/dist/cli/commands/profiles.d.ts +2 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.js +1273 -331
  16. package/dist/lib/agent-integration.d.ts +74 -0
  17. package/dist/lib/cloud-executions.d.ts +39 -0
  18. package/dist/lib/managed-policy.d.ts +6 -0
  19. package/dist/lib/profile-admin.d.ts +4 -0
  20. package/dist/lib/profile-client.d.ts +19 -0
  21. package/dist/lib/read-access.d.ts +3 -23
  22. package/dist/lib/selected-document.d.ts +6 -0
  23. package/dist/lib/selected-run.d.ts +31 -0
  24. package/dist/lib/selection-cache.d.ts +40 -0
  25. package/dist/lib/selection-resolver.d.ts +75 -0
  26. package/dist/lib/skill-context.d.ts +43 -0
  27. package/dist/lib/skillinfo.d.ts +2 -0
  28. package/dist/sdk/execution/dispatchers/ecs.d.ts +8 -0
  29. package/dist/sdk/index.d.ts +2 -0
  30. package/dist/sdk/index.js +9791 -8152
  31. package/dist/server/app.d.ts +7 -1
  32. package/dist/server/artifact-storage.d.ts +7 -10
  33. package/dist/server/auth.d.ts +2 -0
  34. package/dist/server/config.d.ts +2 -0
  35. package/dist/server/profile-api.d.ts +3 -0
  36. package/dist/server/runtime-api.d.ts +31 -0
  37. package/dist/server/runtime-policy.d.ts +25 -0
  38. package/dist/server/runtime-store.d.ts +73 -0
  39. package/dist/server/runtime-worker.d.ts +19 -0
  40. package/dist/server/selection-store.d.ts +39 -0
  41. package/dist/server/sqlite-store.d.ts +2 -0
  42. package/dist/server/store.d.ts +3 -0
  43. package/dist/server/types.d.ts +3 -0
  44. package/dist/storage.js +3 -0
  45. package/dist/types/skill-selection.d.ts +41 -0
  46. package/migrations/postgres/0007_skill_profiles.sql +19 -0
  47. package/migrations/postgres/0008_skill_runtime.sql +14 -0
  48. package/migrations/sqlite/0007_skill_profiles.sql +19 -0
  49. package/migrations/sqlite/0008_skill_runtime.sql +14 -0
  50. package/package.json +3 -2
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join6 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.5.10",
10
+ version: "0.6.0",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
@@ -123,7 +123,8 @@ var package_default = {
123
123
  },
124
124
  repository: {
125
125
  type: "git",
126
- url: "git+https://github.com/hasna/apps.git"
126
+ url: "https://github.com/hasna/apps.git",
127
+ directory: "apps/skills"
127
128
  },
128
129
  homepage: "https://github.com/hasna/skills",
129
130
  bugs: {
@@ -190,6 +191,7 @@ function resolveServerConfig(env = process.env) {
190
191
  seedBundledCorpus: (env.HASNA_SKILLS_SEED_BUNDLED_CORPUS ?? "1") !== "0",
191
192
  artifactBucket: env.HASNA_SKILLS_S3_BUCKET || env.SKILLS_S3_BUCKET || undefined,
192
193
  artifactPrefix: normalizePrefix(env.HASNA_SKILLS_S3_PREFIX || env.SKILLS_S3_PREFIX || "skills/artifacts"),
194
+ runArtifactPrefix: env.HASNA_SKILLS_S3_RUN_PREFIX ? normalizePrefix(env.HASNA_SKILLS_S3_RUN_PREFIX) : undefined,
193
195
  inlineWorker: env.HASNA_SKILLS_INLINE_WORKER === "1",
194
196
  bundleSigningKey: env.HASNA_SKILLS_API_SIGNING_KEY || env.HASNA_SKILLS_SIGNING_KEY || undefined,
195
197
  requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
@@ -438,7 +440,105 @@ function defaultStartDirs() {
438
440
 
439
441
  // src/server/sqlite-store.ts
440
442
  import { Database } from "bun:sqlite";
441
- import { randomUUID as randomUUID2 } from "crypto";
443
+
444
+ // src/server/selection-store.ts
445
+ import { randomUUID } from "crypto";
446
+ function selections(value) {
447
+ return typeof value === "string" ? JSON.parse(value) : structuredClone(value);
448
+ }
449
+ function time(value) {
450
+ return value instanceof Date ? value.toISOString() : String(value);
451
+ }
452
+ function profile(row) {
453
+ return {
454
+ id: String(row.profile_id),
455
+ workspaceId: String(row.org_id),
456
+ revision: String(row.revision),
457
+ selections: selections(row.selections_json),
458
+ updatedAt: time(row.updated_at)
459
+ };
460
+ }
461
+ function state(row) {
462
+ return {
463
+ stationId: String(row.station_id),
464
+ workspaceId: String(row.org_id),
465
+ actorId: String(row.actor_id),
466
+ profileId: String(row.profile_id),
467
+ profileRevision: String(row.profile_revision),
468
+ selections: selections(row.selections_json),
469
+ appliedAt: time(row.applied_at)
470
+ };
471
+ }
472
+
473
+ class MemorySkillSelectionStore {
474
+ profiles = new Map;
475
+ states = new Map;
476
+ key(...values) {
477
+ return JSON.stringify(values);
478
+ }
479
+ async getProfile(p, id) {
480
+ return structuredClone(this.profiles.get(this.key(p.orgId, id)) ?? null);
481
+ }
482
+ async saveProfile(p, id, selected, expected) {
483
+ const key = this.key(p.orgId, id), previous = this.profiles.get(key);
484
+ if ((previous?.revision ?? null) !== expected)
485
+ return null;
486
+ const next = {
487
+ id,
488
+ workspaceId: p.orgId,
489
+ revision: randomUUID(),
490
+ selections: structuredClone(selected),
491
+ updatedAt: new Date().toISOString()
492
+ };
493
+ this.profiles.set(key, next);
494
+ return structuredClone(next);
495
+ }
496
+ async getStationState(p, id) {
497
+ return structuredClone(this.states.get(this.key(p.orgId, p.userId, id)) ?? null);
498
+ }
499
+ async saveStationState(p, id, input) {
500
+ if (this.profiles.get(this.key(p.orgId, input.profileId))?.revision !== input.profileRevision)
501
+ return null;
502
+ const next = {
503
+ ...structuredClone(input),
504
+ stationId: id,
505
+ workspaceId: p.orgId,
506
+ actorId: p.userId,
507
+ appliedAt: new Date().toISOString()
508
+ };
509
+ this.states.set(this.key(p.orgId, p.userId, id), next);
510
+ return structuredClone(next);
511
+ }
512
+ }
513
+
514
+ class SqliteSkillSelectionStore {
515
+ db;
516
+ constructor(db) {
517
+ this.db = db;
518
+ }
519
+ async getProfile(p, id) {
520
+ const row = this.db.query("SELECT * FROM skills_profiles WHERE org_id=? AND profile_id=?").get(p.orgId, id);
521
+ return row ? profile(row) : null;
522
+ }
523
+ async saveProfile(p, id, selected, expected) {
524
+ const revision = randomUUID(), timestamp = new Date().toISOString();
525
+ const row = expected === null ? this.db.query("INSERT INTO skills_profiles(org_id,profile_id,revision,selections_json,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *").get(p.orgId, id, revision, JSON.stringify(selected), timestamp) : this.db.query("UPDATE skills_profiles SET revision=?,selections_json=?,updated_at=? WHERE org_id=? AND profile_id=? AND revision=? RETURNING *").get(revision, JSON.stringify(selected), timestamp, p.orgId, id, expected);
526
+ return row ? profile(row) : null;
527
+ }
528
+ async getStationState(p, id) {
529
+ const row = this.db.query("SELECT * FROM skills_station_state WHERE org_id=? AND actor_id=? AND station_id=?").get(p.orgId, p.userId, id);
530
+ return row ? state(row) : null;
531
+ }
532
+ async saveStationState(p, id, input) {
533
+ const row = this.db.query(`INSERT INTO skills_station_state(org_id,actor_id,station_id,profile_id,profile_revision,selections_json,applied_at)
534
+ SELECT ?,?,?,?,?,?,? WHERE EXISTS(SELECT 1 FROM skills_profiles WHERE org_id=? AND profile_id=? AND revision=?)
535
+ ON CONFLICT(org_id,actor_id,station_id) DO UPDATE SET profile_id=excluded.profile_id,profile_revision=excluded.profile_revision,selections_json=excluded.selections_json,applied_at=excluded.applied_at RETURNING *`).get(p.orgId, p.userId, id, input.profileId, input.profileRevision, JSON.stringify(input.selections), new Date().toISOString(), p.orgId, input.profileId, input.profileRevision);
536
+ return row ? state(row) : null;
537
+ }
538
+ }
539
+
540
+ // src/server/sqlite-store.ts
541
+ import { randomUUID as randomUUID3 } from "crypto";
442
542
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
443
543
  import { dirname as dirname3, join as join5 } from "path";
444
544
 
@@ -456,12 +556,12 @@ function publicPrincipal(partial = {}) {
456
556
  userId: partial.userId || "user_dev",
457
557
  email: partial.email || "dev@example.com",
458
558
  role: partial.role || "owner",
459
- scopes: partial.scopes || ["skills:read", "runs:write"]
559
+ scopes: partial.scopes ?? ["*"]
460
560
  };
461
561
  }
462
562
 
463
563
  // src/server/rows.ts
464
- import { randomUUID } from "crypto";
564
+ import { randomUUID as randomUUID2 } from "crypto";
465
565
 
466
566
  // src/lib/skill-entry-path.ts
467
567
  class SkillEntryPaths {
@@ -771,7 +871,7 @@ function normalizeLimit(limit) {
771
871
  return Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : 0;
772
872
  }
773
873
  function runId() {
774
- return `run_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
874
+ return `run_${Date.now().toString(36)}_${randomUUID2().slice(0, 8)}`;
775
875
  }
776
876
  function rowToRun(row) {
777
877
  return {
@@ -991,6 +1091,9 @@ var NO_REVISION_SENTINEL = "0000000000000000000000000000000000000000000000000000
991
1091
  var LAST_USED_RESOLUTION_MS = 60000;
992
1092
 
993
1093
  class SqliteSkillsStore {
1094
+ get selectionStore() {
1095
+ return new SqliteSkillSelectionStore(this.db);
1096
+ }
994
1097
  backend;
995
1098
  db;
996
1099
  closed = false;
@@ -1092,7 +1195,7 @@ class SqliteSkillsStore {
1092
1195
  JSON.stringify(input.input),
1093
1196
  JSON.stringify(input.args),
1094
1197
  input.idempotencyKey ?? null,
1095
- randomUUID2(),
1198
+ randomUUID3(),
1096
1199
  nowIso()
1097
1200
  ]);
1098
1201
  return rowToRun(row);