mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
feat(util): mint UUIDs without crypto.randomUUID in every context
This commit is contained in:
@@ -57,6 +57,14 @@
|
||||
}
|
||||
],
|
||||
"no-useless-constructor": "error",
|
||||
"no-restricted-properties": [
|
||||
"error",
|
||||
{
|
||||
"object": "crypto",
|
||||
"property": "randomUUID",
|
||||
"message": "browsers withhold crypto.randomUUID outside secure contexts (plain-HTTP LAN pages); mint through @deepseek-ai/dsh-util-crypto instead"
|
||||
}
|
||||
],
|
||||
"typescript/await-thenable": "error",
|
||||
"typescript/ban-ts-comment": [
|
||||
"error",
|
||||
|
||||
@@ -58,7 +58,7 @@ function styleInjectionModule(
|
||||
* Everything else under @deepseek-ai/* is either a module-table entry
|
||||
* (external) or a leak the purity gate rejects.
|
||||
*/
|
||||
export const INLINE_SAFE = /^@deepseek-ai\/dsh-(host-apiproxy|file-reference|session|llm|tools|brand)(\/|$)/
|
||||
export const INLINE_SAFE = /^@deepseek-ai\/dsh-(host-apiproxy|file-reference|session|llm|tools|brand|util-crypto)(\/|$)/
|
||||
|
||||
/**
|
||||
* Vendored framework libraries: rescoped into @deepseek-ai, so the gate below
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -60,21 +60,22 @@
|
||||
"@deepseek-ai/dsh-client-connection": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm-retry": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-stats": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-permission-presets": "workspace:^",
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-todo": "workspace:^"
|
||||
"@deepseek-ai/dsh-session-stats": "workspace:^",
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-todo": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -86,10 +87,10 @@
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-test-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction": "workspace:^",
|
||||
@@ -100,12 +101,13 @@
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-stats": "workspace:^",
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-todo": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"react": "^18.2.0",
|
||||
"@deepseek-ai/dsh-settings": "workspace:^"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
import { Service } from '@deepseek-ai/cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { randomUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
// Type-only imports: a plugin-to-plugin value import is a bundle purity
|
||||
// error, so scope resolution goes through the sessions service (scopeOf
|
||||
// method) instead of the standalone helper.
|
||||
@@ -63,7 +64,7 @@ export interface IConversation {
|
||||
function browserDraftAttachment(file: File): ComposerAttachment {
|
||||
return {
|
||||
kind: 'image',
|
||||
id: crypto.randomUUID() as DraftAttachmentId,
|
||||
id: randomUUID() as DraftAttachmentId,
|
||||
previewUrl: URL.createObjectURL(file),
|
||||
file,
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../ui-settings"
|
||||
},
|
||||
{
|
||||
"path": "../../util/crypto"
|
||||
}
|
||||
],
|
||||
"exclude": [
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^",
|
||||
"@noble/hashes": "^2.3.0",
|
||||
"@yarnpkg/parsers": "^3.1.0",
|
||||
"acorn": "^8.17.0",
|
||||
|
||||
+5
-2
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
import { sha1 } from '@noble/hashes/legacy.js'
|
||||
import { sha256, sha512 } from '@noble/hashes/sha2.js'
|
||||
import { randomUUID as mintUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
import { Buffer } from 'buffer'
|
||||
|
||||
type Hasher = (input: Uint8Array) => Uint8Array
|
||||
@@ -73,11 +74,13 @@ export function randomBytes(size: number): Buffer<ArrayBuffer> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Random v4 UUID.
|
||||
* Random v4 UUID. Delegated to the repository's own mint rather than to
|
||||
* `crypto.randomUUID`, which browsers expose only in secure contexts — a
|
||||
* preview served over plain HTTP on a LAN address has no `randomUUID`.
|
||||
* @returns the UUID string.
|
||||
*/
|
||||
export function randomUUID(): import('node:crypto').UUID {
|
||||
return globalThis.crypto.randomUUID()
|
||||
return mintUUID()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -200,7 +200,8 @@ export function mkdirSync(path: PathArg, options?: { recursive?: boolean }): str
|
||||
* @returns the created directory path.
|
||||
*/
|
||||
export function mkdtempSync(prefix: string): string {
|
||||
const suffix = globalThis.crypto.randomUUID().replaceAll('-', '').slice(0, 6)
|
||||
// Not crypto.randomUUID: browsers expose that only in secure contexts.
|
||||
const suffix = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(3)), byte => byte.toString(16).padStart(2, '0')).join('')
|
||||
const target = `${prefix}${suffix}`
|
||||
vfs().mkdirSync(target, { recursive: true })
|
||||
return target
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Fill the `crypto.randomUUID` gap on insecure origins. Browsers expose
|
||||
* `randomUUID` only in secure contexts, and a preview served over plain HTTP
|
||||
* on a LAN address is not one — while product code (bundled and VFS-loaded
|
||||
* alike) reaches the global directly, Node-style. The worker patches the one
|
||||
* `crypto` instance instead of teaching every caller.
|
||||
*/
|
||||
import { randomUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
|
||||
/** Install `crypto.randomUUID` when the context withholds it. */
|
||||
export function installCryptoGlobals(): void {
|
||||
// In a secure context the platform method is present and stays untouched.
|
||||
if (typeof globalThis.crypto.randomUUID === 'function') return
|
||||
Object.defineProperty(globalThis.crypto, 'randomUUID', {
|
||||
value: randomUUID,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
})
|
||||
}
|
||||
@@ -21,12 +21,14 @@ import { createNodeBuiltins, REPLACED_PREFIXES } from './node/builtins.ts'
|
||||
import { whenRequestListener } from './node/builtin_modules/implemented/http.ts'
|
||||
import { installTimerGlobals } from './node/globals/timers.ts'
|
||||
import { installProcessGlobal } from './node/globals/process.ts'
|
||||
import { installCryptoGlobals } from './node/globals/crypto.ts'
|
||||
import { isShellStartFrame } from './shell/process/protocol.ts'
|
||||
import { runShellProcess } from './shell/process/host.ts'
|
||||
|
||||
// Before the timer globals, so the wrappers close over the patched platform.
|
||||
installAsyncContextHooks()
|
||||
installTimerGlobals()
|
||||
installCryptoGlobals()
|
||||
|
||||
let host: { handleMessage(data: unknown): void } | undefined
|
||||
let shellRole = false
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* The crypto global patch and the shim UUID it installs: on an insecure
|
||||
* origin the platform withholds `crypto.randomUUID` while product code calls
|
||||
* it off the global, so the worker fills the one missing method — and leaves
|
||||
* a platform that already has it untouched.
|
||||
*/
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { installCryptoGlobals } from '../../src/node/globals/crypto.ts'
|
||||
import { randomUUID } from '../../src/node/builtin_modules/implemented/crypto.ts'
|
||||
|
||||
const V4_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
|
||||
|
||||
describe('the shim randomUUID', () => {
|
||||
it('emits RFC 9562 v4 ids without touching the platform method', () => {
|
||||
for (let round = 0; round < 32; round += 1) expect(randomUUID()).toMatch(V4_SHAPE)
|
||||
expect(new Set(Array.from({ length: 32 }, () => randomUUID())).size).toBe(32)
|
||||
})
|
||||
})
|
||||
|
||||
describe('installCryptoGlobals', () => {
|
||||
afterEach(() => { vi.unstubAllGlobals() })
|
||||
|
||||
it('fills randomUUID on a crypto that lacks it, the insecure-origin shape', () => {
|
||||
const bare = { getRandomValues: globalThis.crypto.getRandomValues.bind(globalThis.crypto) }
|
||||
vi.stubGlobal('crypto', bare)
|
||||
installCryptoGlobals()
|
||||
expect((globalThis.crypto as Crypto).randomUUID()).toMatch(V4_SHAPE)
|
||||
})
|
||||
|
||||
it('leaves a platform that already provides randomUUID untouched', () => {
|
||||
const platform = (): string => 'platform-owned'
|
||||
vi.stubGlobal('crypto', { randomUUID: platform })
|
||||
installCryptoGlobals()
|
||||
expect(globalThis.crypto.randomUUID).toBe(platform)
|
||||
})
|
||||
})
|
||||
@@ -31,6 +31,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
},
|
||||
{
|
||||
"path": "../../util/crypto"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -45,15 +45,16 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-attachment": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-default-model": "workspace:^",
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
"@deepseek-ai/dsh-attachment": "workspace:^",
|
||||
"@deepseek-ai/dsh-brand": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-credentials": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-directory-picker": "workspace:^",
|
||||
"@deepseek-ai/dsh-jobs": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-native-command": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
@@ -65,10 +66,10 @@
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-skill": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-jobs": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-approval": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-questions": "workspace:^",
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^",
|
||||
"@deepseek-ai/dsh-workspace": "workspace:^",
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"fflate": "^0.8.2",
|
||||
@@ -88,7 +89,6 @@
|
||||
"@deepseek-ai/dsh-storage": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage-domain": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-registry": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/dsh-typert-registry": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import type { z } from 'zod'
|
||||
import { randomUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
import type { ApiProxy, HostFrame, MuxFrame } from '../api/index.ts'
|
||||
import type { RequestPayload, ResponseValue, RpcMethodMap } from '../api/rpc-map.ts'
|
||||
import type { ClientRequest, ClientResponse, RpcMessage, RpcReceipt, RpcRequest, RpcResponse, ServerRequest } from '../api/rpc.ts'
|
||||
@@ -296,8 +297,9 @@ export abstract class AbstractApiClient implements IApiClient {
|
||||
}
|
||||
|
||||
protected mintRpcId(): RpcId {
|
||||
// crypto.randomUUID is a Web API (browser + Node ≥19): keeps this base platform-neutral.
|
||||
return RpcId(crypto.randomUUID())
|
||||
// Not crypto.randomUUID: browsers withhold it outside secure contexts,
|
||||
// and this base also mints on pages served over plain HTTP.
|
||||
return RpcId(randomUUID())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,6 +100,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../util/native-command"
|
||||
},
|
||||
{
|
||||
"path": "../../util/crypto"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { randomUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { AttachmentError, admitEncodedImages } from '@deepseek-ai/dsh-attachment'
|
||||
import type { EncodedImageAttachment } from '@deepseek-ai/dsh-attachment/types'
|
||||
@@ -256,7 +257,7 @@ export class CommandRuntime extends TypertRemoteService {
|
||||
/** Monotonic per-instance counter behind {@link mintCommandId}. */
|
||||
private commandSeq = 0
|
||||
/** Instance token keeping minted ids unique across process restarts over one resumed log. */
|
||||
private readonly instanceToken = crypto.randomUUID().slice(0, 8)
|
||||
private readonly instanceToken = randomUUID().slice(0, 8)
|
||||
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'commands')
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../typert/protocol"
|
||||
},
|
||||
{
|
||||
"path": "../../util/crypto"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-util-crypto": "workspace:^",
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** Message value types, identity, and immutable construction helpers. */
|
||||
|
||||
import { randomUUID } from '@deepseek-ai/dsh-util-crypto'
|
||||
import { MessageId, type CallId } from './brand.ts'
|
||||
import { deepFreeze } from './call-config.ts'
|
||||
import type { ContentBlock, StreamChunk, ToolResultBlock } from './types.ts'
|
||||
@@ -180,7 +181,7 @@ export function createMessage<T extends NewMessage>(
|
||||
): T & Pick<Message, 'id'> {
|
||||
return freezeMessage({
|
||||
...input,
|
||||
id: MessageId(crypto.randomUUID()),
|
||||
id: MessageId(randomUUID()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../util/timeout"
|
||||
},
|
||||
{
|
||||
"path": "../../util/crypto"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/util/crypto/README.md
|
||||
README.md: 456f7b8dc9476b6e2dc4844964912410ec934c2d
|
||||
README.zh.md: 9b9836b4bef528b3aeae5531ceba549b1963d4bd
|
||||
@@ -0,0 +1,31 @@
|
||||
# dsh-util-crypto
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Zero-dependency v4 UUID minting over `crypto.getRandomValues` — the one random primitive every shipped context provides. `crypto.randomUUID` is a secure-context Web API: a page or worker served over plain HTTP on a LAN address (the browser preview deployment) has no such method, so code that must run there cannot call it. The repository-wide `no-restricted-properties` lint rule points `crypto.randomUUID` callers here; Node-only code importing `randomUUID` from `node:crypto` stays as it is.
|
||||
|
||||
It is a **library, not a service or plugin**: no `ctx`, registers nothing, holds no state.
|
||||
|
||||
## API
|
||||
|
||||
```ts
|
||||
import { randomUUID, type Uuid } from '@deepseek-ai/dsh-util-crypto'
|
||||
```
|
||||
|
||||
| Export | Role |
|
||||
|---|---|
|
||||
| `randomUUID()` | Random RFC 9562 v4 UUID string, minted from `crypto.getRandomValues`. Drop-in for `crypto.randomUUID()`. |
|
||||
| `Uuid` | The five-group UUID string type, matching `crypto.randomUUID`'s declared return shape. |
|
||||
|
||||
## Model Experience
|
||||
|
||||
None directly; consumers mint request, session, and attachment identifiers with it, none of which enter prompts as semantic content.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; identifier-minting consumers own any request changes.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **v4 only** — no other UUID versions, namespaces, or parsing; consumers needing more should take a real UUID dependency.
|
||||
- **Uniqueness is probabilistic** — 122 random bits, the same guarantee `crypto.randomUUID` gives; nothing here detects collisions.
|
||||
@@ -0,0 +1,31 @@
|
||||
# dsh-util-crypto
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
零依赖的 v4 UUID 铸造,基于 `crypto.getRandomValues`——所有发布上下文都提供的那个随机原语。`crypto.randomUUID` 是安全上下文限定的 Web API:经普通 HTTP 在局域网地址上提供的页面或 worker(浏览器预览部署)根本没有这个方法,必须在那里运行的代码不能调它。全仓 `no-restricted-properties` lint 规则把 `crypto.randomUUID` 的调用者指到这里;只跑在 Node 的代码从 `node:crypto` 导入 `randomUUID` 维持原样。
|
||||
|
||||
它是**库,不是服务也不是插件**:无 `ctx`、不注册任何东西、不持有状态。
|
||||
|
||||
## API
|
||||
|
||||
```ts
|
||||
import { randomUUID, type Uuid } from '@deepseek-ai/dsh-util-crypto'
|
||||
```
|
||||
|
||||
| 导出 | 角色 |
|
||||
|---|---|
|
||||
| `randomUUID()` | 随机 RFC 9562 v4 UUID 字符串,由 `crypto.getRandomValues` 铸造。可原位替换 `crypto.randomUUID()`。 |
|
||||
| `Uuid` | 五段式 UUID 字符串类型,与 `crypto.randomUUID` 声明的返回形状一致。 |
|
||||
|
||||
## Model Experience
|
||||
|
||||
无直接影响;消费方用它铸造请求、会话与附件标识符,均不作为语义内容进入提示词。
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
无直接失效;铸造标识符的消费方自行负责其请求变化。
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **仅 v4**——不提供其他 UUID 版本、命名空间或解析;需要更多能力的消费方应引入真正的 UUID 依赖。
|
||||
- **唯一性是概率性的**——122 位随机,与 `crypto.randomUUID` 同级保证;此处不做碰撞检测。
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-util-crypto",
|
||||
"description": "Zero-dependency crypto-adjacent helpers usable in every context (browser, worker, Node) including insecure origins: v4 UUID minting over crypto.getRandomValues today, room for siblings",
|
||||
"version": "0.1.0-rc.8",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
||||
"directory": "packages/util/crypto"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./invariant": {
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* UUID minting that works in every JavaScript context this repository ships
|
||||
* to. `crypto.randomUUID` is a secure-context Web API — a page or worker
|
||||
* served over plain HTTP on a LAN address has no such method — while
|
||||
* `crypto.getRandomValues` is unrestricted everywhere (browsers, workers,
|
||||
* Node ≥ 19). One implementation here replaces per-caller polyfills; the
|
||||
* `no-restricted-properties` lint rule points `crypto.randomUUID` callers at
|
||||
* this module.
|
||||
* @module @deepseek-ai/dsh-util-crypto
|
||||
*/
|
||||
|
||||
/** RFC 9562 UUID string, the shape `crypto.randomUUID` declares. */
|
||||
export type Uuid = `${string}-${string}-${string}-${string}-${string}`
|
||||
|
||||
/**
|
||||
* Random v4 UUID, minted from `crypto.getRandomValues`.
|
||||
* @returns the UUID string.
|
||||
*/
|
||||
export function randomUUID(): Uuid {
|
||||
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16))
|
||||
// RFC 9562 §5.4: version 4 in the high nibble of byte 6, variant 10 in byte 8.
|
||||
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40
|
||||
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80
|
||||
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('')
|
||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Package-owned invariant companion for `@deepseek-ai/dsh-util-crypto`.
|
||||
* @module @deepseek-ai/dsh-util-crypto/invariant
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start */
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-util-crypto'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'util-crypto-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: this pure utility owns no event stream or mutable runtime data; its value
|
||||
* algebra is enforced by unit tests.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
/**
|
||||
* Register this package's invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
/* jscpd:ignore-end */
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* The minted UUID's contract: RFC 9562 v4 shape (version and variant bits
|
||||
* pinned), uniqueness across calls, and no dependence on the secure-context
|
||||
* `crypto.randomUUID` — the reason this package exists.
|
||||
*/
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { randomUUID } from '../src/index.ts'
|
||||
|
||||
const V4_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
|
||||
|
||||
describe('randomUUID', () => {
|
||||
it('emits RFC 9562 v4 ids', () => {
|
||||
for (let round = 0; round < 64; round += 1) expect(randomUUID()).toMatch(V4_SHAPE)
|
||||
})
|
||||
|
||||
it('emits distinct ids across calls', () => {
|
||||
expect(new Set(Array.from({ length: 64 }, () => randomUUID())).size).toBe(64)
|
||||
})
|
||||
|
||||
it('mints without the platform randomUUID, the insecure-origin shape', () => {
|
||||
const bare = { getRandomValues: globalThis.crypto.getRandomValues.bind(globalThis.crypto) }
|
||||
vi.stubGlobal('crypto', bare)
|
||||
try {
|
||||
expect(randomUUID()).toMatch(V4_SHAPE)
|
||||
} finally {
|
||||
vi.unstubAllGlobals()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+24
@@ -1864,6 +1864,9 @@ importers:
|
||||
|
||||
packages/client/ui-conversation:
|
||||
dependencies:
|
||||
'@deepseek-ai/dsh-util-crypto':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/crypto
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
@@ -4251,6 +4254,9 @@ importers:
|
||||
|
||||
packages/experimental/webworker-runtime:
|
||||
dependencies:
|
||||
'@deepseek-ai/dsh-util-crypto':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/crypto
|
||||
'@noble/hashes':
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
@@ -5127,6 +5133,9 @@ importers:
|
||||
'@deepseek-ai/dsh-user-questions':
|
||||
specifier: workspace:^
|
||||
version: link:../../interaction/user-questions
|
||||
'@deepseek-ai/dsh-util-crypto':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/crypto
|
||||
'@deepseek-ai/dsh-workspace':
|
||||
specifier: workspace:^
|
||||
version: link:../../workspace/workspace
|
||||
@@ -5316,6 +5325,9 @@ importers:
|
||||
|
||||
packages/interaction/commands:
|
||||
dependencies:
|
||||
'@deepseek-ai/dsh-util-crypto':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/crypto
|
||||
zod:
|
||||
specifier: ^4.4.3
|
||||
version: 4.4.3
|
||||
@@ -5552,6 +5564,9 @@ importers:
|
||||
|
||||
packages/llm/llm:
|
||||
dependencies:
|
||||
'@deepseek-ai/dsh-util-crypto':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/crypto
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
@@ -8374,6 +8389,15 @@ importers:
|
||||
specifier: workspace:^
|
||||
version: link:../../runtime-diagnostics/invariants
|
||||
|
||||
packages/util/crypto:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
version: link:../../../vendor/cordis
|
||||
'@deepseek-ai/dsh-invariants':
|
||||
specifier: workspace:^
|
||||
version: link:../../runtime-diagnostics/invariants
|
||||
|
||||
packages/util/home-paths:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
|
||||
@@ -18,19 +18,19 @@ interface Profile {
|
||||
// snapshot; they do not re-evaluate that deleted baseline or track its preset.
|
||||
const profiles = {
|
||||
source: {
|
||||
count: 88,
|
||||
count: 89,
|
||||
indexes: [0, 1, 4, 5],
|
||||
sha256: 'da1dfd77cb6eb66be93d8d3820f9b9b68b7aa391c24680f8851c0910298f9e3b',
|
||||
sha256: '3e8b8455827254d6da59afb803d30f062b9fe1264b4df75ce4c8cd6e742b93dd',
|
||||
},
|
||||
example: {
|
||||
count: 87,
|
||||
count: 88,
|
||||
indexes: [0, 1, 2, 4, 5],
|
||||
sha256: '6a2606053bc1ec1de3b02611de88ea51d201dac13a1f193e4934d33c08b95f08',
|
||||
sha256: 'e2b944c028ef1971302d47b2eb0e95c05e740623c7cbaebb4b1312dbf73bf156',
|
||||
},
|
||||
test: {
|
||||
count: 83,
|
||||
count: 84,
|
||||
indexes: [0, 3, 4, 5],
|
||||
sha256: '7995e14926a36c40bd65c474637735222a95fb030395681685f03060e50a7b78',
|
||||
sha256: '7800bf57f0d689a5623e4ad60315b10173158fb9cf988d3f005cb115f2245691',
|
||||
},
|
||||
} as const satisfies Record<string, Profile>
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"@deepseek-ai/dsh-experimental-tool-agent-team/invariant": ["./packages/experimental/tool-agent-team/src/invariant.ts"],
|
||||
"@deepseek-ai/dsh-experimental-webworker-runtime/invariant": ["./packages/experimental/webworker-runtime/src/invariant.ts"],
|
||||
"@deepseek-ai/dsh-experimental-webworker-packer/invariant": ["./packages/experimental/webworker-packer/src/invariant.ts"],
|
||||
"@deepseek-ai/dsh-util-crypto/invariant": ["./packages/util/crypto/src/invariant.ts"],
|
||||
"@deepseek-ai/dsh-*/invariant": [
|
||||
"./packages/core/*/src/invariant.ts",
|
||||
"./packages/prompt/*/src/invariant.ts",
|
||||
@@ -241,6 +242,7 @@
|
||||
"@deepseek-ai/dsh-experimental-tool-agent-team": ["./packages/experimental/tool-agent-team/src"],
|
||||
"@deepseek-ai/dsh-experimental-webworker-runtime": ["./packages/experimental/webworker-runtime/src"],
|
||||
"@deepseek-ai/dsh-experimental-webworker-packer": ["./packages/experimental/webworker-packer/src"],
|
||||
"@deepseek-ai/dsh-util-crypto": ["./packages/util/crypto/src"],
|
||||
"@deepseek-ai/dsh-*": [
|
||||
"./packages/core/*/src",
|
||||
"./packages/prompt/*/src",
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
{ "path": "./packages/util/native-command" },
|
||||
{ "path": "./packages/util/home-paths" },
|
||||
{ "path": "./packages/util/timeout" },
|
||||
{ "path": "./packages/util/crypto" },
|
||||
{ "path": "./packages/util/output-retention" },
|
||||
{ "path": "./packages/util/atomic-write" },
|
||||
{ "path": "./packages/attachment/attachment" },
|
||||
|
||||
Reference in New Issue
Block a user