@sparkelf/dsh-dataops-managed 0.3.22 → 0.3.24

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/lib/index.js CHANGED
@@ -1930,40 +1930,67 @@ const CONNECTORS_PATH = "/api/dsh-skill-center/connectors";
1930
1930
  const DATAOPS_CONNECTOR_ID = "dataops-managed";
1931
1931
  /** Resolve the persisted state file without depending on a global DSH path. */
1932
1932
  const CONNECTOR_STATE_PATH = join(process.env.DSH_HOME?.trim() || "/workspace/.dataops/dsh", "dataops-managed", "connector-state.json");
1933
- /** Capability definitions before permission filtering. */
1933
+ /**
1934
+ * Capability definitions before permission filtering.
1935
+ *
1936
+ * The set and its order follow `dataops/docs/dataops-connector-development-spec.md` §2.3,
1937
+ * whose credential `requiredFor` list is `approval.push`, `data.query`, and `admin.manage`.
1938
+ * Skill plaza and review queue are governed by `skill:*` permissions and stay out of the
1939
+ * connector's capability switches.
1940
+ */
1934
1941
  const CAPABILITIES = [
1935
- {
1936
- id: "skill.plaza",
1937
- name: "技能广场",
1938
- description: "读取、安装和管理当前账号可见的技能",
1939
- allowed: (scope) => hasPermission(scope, "skill:read")
1940
- },
1941
- {
1942
- id: "review.queue",
1943
- name: "审核队列",
1944
- description: "查看待审技能、扫描风险并给出审核结论",
1945
- allowed: (scope) => canReview(scope)
1946
- },
1947
1942
  {
1948
1943
  id: "data.query",
1949
1944
  name: "数据查询",
1950
1945
  description: "通过 DataOps 数据查询能力回答业务问题",
1946
+ defaultEnabled: true,
1951
1947
  allowed: () => true
1952
1948
  },
1953
1949
  {
1954
- id: "api.docs",
1955
- name: "管理接口文档",
1956
- description: "仅超级管理员可查询 DataOps OpenAPI 接口文档",
1957
- allowed: (scope) => isSuperAdmin(scope)
1950
+ id: "approval.push",
1951
+ name: "审批资源",
1952
+ description: "读取/办理万相中台的审批任务,并把状态回写",
1953
+ defaultEnabled: true,
1954
+ allowed: (scope) => canReview(scope)
1955
+ },
1956
+ {
1957
+ id: "admin.manage",
1958
+ name: "万相中台管理",
1959
+ description: "登录万相中台后台执行管理动作",
1960
+ defaultEnabled: true,
1961
+ allowed: (scope) => isAdmin(scope)
1958
1962
  }
1959
1963
  ];
1964
+ /** The ids the catalogue accepts; any other persisted key is a legacy value. */
1965
+ const CAPABILITY_IDS = CAPABILITIES.map((capability) => capability.id);
1966
+ /**
1967
+ * Keep only boolean values for known capability ids.
1968
+ *
1969
+ * A state file written before the catalogue changed may still hold retired ids. Those keys are
1970
+ * ignored rather than treated as a corrupt file; a missing new key takes the capability's
1971
+ * `defaultEnabled`.
1972
+ * @param value - the `capabilities` member of a parsed state file.
1973
+ * @returns the readable toggles, keyed by current capability id.
1974
+ */
1975
+ function migrateCapabilities(value) {
1976
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
1977
+ const stored = value;
1978
+ const capabilities = {};
1979
+ for (const id of CAPABILITY_IDS) if (typeof stored[id] === "boolean") capabilities[id] = stored[id];
1980
+ return capabilities;
1981
+ }
1960
1982
  /** Read state; a missing or corrupt file is a safe disabled state. */
1961
1983
  async function readConnectorState(path = CONNECTOR_STATE_PATH) {
1962
1984
  try {
1963
1985
  const payload = JSON.parse(await readFile(path, "utf8"));
1986
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return {
1987
+ enabled: false,
1988
+ capabilities: {}
1989
+ };
1990
+ const record = payload;
1964
1991
  return {
1965
- enabled: payload.enabled === true,
1966
- capabilities: typeof payload.capabilities === "object" && payload.capabilities !== null ? { ...payload.capabilities } : {}
1992
+ enabled: record.enabled === true,
1993
+ capabilities: migrateCapabilities(record.capabilities)
1967
1994
  };
1968
1995
  } catch {
1969
1996
  return {
@@ -1976,7 +2003,11 @@ async function readConnectorState(path = CONNECTOR_STATE_PATH) {
1976
2003
  async function writeConnectorState(state, path = CONNECTOR_STATE_PATH) {
1977
2004
  await mkdir(dirname(path), { recursive: true });
1978
2005
  const temporary = path + ".tmp-" + process.pid.toString();
1979
- await writeFile(temporary, JSON.stringify(state, null, 2), "utf8");
2006
+ const payload = {
2007
+ enabled: state.enabled,
2008
+ capabilities: migrateCapabilities(state.capabilities)
2009
+ };
2010
+ await writeFile(temporary, JSON.stringify(payload, null, 2), "utf8");
1980
2011
  await rename(temporary, path);
1981
2012
  }
1982
2013
  /** Build the connector row for one current scope. */
@@ -1985,7 +2016,7 @@ function buildConnectorView(scope, state) {
1985
2016
  id: capability.id,
1986
2017
  name: capability.name,
1987
2018
  description: capability.description,
1988
- enabled: state.capabilities[capability.id] !== false,
2019
+ enabled: state.capabilities[capability.id] ?? capability.defaultEnabled,
1989
2020
  permitted: true
1990
2021
  }));
1991
2022
  return {
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { InjectFace } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { en } from './locales.ts';
4
+ import { type PlazaSkill } from './skill-plaza-client.ts';
4
5
  /** Values the skill center passes to a contributed section. */
5
6
  export interface SkillPlazaSectionInjected {
6
7
  /** Translate one DataOps plaza message key. */
@@ -16,6 +17,38 @@ export interface SkillPlazaSectionInjected {
16
17
  }
17
18
  /** Props accepted by the plaza section. */
18
19
  export type SkillPlazaSectionProps = Partial<InjectFace<SkillPlazaSectionInjected>>;
20
+ /** One department option derived from the rows the plaza can show. */
21
+ export interface PlazaDepartmentOption {
22
+ /** Value the select submits: the department code when the row carries one. */
23
+ code: string;
24
+ /** Label shown in the select; never empty. */
25
+ name: string;
26
+ }
27
+ /**
28
+ * The department a plaza row belongs to.
29
+ *
30
+ * The backend normally sends `departmentCode`; a legacy row may carry only a name or path, so the
31
+ * fallbacks keep those rows selectable instead of dropping them into an empty key.
32
+ * @param skill - one plaza row.
33
+ * @returns the code, the name, or the deepest path name; empty when the row says nothing.
34
+ */
35
+ export declare function departmentKeyOf(skill: PlazaSkill): string;
36
+ /**
37
+ * The department choices present in the visible rows.
38
+ *
39
+ * The label falls back from the row's own name to the deepest path name and finally to the code,
40
+ * so a department the backend can no longer name still renders as a readable option.
41
+ * @param skills - every row the current query returned.
42
+ * @returns distinct departments, sorted by label.
43
+ */
44
+ export declare function departmentOptionsOf(skills: readonly PlazaSkill[]): PlazaDepartmentOption[];
45
+ /**
46
+ * The rows the department tab shows.
47
+ * @param skills - every row the current query returned.
48
+ * @param code - selected department key, or an empty string for all of them.
49
+ * @returns the department-shared rows, narrowed to `code` when it is set.
50
+ */
51
+ export declare function filterByDepartment(skills: readonly PlazaSkill[], code: string): PlazaSkill[];
19
52
  /**
20
53
  * Browse, share, and install skills other users published.
21
54
  *
@@ -23,7 +23,6 @@ export declare const en: {
23
23
  readonly defaultsNeverPublished: "Not published yet";
24
24
  readonly defaultsNotAdmin: "Administrators only";
25
25
  readonly plazaTitle: "Skill plaza";
26
- readonly plazaAllTags: "All tags";
27
26
  readonly plazaMine: "Shared by me";
28
27
  readonly plazaShowDetail: "Details";
29
28
  readonly plazaClose: "Close";
@@ -42,6 +41,10 @@ export declare const en: {
42
41
  readonly plazaTabApproved: "Approved";
43
42
  readonly plazaTabPending: "Pending";
44
43
  readonly plazaTabAll: "All";
44
+ readonly plazaTabDepartment: "Department";
45
+ readonly plazaDepartment: "Department";
46
+ readonly plazaDepartmentAll: "All departments";
47
+ readonly plazaDepartmentEmpty: "No department-shared skills yet.";
45
48
  readonly plazaSharedTag: "Shared";
46
49
  readonly plazaReview: "Review";
47
50
  readonly plazaPendingEmpty: "No skills are waiting for review.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparkelf/dsh-dataops-managed",
3
- "version": "0.3.22",
3
+ "version": "0.3.24",
4
4
  "description": "DataOps JWT intake and credential-backed MCP tools for managed DeepSeek Harness workspaces.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",