yourskills 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/main.js +126 -48
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -611,6 +611,7 @@ async function localAccount(server) {
611
611
  return {
612
612
  signedIn: Boolean(config.token),
613
613
  email: config.userEmail ?? null,
614
+ organization: config.orgName ?? null,
614
615
  server: serverUrl(config, server)
615
616
  };
616
617
  }
@@ -5753,7 +5754,7 @@ import { realpathSync } from "node:fs";
5753
5754
  // package.json
5754
5755
  var package_default = {
5755
5756
  name: "yourskills",
5756
- version: "0.3.0",
5757
+ version: "0.3.1",
5757
5758
  description: "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
5758
5759
  license: "UNLICENSED",
5759
5760
  homepage: "https://yourskills.store",
@@ -6198,8 +6199,41 @@ async function runSessionStart() {
6198
6199
  const outcome = await sessionStart();
6199
6200
  emit(outcome, await upgradeCheck());
6200
6201
  }
6201
- // src/org/org.ts
6202
+ // src/org/wire.ts
6202
6203
  var authBase2 = (base) => `${base.replace(/\/$/, "")}/api/auth`;
6204
+ async function activeOrganizationId(base, token) {
6205
+ const res = await fetch(`${authBase2(base)}/get-session`, {
6206
+ headers: { authorization: `Bearer ${token}` }
6207
+ });
6208
+ if (!res.ok)
6209
+ return null;
6210
+ const body = await res.json();
6211
+ return body?.session?.activeOrganizationId ?? null;
6212
+ }
6213
+ async function listOrganizations(base, token) {
6214
+ let res;
6215
+ try {
6216
+ res = await fetch(`${authBase2(base)}/organization/list`, {
6217
+ headers: { authorization: `Bearer ${token}` }
6218
+ });
6219
+ } catch (error) {
6220
+ throw new Error(`Could not reach ${base}: ${error instanceof Error ? error.message : String(error)}`);
6221
+ }
6222
+ if (!res.ok) {
6223
+ throw new Error(`Could not list organizations: ${res.status} ${res.statusText}`);
6224
+ }
6225
+ return await res.json();
6226
+ }
6227
+ async function rememberOrganization(name) {
6228
+ try {
6229
+ const config = await readConfig();
6230
+ if ((config.orgName ?? null) === name)
6231
+ return;
6232
+ await writeConfig(name === null ? { ...config, orgName: undefined } : { ...config, orgName: name });
6233
+ } catch {}
6234
+ }
6235
+
6236
+ // src/org/org.ts
6203
6237
  var NO_ACTIVE_ORGANIZATION = "none: choose one with `yourskills org <name>`, or in the web app";
6204
6238
  async function namedOrganization(server) {
6205
6239
  const client = await createClient(server);
@@ -6208,11 +6242,9 @@ async function namedOrganization(server) {
6208
6242
  }
6209
6243
  async function whoamiReport(server) {
6210
6244
  const who = await whoami(server);
6211
- return drawWhoami({
6212
- user: who.user,
6213
- server: who.server,
6214
- organization: who.org ? await namedOrganization(server) : null
6215
- });
6245
+ const organization = who.org ? await namedOrganization(server) : null;
6246
+ await rememberOrganization(organization?.name ?? null);
6247
+ return drawWhoami({ user: who.user, server: who.server, organization });
6216
6248
  }
6217
6249
  function drawWhoami({ user, server, organization }) {
6218
6250
  const label = (text) => text.padEnd(7);
@@ -6230,29 +6262,6 @@ function drawWhoami({ user, server, organization }) {
6230
6262
  json: { user, server, organization }
6231
6263
  };
6232
6264
  }
6233
- async function activeOrganizationId(base, token) {
6234
- const res = await fetch(`${authBase2(base)}/get-session`, {
6235
- headers: { authorization: `Bearer ${token}` }
6236
- });
6237
- if (!res.ok)
6238
- return null;
6239
- const body = await res.json();
6240
- return body?.session?.activeOrganizationId ?? null;
6241
- }
6242
- async function listOrganizations(base, token) {
6243
- let res;
6244
- try {
6245
- res = await fetch(`${authBase2(base)}/organization/list`, {
6246
- headers: { authorization: `Bearer ${token}` }
6247
- });
6248
- } catch (error) {
6249
- throw new Error(`Could not reach ${base}: ${error instanceof Error ? error.message : String(error)}`);
6250
- }
6251
- if (!res.ok) {
6252
- throw new Error(`Could not list organizations: ${res.status} ${res.statusText}`);
6253
- }
6254
- return await res.json();
6255
- }
6256
6265
  function rows(orgs, active) {
6257
6266
  if (orgs.length === 0) {
6258
6267
  return [
@@ -6269,12 +6278,40 @@ function rows(orgs, active) {
6269
6278
  note: org.slug ?? org.id
6270
6279
  }));
6271
6280
  }
6281
+ async function learnActiveOrganization(server) {
6282
+ try {
6283
+ const { token, base } = await requireToken(server);
6284
+ const [orgs, active] = await Promise.all([
6285
+ listOrganizations(base, token),
6286
+ activeOrganizationId(base, token)
6287
+ ]);
6288
+ const name = orgs.find((o) => o.id === active)?.name ?? null;
6289
+ await rememberOrganization(name);
6290
+ return name;
6291
+ } catch {
6292
+ return null;
6293
+ }
6294
+ }
6295
+ async function organizationChoices(server) {
6296
+ const { token, base } = await requireToken(server);
6297
+ const [orgs, active] = await Promise.all([
6298
+ listOrganizations(base, token),
6299
+ activeOrganizationId(base, token)
6300
+ ]);
6301
+ await rememberOrganization(orgs.find((o) => o.id === active)?.name ?? null);
6302
+ return orgs.map((org) => ({
6303
+ name: org.name,
6304
+ slug: org.slug ?? null,
6305
+ active: org.id === active
6306
+ }));
6307
+ }
6272
6308
  async function orgListReport(server) {
6273
6309
  const { token, base } = await requireToken(server);
6274
6310
  const [orgs, active] = await Promise.all([
6275
6311
  listOrganizations(base, token),
6276
6312
  activeOrganizationId(base, token)
6277
6313
  ]);
6314
+ await rememberOrganization(orgs.find((o) => o.id === active)?.name ?? null);
6278
6315
  return {
6279
6316
  title: "org",
6280
6317
  lines: rows(orgs, active),
@@ -6331,6 +6368,7 @@ async function orgSwitchReport(name, server) {
6331
6368
  failed: true
6332
6369
  };
6333
6370
  }
6371
+ await rememberOrganization(match.name);
6334
6372
  return {
6335
6373
  title: "org",
6336
6374
  lines: [{ mark: "ok", text: `Active organization: ${match.name}` }],
@@ -7095,7 +7133,7 @@ var TABLE = {
7095
7133
  label: "switch org",
7096
7134
  hint: "org <name>",
7097
7135
  when: (facts) => facts.signedIn,
7098
- opens: { kind: "org" }
7136
+ says: (facts) => facts.organization ? `switch this machine out of ${facts.organization}` : "choose which organization this machine installs from"
7099
7137
  }
7100
7138
  ],
7101
7139
  invitesHooks: false,
@@ -8056,18 +8094,21 @@ function paletteGroups(facts) {
8056
8094
  // src/shell/facts.ts
8057
8095
  import { existsSync as existsSync7 } from "node:fs";
8058
8096
  async function paletteFacts(scope, server) {
8059
- const [account, nearest, config] = await Promise.all([
8097
+ const [account, nearest, config, agents] = await Promise.all([
8060
8098
  localAccount(server),
8061
8099
  nearestManifest(scope),
8062
- readConfig()
8100
+ readConfig(),
8101
+ resolveAgents()
8063
8102
  ]);
8064
8103
  const skills = Object.keys(nearest.manifest.skills).length;
8065
8104
  return {
8066
8105
  signedIn: account.signedIn,
8067
8106
  email: account.email,
8107
+ organization: account.organization,
8068
8108
  server: account.server,
8069
8109
  firstRun: !account.signedIn && skills === 0 && !config.welcomed,
8070
8110
  agentsOnDisk: HOOK_ADAPTERS.filter((adapter) => existsSync7(adapter.home())).map((adapter) => adapter.agent),
8111
+ agents,
8071
8112
  scope: nearest.scope,
8072
8113
  manifest: nearest.path,
8073
8114
  skills,
@@ -8406,6 +8447,8 @@ function PickScreen({
8406
8447
  rows,
8407
8448
  empty,
8408
8449
  verb,
8450
+ single = false,
8451
+ marked,
8409
8452
  onPick,
8410
8453
  onBack
8411
8454
  }) {
@@ -8439,7 +8482,7 @@ function PickScreen({
8439
8482
  } else if (key.backspace || key.delete) {
8440
8483
  setQuery((q) => q.slice(0, -1));
8441
8484
  setCursor(0);
8442
- } else if (input === " ") {
8485
+ } else if (input === " " && !single) {
8443
8486
  const row = visible[cursor];
8444
8487
  if (!row)
8445
8488
  return;
@@ -8490,21 +8533,21 @@ function PickScreen({
8490
8533
  names,
8491
8534
  selectable: true,
8492
8535
  cursor: i === cursor,
8493
- selected: picked.has(row.name)
8536
+ selected: single ? row.name === marked : picked.has(row.name)
8494
8537
  }, row.name)),
8495
8538
  /* @__PURE__ */ jsx16(Divider, {
8496
8539
  width
8497
8540
  }),
8498
8541
  /* @__PURE__ */ jsxs14(Box12, {
8499
8542
  children: [
8500
- picked.size > 0 ? /* @__PURE__ */ jsx16(Text14, {
8543
+ picked.size > 0 && !single ? /* @__PURE__ */ jsx16(Text14, {
8501
8544
  color: tone.notice,
8502
8545
  children: `${mark.selected} ${picked.size} marked `
8503
8546
  }) : null,
8504
8547
  /* @__PURE__ */ jsx16(Hints, {
8505
8548
  hints: [
8506
8549
  ["^v", "move"],
8507
- ["space", "mark"],
8550
+ ...single ? [] : [["space", "mark"]],
8508
8551
  ["enter", verb],
8509
8552
  ["esc", query ? "clear" : "back"]
8510
8553
  ]
@@ -8621,11 +8664,15 @@ function failure(title, error) {
8621
8664
  };
8622
8665
  }
8623
8666
  function AccountLine({ facts, width }) {
8624
- const right = `${facts.scope} ${facts.skills} installed`;
8667
+ const agents = facts.agents.length > 0 ? facts.agents.join(",") : "agents unset";
8668
+ const right = `${agents} ${facts.scope} ${facts.skills} installed`;
8625
8669
  const head = facts.signedIn ? facts.email ?? "signed in" : "not signed in";
8626
8670
  const room = Math.max(width - FRAME_CHROME - right.length - 2, 12);
8627
8671
  const tail = facts.signedIn ? ` ${hostOf(facts.server)}` : " — the catalog needs a session";
8672
+ const org = facts.signedIn && facts.organization ? ` ${facts.organization}` : "";
8628
8673
  const spare = room - 2 - head.length;
8674
+ const showOrg = spare >= org.length;
8675
+ const showTail = spare - (showOrg ? org.length : 0) >= tail.length;
8629
8676
  return /* @__PURE__ */ jsxs16(Box14, {
8630
8677
  width: width - FRAME_CHROME,
8631
8678
  justifyContent: "space-between",
@@ -8640,7 +8687,11 @@ function AccountLine({ facts, width }) {
8640
8687
  color: facts.signedIn ? undefined : tone.notice,
8641
8688
  children: ` ${clip(head, room - 2)}`
8642
8689
  }),
8643
- spare >= tail.length ? /* @__PURE__ */ jsx18(Text16, {
8690
+ showOrg ? /* @__PURE__ */ jsx18(Text16, {
8691
+ color: tone.primary,
8692
+ children: org
8693
+ }) : null,
8694
+ showTail ? /* @__PURE__ */ jsx18(Text16, {
8644
8695
  dimColor: true,
8645
8696
  children: tail
8646
8697
  }) : null
@@ -8806,20 +8857,41 @@ function HomeScreen({
8806
8857
  held.current = null;
8807
8858
  return routeFrom(answer, say(command, facts));
8808
8859
  }, [catalogDoor, ctx, facts, routeFrom]);
8809
- const ownRoute = useCallback3((value) => {
8860
+ const ownRoute = useCallback3(async (value) => {
8810
8861
  switch (value) {
8811
8862
  case "catalog.search":
8812
8863
  return { kind: "browse", tab: "skills" };
8813
8864
  case "installed.remove":
8814
8865
  return {
8815
8866
  kind: "pick",
8867
+ title: "remove",
8816
8868
  rows: installedRows(where).map((row) => ({
8817
8869
  name: row.name,
8818
8870
  description: `${row.bundle ?? "loose"} ${shortRef(row.ref)}`
8819
- }))
8871
+ })),
8872
+ empty: "Nothing installed here.",
8873
+ verb: "remove",
8874
+ command: (names) => ({ kind: "remove", names })
8820
8875
  };
8876
+ case "account.org": {
8877
+ const orgs = await organizationChoices(server);
8878
+ const active = orgs.find((org) => org.active)?.name;
8879
+ return {
8880
+ kind: "pick",
8881
+ title: "org",
8882
+ rows: orgs.map((org) => ({
8883
+ name: org.name,
8884
+ description: org.slug ?? ""
8885
+ })),
8886
+ empty: "You are not a member of any organization yet.",
8887
+ verb: "switch",
8888
+ single: true,
8889
+ ...active ? { marked: active } : {},
8890
+ command: (names) => ({ kind: "org", name: names[0] ?? "" })
8891
+ };
8892
+ }
8821
8893
  }
8822
- }, [where]);
8894
+ }, [where, server]);
8823
8895
  const routeForDoor = useCallback3(async (door) => door.opens === undefined ? ownRoute(door.value) : routeForCommand(door.opens), [ownRoute, routeForCommand]);
8824
8896
  const open = useCallback3((pending, title) => {
8825
8897
  pending.then((next) => {
@@ -8870,6 +8942,9 @@ function HomeScreen({
8870
8942
  });
8871
8943
  }, [reload, resite]);
8872
8944
  const afterLogin = useCallback3(() => {
8945
+ learnActiveOrganization(server).then(() => reload()).then(setFacts).catch(() => {
8946
+ return;
8947
+ });
8873
8948
  if (!wizard) {
8874
8949
  back();
8875
8950
  return;
@@ -8879,7 +8954,7 @@ function HomeScreen({
8879
8954
  setFacts(next);
8880
8955
  setRoute({ kind: "browse" });
8881
8956
  }).catch(() => setRoute(null));
8882
- }, [wizard, back, reload]);
8957
+ }, [wizard, back, reload, server]);
8883
8958
  const leaveWelcome = useCallback3((start) => {
8884
8959
  setWelcome(false);
8885
8960
  onWelcomed().catch(() => {
@@ -8966,13 +9041,16 @@ function HomeScreen({
8966
9041
  });
8967
9042
  }
8968
9043
  if (route?.kind === "pick") {
9044
+ const picked = route;
8969
9045
  return /* @__PURE__ */ jsx18(PickScreen, {
8970
- title: "remove",
8971
- rows: route.rows,
8972
- empty: "Nothing installed here.",
8973
- verb: "remove",
9046
+ title: picked.title,
9047
+ rows: picked.rows,
9048
+ empty: picked.empty,
9049
+ verb: picked.verb,
9050
+ ...picked.single ? { single: true } : {},
9051
+ ...picked.marked ? { marked: picked.marked } : {},
8974
9052
  onPick: (names) => {
8975
- open(routeForCommand({ kind: "remove", names }), "remove");
9053
+ open(routeForCommand(picked.command(names)), picked.title);
8976
9054
  },
8977
9055
  onBack: back
8978
9056
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yourskills",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://yourskills.store",