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 +6 -6
- package/README_CN.md +5 -5
- package/connector/credentials.mjs +42 -0
- package/connector/errors.mjs +58 -0
- package/connector/index.mjs +12 -0
- package/connector/oauth2.mjs +28 -0
- package/package.json +1 -1
- package/skills/scnet-hpc/VERSION +1 -1
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
|
+
Current release: **0.6.8**
|
|
8
8
|
|
|
9
9
|
<!-- scnet-release:start -->
|
|
10
|
-
## 0.6.
|
|
10
|
+
## 0.6.8 highlights
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
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
|
+
当前版本:**0.6.8**
|
|
8
8
|
|
|
9
9
|
<!-- scnet-release:start -->
|
|
10
|
-
## 0.6.
|
|
10
|
+
## 0.6.8 更新
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
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
|
+
}
|
package/connector/index.mjs
CHANGED
|
@@ -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,
|
package/connector/oauth2.mjs
CHANGED
|
@@ -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.
|
|
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",
|
package/skills/scnet-hpc/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.6.
|
|
1
|
+
0.6.8
|