dsh-scnet 0.6.7 → 0.6.8

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/README.md CHANGED
@@ -4,15 +4,15 @@
4
4
 
5
5
  DSH-SCNet (`dsh-scnet` on npm) is a community-maintained DSH bundle for operating Supercomputing Network (SCNet). It packages the source [`scnet-hpc`](https://github.com/lql341/scnet-hpc) Skill, profile-aware utilities, and bounded tools for SSH, Slurm, OpenAPI files, regional resources, and Notebook discovery.
6
6
 
7
- Current release: **0.6.7**
7
+ Current release: **0.6.8**
8
8
 
9
9
  <!-- scnet-release:start -->
10
- ## 0.6.7 highlights
10
+ ## 0.6.8 highlights
11
11
 
12
- - Desktop hosts can inject a verified Python 3 interpreter through `DSH_SCNET_PYTHON` or
13
- `SCNET_PYTHON`; local entrypoints no longer require the command name `python3`.
14
- - Shared shell helpers reject Python 2 and invalid configured interpreters before running
15
- OpenAPI or local job-generation code.
12
+ - The connector now exposes stable error codes and JSON-safe envelopes for authentication,
13
+ callback, network, scope, region, and platform failures.
14
+ - OAuth callback sessions are one-shot and reject replay; platform keyring adapters have no
15
+ plaintext fallback.
16
16
  <!-- scnet-release:end -->
17
17
 
18
18
  > This is an independent community project. It is compatible with DeepSeek Harness but is not an official DeepSeek product and does not imply endorsement, partnership, or authorization by DeepSeek.
package/README_CN.md CHANGED
@@ -4,14 +4,14 @@
4
4
 
5
5
  <p align="center">在 DeepSeek Harness 里操作超算互联网(SCNet):配置 SSH、管理 Slurm 作业、查询 OpenAPI 区域和文件、检查 Notebook 资源与实例,并保留海光 DCU/DTK 诊断能力。</p>
6
6
 
7
- 当前版本:**0.6.7**
7
+ 当前版本:**0.6.8**
8
8
 
9
9
  <!-- scnet-release:start -->
10
- ## 0.6.7 更新
10
+ ## 0.6.8 更新
11
11
 
12
- - Desktop 主机可通过 `DSH_SCNET_PYTHON` 或 `SCNET_PYTHON` 注入经过校验的 Python 3
13
- 解释器,本地入口不再强制要求命令名为 `python3`。
14
- - 共享 shell 辅助函数会在运行 OpenAPI 或本地作业生成前拒绝 Python 2 和无效解释器路径。
12
+ - connector 现在提供稳定错误码和 JSON 安全错误信封,覆盖认证、回调、网络、权限、
13
+ 区域和平台能力失败。
14
+ - OAuth 回调会话只允许消费一次并拒绝重放;平台 keyring 适配器不提供明文回退。
15
15
  <!-- scnet-release:end -->
16
16
 
17
17
  > 本项目是独立维护的社区项目,与 DeepSeek Harness 兼容,但不是 DeepSeek 官方产品,也不表示 DeepSeek 对本项目提供背书、合作或授权。
@@ -0,0 +1,42 @@
1
+ function requiredString(value, label) {
2
+ const normalized = String(value ?? '').trim()
3
+ if (!normalized) throw new TypeError(`${label} must be a non-empty string`)
4
+ return normalized
5
+ }
6
+
7
+ function assertBackend(backend) {
8
+ if (!backend || typeof backend !== 'object') {
9
+ throw new TypeError('credential backend is required')
10
+ }
11
+ for (const method of ['get', 'set', 'delete']) {
12
+ if (typeof backend[method] !== 'function') {
13
+ throw new TypeError(`credential backend must implement ${method}()`)
14
+ }
15
+ }
16
+ return backend
17
+ }
18
+
19
+ /**
20
+ * Adapt a platform keyring implementation without providing a plaintext fallback.
21
+ * The backend owns encryption, locking, and OS-specific Secret Service behavior.
22
+ */
23
+ export function createCredentialStoreAdapter({ backend, namespace = 'scnet' } = {}) {
24
+ const store = assertBackend(backend)
25
+ const prefix = requiredString(namespace, 'credential namespace')
26
+ const keyOf = (key) => `${prefix}.${requiredString(key, 'credential key')}`
27
+ return Object.freeze({
28
+ async get(key) {
29
+ return store.get(keyOf(key))
30
+ },
31
+ async set(key, value) {
32
+ if (value === undefined) throw new TypeError('credential value must not be undefined')
33
+ return store.set(keyOf(key), structuredClone(value))
34
+ },
35
+ async delete(key) {
36
+ return store.delete(keyOf(key))
37
+ },
38
+ async clear() {
39
+ if (typeof store.clear === 'function') return store.clear(prefix)
40
+ },
41
+ })
42
+ }
@@ -0,0 +1,58 @@
1
+ const ERROR_CODES = [
2
+ 'AUTHENTICATION_REQUIRED',
3
+ 'AUTHENTICATION_FAILED',
4
+ 'SCOPE_INSUFFICIENT',
5
+ 'NETWORK_TIMEOUT',
6
+ 'REGION_UNAVAILABLE',
7
+ 'PLATFORM_UNSUPPORTED',
8
+ 'CALLBACK_REPLAY',
9
+ 'CALLBACK_MISMATCH',
10
+ 'CONFIGURATION_REQUIRED',
11
+ 'INTERNAL_ERROR',
12
+ ]
13
+
14
+ export const CONNECTOR_ERROR_CODES = Object.freeze(ERROR_CODES)
15
+
16
+ function requiredString(value, label) {
17
+ const normalized = String(value ?? '').trim()
18
+ if (!normalized) throw new TypeError(`${label} must be a non-empty string`)
19
+ return normalized
20
+ }
21
+
22
+ export class ConnectorError extends Error {
23
+ constructor(code, message, { operation, retryable = false } = {}) {
24
+ if (!CONNECTOR_ERROR_CODES.includes(code)) {
25
+ throw new RangeError(`unsupported connector error code: ${code}`)
26
+ }
27
+ super(requiredString(message, 'error message'))
28
+ this.name = 'ConnectorError'
29
+ this.code = code
30
+ this.operation = operation === undefined ? undefined : requiredString(operation, 'operation')
31
+ this.retryable = Boolean(retryable)
32
+ }
33
+
34
+ toJSON() {
35
+ return {
36
+ ok: false,
37
+ error_code: this.code,
38
+ error: this.message,
39
+ ...(this.operation ? { operation: this.operation } : {}),
40
+ retryable: this.retryable,
41
+ }
42
+ }
43
+ }
44
+
45
+ export function connectorError(code, message, options) {
46
+ return new ConnectorError(code, message, options)
47
+ }
48
+
49
+ export function errorEnvelope(error, { operation } = {}) {
50
+ if (error instanceof ConnectorError) {
51
+ return error.toJSON()
52
+ }
53
+ return new ConnectorError(
54
+ 'INTERNAL_ERROR',
55
+ error instanceof Error ? error.message : String(error || 'unknown connector error'),
56
+ { operation },
57
+ ).toJSON()
58
+ }
@@ -8,10 +8,22 @@ export {
8
8
  getOperationDefinition,
9
9
  } from './contract.mjs'
10
10
 
11
+ export {
12
+ CONNECTOR_ERROR_CODES,
13
+ ConnectorError,
14
+ connectorError,
15
+ errorEnvelope,
16
+ } from './errors.mjs'
17
+
18
+ export {
19
+ createCredentialStoreAdapter,
20
+ } from './credentials.mjs'
21
+
11
22
  export {
12
23
  MemoryCredentialStore,
13
24
  TokenLifecycle,
14
25
  createAuthorizationRequest,
26
+ createAuthorizationSession,
15
27
  createCodeVerifier,
16
28
  createPkceChallenge,
17
29
  createRefreshTokenRequest,
@@ -1,4 +1,5 @@
1
1
  import { createHash, randomBytes, randomUUID } from 'node:crypto'
2
+ import { ConnectorError } from './errors.mjs'
2
3
 
3
4
  const LOOPBACK_HOSTS = new Set(['127.0.0.1', '[::1]', 'localhost'])
4
5
 
@@ -130,6 +131,33 @@ export function validateAuthorizationCallback(callbackUrl, request) {
130
131
  })
131
132
  }
132
133
 
134
+ export function createAuthorizationSession(options = {}) {
135
+ const request = createAuthorizationRequest(options)
136
+ let consumed = false
137
+ return Object.freeze({
138
+ request,
139
+ get consumed() {
140
+ return consumed
141
+ },
142
+ consumeCallback(callbackUrl) {
143
+ if (consumed) {
144
+ throw new ConnectorError(
145
+ 'CALLBACK_REPLAY',
146
+ 'OAuth authorization callback has already been consumed',
147
+ { operation: 'login' },
148
+ )
149
+ }
150
+ consumed = true
151
+ try {
152
+ return validateAuthorizationCallback(callbackUrl, request)
153
+ } catch (error) {
154
+ if (error instanceof ConnectorError) throw error
155
+ throw new ConnectorError('CALLBACK_MISMATCH', error.message, { operation: 'login' })
156
+ }
157
+ },
158
+ })
159
+ }
160
+
133
161
  export function createTokenExchangeRequest({
134
162
  tokenEndpoint,
135
163
  clientId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-scnet",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "description": "Community DeepSeek Harness bundle for SCNet SSH and OpenAPI jobs, files, Notebook resources, Slurm diagnostics, and Hygon DCU/DTK guidance.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
@@ -1 +1 @@
1
- 0.6.7
1
+ 0.6.8