Files
deepseek-harness/apps/desktop/scripts/desktop-cos.ts
T
winewill 1b6451a647 fix(desktop): follow the documented release version rules
Review found that `apps/desktop/README.md#release-versions` and the
2026-09-16 version-derivation note already specify this convention, and
that three of their requirements were reversed without saying so. The
rules themselves are right; what was wrong was leaving them undocumented
and rejecting one of the forms they prescribe.

A stable base takes `-test.YYYYMMDD.index`, which validation refused
because such a version ranks below its base. That ordering is the point:
the form belongs to a test feed that never serves the production
release. Both READMEs, the older note, and a new note now record which
requirements this change reverses and why.

Three defects the review found, each reproducible:

- `afterPack` compared the bundled runtime against the product version,
  which broke installed-update qualification: that flow passes a runtime
  whose version was deliberately rewritten. The expected version is now
  a parameter the preparer supplies, and `verifyDesktopRuntime` says
  "bundled X is not the expected Y" rather than naming Electron.
- Upload could only learn the published version from a shell variable,
  conflicting with "release fields come only from the target file". It
  reads the completion record packaging wrote, and a mismatch now prints
  the version that record holds.
- Automatic numbering read one listing page and one fixed directory. It
  now follows every page, discards a listing it could not finish rather
  than reusing an index that would overwrite a published installer,
  bounds the request through the client's own timeout, and reads the
  directory an unsigned build actually writes.

Also: `npm run` and `yarn run` set `npm_execpath` too, so the pnpm
resolution checks the path names pnpm; the argument separator can arrive
after a run script's preset arguments, not only first; and the toolchain
probe, parser, build commit, and tarball readers now have specs.
2026-09-21 16:04:08 +08:00

55 lines
2.1 KiB
TypeScript

/** Shared Tencent COS client construction for Desktop update objects. */
import COS from 'cos-nodejs-sdk-v5'
/** Region of every Desktop update bucket, present or future deployment. */
export const DESKTOP_COS_REGION = 'ap-beijing'
/** Request-level inactivity deadline for COS transfers, in milliseconds. */
const TRANSFER_TIMEOUT_MS = 900_000
/** Credentials for one COS client; values are never written to retained records. */
export interface DesktopCosCredentials {
readonly secretId: string
readonly secretKey: string
}
/** Request options the SDK exposes to `before-send` listeners. */
interface CosRequestOptions {
headers: Record<string, unknown>
}
/**
* Create a COS client whose writes cannot be repeated by the SDK.
*
* The SDK retries a failed request only while the request body is not a stream, so every caller
* supplies a stream with an explicit `ContentLength` and a precomputed `Content-MD5`. Host
* switching, redirect following, and clock-offset correction stay off so an ambiguous write is
* never sent to another endpoint. The SDK also injects an empty `Cache-Control` header when the
* caller names none; that header is removed here so uploading leaves cache policy to deployment
* infrastructure.
* @param credentials SecretId and SecretKey for the selected deployment.
* @param timeoutMs Request-level inactivity deadline; a caller that must not wait out a transfer passes its own.
* @returns A COS client that sends HTTPS requests to the region named by each call.
*/
export function createDesktopCos(credentials: DesktopCosCredentials, timeoutMs: number = TRANSFER_TIMEOUT_MS): COS {
const cos = new COS({
SecretId: credentials.secretId,
SecretKey: credentials.secretKey,
Protocol: 'https:',
KeepAlive: false,
FollowRedirect: false,
AutoSwitchHost: false,
CorrectClockSkew: false,
ChunkRetryTimes: 0,
Timeout: timeoutMs,
UploadCheckContentMd5: false,
})
cos.on('before-send', (options: CosRequestOptions) => {
for (const name of Object.keys(options.headers)) {
if (name.toLowerCase() === 'cache-control' && options.headers[name] === '') delete options.headers[name]
}
})
return cos
}