Explorar o código

feat(util): configure package publishing

Dax Raad hai 3 semanas
pai
achega
674ed2a8df

+ 0 - 1
bun.lock

@@ -967,7 +967,6 @@
         "@effect/platform-node": "catalog:",
         "@npmcli/arborist": "catalog:",
         "@npmcli/config": "10.8.1",
-        "@opencode-ai/ai": "workspace:*",
         "@opentelemetry/api": "1.9.0",
         "@opentelemetry/context-async-hooks": "2.6.1",
         "@opentelemetry/exporter-trace-otlp-http": "0.214.0",

+ 13 - 0
packages/core/src/effect/app-node-platform.ts

@@ -0,0 +1,13 @@
+import { LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
+import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
+import { httpClient } from "@opencode-ai/util/effect/app-node-platform"
+
+export const requestExecutor = makeGlobalNode({
+  service: RequestExecutor.Service,
+  layer: RequestExecutor.layer,
+  deps: [httpClient],
+})
+
+export const llmClient = makeGlobalNode({ service: LLMClient.Service, layer: LLMClient.layer, deps: [requestExecutor] })
+
+export * as LayerNodePlatform from "./app-node-platform"

+ 1 - 1
packages/core/src/generate.ts

@@ -4,7 +4,7 @@ import { LLM, LLMClient, LLMError } from "@opencode-ai/ai"
 import { Context, Effect, Layer, Schema } from "effect"
 import { Catalog } from "./catalog"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "./effect/app-node-platform"
 import { Integration } from "./integration"
 import { ModelV2 } from "./model"
 import { SessionRunnerModel } from "./session/runner/model"

+ 1 - 1
packages/core/src/session/compaction.ts

@@ -6,7 +6,7 @@ import { Context, Effect, Layer, Stream } from "effect"
 import { Config } from "../config"
 import { EventV2 } from "../event"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "../effect/app-node-platform"
 import { SessionEvent } from "./event"
 import type { SessionMessage } from "./message"
 import { SessionModelHeaders } from "./model-headers"

+ 1 - 1
packages/core/src/session/generate-node.ts

@@ -4,7 +4,7 @@ import { LLM, LLMClient, Message, SystemPart } from "@opencode-ai/ai"
 import { Effect, Layer } from "effect"
 import { Database } from "../database/database"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "../effect/app-node-platform"
 import { PluginHooks } from "../plugin/hooks"
 import { SessionContext } from "./context"
 import { SessionGenerate } from "./generate"

+ 1 - 1
packages/core/src/session/runner/llm.ts

@@ -22,7 +22,7 @@ import { Service } from "./index"
 import { createLLMEventPublisher } from "./publish-llm-event"
 import { Snapshot } from "../../snapshot"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "../../effect/app-node-platform"
 import { StepFailedError } from "../error"
 import { toSessionError } from "../to-session-error"
 import { SessionRunnerRetry } from "./retry"

+ 1 - 1
packages/core/src/session/title.ts

@@ -6,7 +6,7 @@ import { AgentV2 } from "../agent"
 import { Database } from "../database/database"
 import { EventV2 } from "../event"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "../effect/app-node-platform"
 import { SessionEvent } from "./event"
 import { SessionHistory } from "./history"
 import { SessionModelHeaders } from "./model-headers"

+ 1 - 1
packages/core/test/session-compaction.test.ts

@@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
 import { Config } from "@opencode-ai/core/config"
 import { Database } from "@opencode-ai/core/database/database"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { EventTable } from "@opencode-ai/core/event/sql"

+ 1 - 1
packages/core/test/session-generate.test.ts

@@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
 import { AgentV2 } from "@opencode-ai/core/agent"
 import { Database } from "@opencode-ai/core/database/database"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { EventTable } from "@opencode-ai/core/event/sql"

+ 1 - 1
packages/core/test/session-runner-recorded.test.ts

@@ -4,7 +4,7 @@ import { Auth, LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
 import { Catalog } from "@opencode-ai/core/catalog"
 import { Database } from "@opencode-ai/core/database/database"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
-import { LayerNodePlatform } from "@opencode-ai/util/effect/app-node-platform"
+import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { EventTable } from "@opencode-ai/core/event/sql"

+ 1 - 1
packages/core/test/session-runner.test.ts

@@ -19,7 +19,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
 import { Database } from "@opencode-ai/core/database/database"
 import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
-import { LayerNodePlatform } from "@opencode-ai/util/effect/app-node-platform"
+import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { InstallationVersion } from "@opencode-ai/util/installation/version"

+ 1 - 1
packages/core/test/session-title.test.ts

@@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
 import { AgentV2 } from "@opencode-ai/core/agent"
 import { Database } from "@opencode-ai/core/database/database"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
-import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
+import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { SessionEvent } from "@opencode-ai/core/session/event"

+ 12 - 2
packages/util/package.json

@@ -2,9 +2,19 @@
   "$schema": "https://json.schemastore.org/package.json",
   "name": "@opencode-ai/util",
   "version": "1.18.3",
-  "private": true,
   "type": "module",
   "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/anomalyco/opencode.git",
+    "directory": "packages/util"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "files": [
+    "dist"
+  ],
   "exports": {
     "./effect/layer-node": "./src/effect/layer-node.ts",
     "./*": "./src/*.ts"
@@ -17,6 +27,7 @@
     }
   },
   "scripts": {
+    "build": "bun run script/build.ts",
     "typecheck": "tsgo --noEmit"
   },
   "dependencies": {
@@ -24,7 +35,6 @@
     "@effect/platform-node": "catalog:",
     "@npmcli/arborist": "catalog:",
     "@npmcli/config": "10.8.1",
-    "@opencode-ai/ai": "workspace:*",
     "@opentelemetry/api": "1.9.0",
     "@opentelemetry/context-async-hooks": "2.6.1",
     "@opentelemetry/exporter-trace-otlp-http": "0.214.0",

+ 9 - 0
packages/util/script/build.ts

@@ -0,0 +1,9 @@
+#!/usr/bin/env bun
+
+import { $ } from "bun"
+import { fileURLToPath } from "node:url"
+
+process.chdir(fileURLToPath(new URL("..", import.meta.url)))
+
+await $`rm -rf dist`
+await $`bun tsc -p tsconfig.build.json`

+ 49 - 0
packages/util/script/publish.ts

@@ -0,0 +1,49 @@
+#!/usr/bin/env bun
+
+import { Script } from "@opencode-ai/script"
+import { $ } from "bun"
+import { rm } from "node:fs/promises"
+import { fileURLToPath } from "node:url"
+
+process.chdir(fileURLToPath(new URL("..", import.meta.url)))
+
+const originalText = await Bun.file("package.json").text()
+const pkg = JSON.parse(originalText) as {
+  name: string
+  version: string
+  exports: Record<string, string | { import: string; types: string }>
+  imports: Record<string, Record<string, string>>
+}
+const tarball = `${pkg.name.replace("@", "").replace("/", "-")}-${pkg.version}.tgz`
+
+if ((await $`npm view ${pkg.name}@${pkg.version} version`.nothrow()).exitCode === 0) {
+  console.log(`already published ${pkg.name}@${pkg.version}`)
+  process.exit(0)
+}
+
+const output = (value: string, extension: ".js" | ".d.ts") =>
+  value.replace("./src/", "./dist/").replace(/\.ts$/, extension)
+
+try {
+  await $`bun run typecheck`
+  await $`bun run build`
+  pkg.exports = Object.fromEntries(
+    Object.entries(pkg.exports).map(([key, value]) => {
+      if (typeof value !== "string") return [key, value]
+      return [key, { import: output(value, ".js"), types: output(value, ".d.ts") }]
+    }),
+  )
+  pkg.imports = Object.fromEntries(
+    Object.entries(pkg.imports).map(([key, conditions]) => [
+      key,
+      Object.fromEntries(Object.entries(conditions).map(([condition, value]) => [condition, output(value, ".js")])),
+    ]),
+  )
+  await Bun.write("package.json", JSON.stringify(pkg, null, 2) + "\n")
+  await rm(tarball, { force: true })
+  await $`bun pm pack`
+  await $`npm publish ${tarball} --tag ${Script.channel} --access public`
+} finally {
+  await Bun.write("package.json", originalText)
+  await rm(tarball, { force: true })
+}

+ 3 - 3
packages/util/src/cross-spawn-spawner.ts

@@ -15,8 +15,8 @@ import {
 import * as NodeChildProcess from "node:child_process"
 import { PassThrough } from "node:stream"
 import launch from "cross-spawn"
-import { makeGlobalNode } from "./effect/app-node"
-import { filesystem, path } from "./effect/app-node-platform"
+import { makeGlobalNode } from "./effect/app-node.js"
+import { filesystem, path } from "./effect/app-node-platform.js"
 
 const toError = (err: unknown): Error => (err instanceof globalThis.Error ? err : new globalThis.Error(String(err)))
 
@@ -495,4 +495,4 @@ const layer: Layer.Layer<ChildProcessSpawner, never, FileSystem.FileSystem | Pat
 
 export const node = makeGlobalNode({ service: ChildProcessSpawner, layer, deps: [filesystem, path] })
 
-export * as CrossSpawnSpawner from "./cross-spawn-spawner"
+export * as CrossSpawnSpawner from "./cross-spawn-spawner.js"

+ 4 - 4
packages/util/src/effect-flock.ts

@@ -4,10 +4,10 @@ import { randomUUID } from "crypto"
 import { Context, Effect, Function, Layer, Option, Schedule, Schema } from "effect"
 import type { FileSystem, Scope } from "effect"
 import type { PlatformError } from "effect/PlatformError"
-import { FSUtil } from "./fs-util"
-import { Global } from "./global"
-import { makeGlobalNode } from "./effect/app-node"
-import { Hash } from "./hash"
+import { FSUtil } from "./fs-util.js"
+import { Global } from "./global.js"
+import { makeGlobalNode } from "./effect/app-node.js"
+import { Hash } from "./hash.js"
 
 export namespace EffectFlock {
   // ---------------------------------------------------------------------------

+ 2 - 9
packages/util/src/effect/app-node-platform.ts

@@ -1,17 +1,10 @@
 import { NodeFileSystem, NodePath } from "@effect/platform-node"
-import { LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
 import { FileSystem, Path } from "effect"
 import { FetchHttpClient, HttpClient } from "effect/unstable/http"
-import { makeGlobalNode } from "./app-node"
+import { makeGlobalNode } from "./app-node.js"
 
 export const filesystem = makeGlobalNode({ service: FileSystem.FileSystem, layer: NodeFileSystem.layer, deps: [] })
 export const path = makeGlobalNode({ service: Path.Path, layer: NodePath.layer, deps: [] })
 export const httpClient = makeGlobalNode({ service: HttpClient.HttpClient, layer: FetchHttpClient.layer, deps: [] })
-export const requestExecutor = makeGlobalNode({
-  service: RequestExecutor.Service,
-  layer: RequestExecutor.layer,
-  deps: [httpClient],
-})
-export const llmClient = makeGlobalNode({ service: LLMClient.Service, layer: LLMClient.layer, deps: [requestExecutor] })
 
-export * as LayerNodePlatform from "./app-node-platform"
+export * as LayerNodePlatform from "./app-node-platform.js"

+ 2 - 2
packages/util/src/effect/app-node.ts

@@ -1,4 +1,4 @@
-import { LayerNode } from "./layer-node"
+import { LayerNode } from "./layer-node.js"
 
 export const tags = LayerNode.tags({
   location: ["global"],
@@ -11,4 +11,4 @@ export type LocationNode<A, E = never> = LayerNode.Node<A, E, (typeof tags.value
 export const makeGlobalNode = tags.make("global")
 export const makeLocationNode = tags.make("location")
 
-export * as Node from "./app-node"
+export * as Node from "./app-node.js"

+ 1 - 1
packages/util/src/effect/layer-node.ts

@@ -330,4 +330,4 @@ function flatten(node: AnyNode): readonly AnyNode[] {
   return node.kind === "group" ? node.dependencies.flatMap(flatten) : [node]
 }
 
-export * as LayerNode from "./layer-node"
+export * as LayerNode from "./layer-node.js"

+ 2 - 2
packages/util/src/effect/runtime.ts

@@ -1,6 +1,6 @@
 import { Layer, type Context, ManagedRuntime, type Effect } from "effect"
-import { memoMap } from "./memo-map"
-import { Observability } from "../observability"
+import { memoMap } from "./memo-map.js"
+import { Observability } from "../observability.js"
 
 export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
   let rt: ManagedRuntime.ManagedRuntime<I, E> | undefined

+ 1 - 1
packages/util/src/flock.ts

@@ -2,7 +2,7 @@ import path from "path"
 import os from "os"
 import { randomBytes, randomUUID } from "crypto"
 import { mkdir, readFile, rm, stat, utimes, writeFile } from "fs/promises"
-import { Hash } from "./hash"
+import { Hash } from "./hash.js"
 import { Effect } from "effect"
 
 export type FlockGlobal = {

+ 4 - 4
packages/util/src/fs-util.ts

@@ -5,10 +5,10 @@ import { readdir } from "fs/promises"
 import { lookup } from "mime-types"
 import { Context, Effect, FileSystem, Layer, Schema } from "effect"
 import type { PlatformError } from "effect/PlatformError"
-import { Glob } from "./glob"
-import { serviceUse } from "./effect/service-use"
-import { makeGlobalNode } from "./effect/app-node"
-import { filesystem } from "./effect/app-node-platform"
+import { Glob } from "./glob.js"
+import { serviceUse } from "./effect/service-use.js"
+import { makeGlobalNode } from "./effect/app-node.js"
+import { filesystem } from "./effect/app-node-platform.js"
 
 export namespace FSUtil {
   export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {

+ 3 - 3
packages/util/src/global.ts

@@ -3,8 +3,8 @@ import fs from "fs/promises"
 import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
 import os from "os"
 import { Context, Effect, Layer } from "effect"
-import { Flock } from "./flock"
-import { makeGlobalNode } from "./effect/app-node"
+import { Flock } from "./flock.js"
+import { makeGlobalNode } from "./effect/app-node.js"
 
 const app = "opencode"
 const data = path.join(xdgData!, app)
@@ -83,4 +83,4 @@ export const layerWith = (input: Partial<Interface>) =>
     Effect.sync(() => Service.of(make(input))),
   )
 
-export * as Global from "./global"
+export * as Global from "./global.js"

+ 1 - 1
packages/util/src/npm-config.ts

@@ -1,4 +1,4 @@
-export * as NpmConfig from "./npm-config"
+export * as NpmConfig from "./npm-config.js"
 
 import { fileURLToPath } from "url"
 // @ts-expect-error npm does not publish types for this internal config API.

+ 9 - 9
packages/util/src/npm.ts

@@ -1,17 +1,17 @@
-export * as Npm from "./npm"
+export * as Npm from "./npm.js"
 
 import path from "path"
 import npa from "npm-package-arg"
 import { Effect, Schema, Context, Layer, Option, FileSystem } from "effect"
 import { NodeFileSystem } from "@effect/platform-node"
-import { FSUtil } from "./fs-util"
-import { Global } from "./global"
-import { EffectFlock } from "./effect-flock"
-import { makeGlobalNode } from "./effect/app-node"
-import { filesystem } from "./effect/app-node-platform"
-import { LayerNode } from "./effect/layer-node"
-import { makeRuntime } from "./effect/runtime"
-import { NpmConfig } from "./npm-config"
+import { FSUtil } from "./fs-util.js"
+import { Global } from "./global.js"
+import { EffectFlock } from "./effect-flock.js"
+import { makeGlobalNode } from "./effect/app-node.js"
+import { filesystem } from "./effect/app-node-platform.js"
+import { LayerNode } from "./effect/layer-node.js"
+import { makeRuntime } from "./effect/runtime.js"
+import { NpmConfig } from "./npm-config.js"
 import { resolveModule } from "#runtime-import"
 
 export class InstallFailedError extends Schema.TaggedErrorClass<InstallFailedError>()("NpmInstallFailedError", {

+ 4 - 4
packages/util/src/observability.ts

@@ -1,12 +1,12 @@
-export * as Observability from "./observability"
+export * as Observability from "./observability.js"
 
 import { NodeFileSystem } from "@effect/platform-node"
-import { LayerNode } from "./effect/layer-node"
+import { LayerNode } from "./effect/layer-node.js"
 import { Effect, Layer, Logger, References, Schema } from "effect"
 import { FetchHttpClient } from "effect/unstable/http"
 import { OtlpSerialization } from "effect/unstable/observability"
-import { Logging } from "./observability/logging"
-import { Otlp } from "./observability/otlp"
+import { Logging } from "./observability/logging.js"
+import { Otlp } from "./observability/otlp.js"
 
 export const Options = Schema.Struct({
   endpoint: Schema.optional(Schema.String),

+ 4 - 4
packages/util/src/observability/logging.ts

@@ -1,8 +1,8 @@
 import { Formatter, Logger, type LogLevel } from "effect"
 import path from "path"
-import { Global } from "../global"
-import { InstallationChannel, InstallationLocal } from "../installation/version"
-import { runID } from "./shared"
+import { Global } from "../global.js"
+import { InstallationChannel, InstallationLocal } from "../installation/version.js"
+import { runID } from "./shared.js"
 
 function formatter(id: string = runID) {
   return Logger.map(Logger.formatStructured, (output) => {
@@ -74,4 +74,4 @@ export function loggers() {
   return process.env.OPENCODE_PRINT_LOGS === "1" ? [fileLogger(), stderrLogger] : [fileLogger()]
 }
 
-export * as Logging from "./logging"
+export * as Logging from "./logging.js"

+ 3 - 3
packages/util/src/observability/otlp.ts

@@ -1,7 +1,7 @@
 import { Layer } from "effect"
 import { OtlpLogger } from "effect/unstable/observability"
-import { InstallationChannel, InstallationVersion } from "../installation/version"
-import { runID } from "./shared"
+import { InstallationChannel, InstallationVersion } from "../installation/version.js"
+import { runID } from "./shared.js"
 
 export interface Options {
   readonly endpoint?: string
@@ -87,4 +87,4 @@ export async function tracingLayer(options?: Options) {
   }))
 }
 
-export * as Otlp from "./otlp"
+export * as Otlp from "./otlp.js"

+ 3 - 3
packages/util/src/process.ts

@@ -2,8 +2,8 @@ import { Context, Duration, Effect, Fiber, Layer, Schema, Stream } from "effect"
 import type { PlatformError } from "effect/PlatformError"
 import { ChildProcess } from "effect/unstable/process"
 import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
-import { CrossSpawnSpawner } from "./cross-spawn-spawner"
-import { makeGlobalNode } from "./effect/app-node"
+import { CrossSpawnSpawner } from "./cross-spawn-spawner.js"
+import { makeGlobalNode } from "./effect/app-node.js"
 
 export class AppProcessError extends Schema.TaggedErrorClass<AppProcessError>()("AppProcessError", {
   command: Schema.String,
@@ -258,4 +258,4 @@ const layer = Layer.effect(
 
 export const node = makeGlobalNode({ service: Service, layer: layer, deps: [CrossSpawnSpawner.node] })
 
-export * as AppProcess from "./process"
+export * as AppProcess from "./process.js"

+ 11 - 0
packages/util/tsconfig.build.json

@@ -0,0 +1,11 @@
+{
+  "$schema": "https://json.schemastore.org/tsconfig",
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "rootDir": "src",
+    "outDir": "dist",
+    "noEmit": false,
+    "declaration": true
+  },
+  "include": ["src"]
+}

+ 5 - 0
packages/util/tsconfig.json

@@ -2,6 +2,11 @@
   "$schema": "https://json.schemastore.org/tsconfig",
   "extends": "@tsconfig/bun/tsconfig.json",
   "compilerOptions": {
+    "lib": ["ESNext", "DOM", "DOM.Iterable"],
+    "module": "NodeNext",
+    "moduleResolution": "NodeNext",
+    "allowImportingTsExtensions": false,
+    "allowJs": false,
     "noUncheckedIndexedAccess": false
   }
 }

+ 3 - 0
script/publish.ts

@@ -41,6 +41,9 @@ await $`bun ./packages/schema/script/publish.ts`
 console.log("\n=== ai ===\n")
 await $`bun ./packages/ai/script/publish.ts`
 
+console.log("\n=== util ===\n")
+await $`bun ./packages/util/script/publish.ts`
+
 console.log("\n=== protocol ===\n")
 await $`bun ./packages/protocol/script/publish.ts`