@dyyz1993/codenomad 0.15.8 → 0.15.9

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.
@@ -3,6 +3,18 @@ function prettyLabel(p) {
3
3
  const last = parts[parts.length - 1] || p;
4
4
  return last || p;
5
5
  }
6
+ function readUiBinariesSync(settings) {
7
+ const ui = settings.getOwnerSync("state", "ui");
8
+ const list = ui?.opencodeBinaries;
9
+ if (!Array.isArray(list))
10
+ return [];
11
+ return list.filter((item) => item && typeof item === "object" && typeof item.path === "string");
12
+ }
13
+ function readDefaultBinaryPathSync(settings) {
14
+ const server = settings.getOwnerSync("config", "server");
15
+ const value = server?.opencodeBinary;
16
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
17
+ }
6
18
  async function readUiBinaries(settings) {
7
19
  const ui = await settings.getOwner("state", "ui");
8
20
  const list = ui?.opencodeBinaries;
@@ -20,11 +32,11 @@ export class BinaryResolver {
20
32
  this.settings = settings;
21
33
  }
22
34
  async list() {
23
- return readUiBinaries(this.settings);
35
+ return readUiBinariesSync(this.settings) ?? readUiBinaries(this.settings);
24
36
  }
25
37
  async resolveDefault() {
26
- const binaries = await this.list();
27
- const configuredDefault = await readDefaultBinaryPath(this.settings);
38
+ const binaries = readUiBinariesSync(this.settings) ?? (await readUiBinaries(this.settings));
39
+ const configuredDefault = readDefaultBinaryPathSync(this.settings) ?? (await readDefaultBinaryPath(this.settings));
28
40
  const fallback = binaries[0]?.path;
29
41
  const path = configuredDefault ?? fallback ?? "opencode";
30
42
  const entry = binaries.find((b) => b.path === path);
@@ -51,6 +51,27 @@ export class SettingsService {
51
51
  this.configStore = new YamlDocStore(location.configYamlPath, logger.child({ component: "settings-config" }));
52
52
  this.stateStore = new YamlDocStore(location.stateYamlPath, logger.child({ component: "settings-state" }));
53
53
  }
54
+ getDocSync(kind) {
55
+ const store = kind === "config" ? this.configStore : this.stateStore;
56
+ const data = store.getSync();
57
+ if (!data)
58
+ return undefined;
59
+ if (kind === "config")
60
+ return normalizeConfigDoc(data);
61
+ return data;
62
+ }
63
+ getOwnerSync(kind, owner) {
64
+ if (kind !== "config") {
65
+ const data = this.stateStore.getSync();
66
+ return data ? (isPlainObject(data?.[owner]) ? data?.[owner] : {}) : undefined;
67
+ }
68
+ const doc = this.getDocSync("config");
69
+ if (!doc)
70
+ return undefined;
71
+ if (owner === "server")
72
+ return normalizeServerConfigOwner(doc.server);
73
+ return doc?.[owner];
74
+ }
54
75
  async getDoc(kind) {
55
76
  if (kind !== "config") {
56
77
  return this.stateStore.get();
@@ -21,6 +21,11 @@ export class YamlDocStore {
21
21
  this.cache = {};
22
22
  this.loaded = false;
23
23
  }
24
+ getSync() {
25
+ if (this.loaded)
26
+ return this.cache;
27
+ return undefined;
28
+ }
24
29
  async load() {
25
30
  if (this.loaded) {
26
31
  return this.cache;
@@ -149,7 +149,7 @@ export class SideCarManager {
149
149
  await this.options.settings.mergePatchOwner("config", "server", { sidecars });
150
150
  }
151
151
  async loadConfiguredSideCars() {
152
- const serverConfig = await this.options.settings.getOwner("config", "server");
152
+ const serverConfig = (this.options.settings.getOwnerSync("config", "server") ?? await this.options.settings.getOwner("config", "server"));
153
153
  const list = Array.isArray(serverConfig?.sidecars) ? serverConfig.sidecars : [];
154
154
  const records = [];
155
155
  for (const item of list) {
@@ -42,7 +42,24 @@ export class SpeechService {
42
42
  logger: this.logger.child({ provider: settings.provider }),
43
43
  });
44
44
  }
45
+ resolveSettingsSync() {
46
+ const owner = this.settings.getOwnerSync("config", "server") ?? {};
47
+ const parsed = ServerSpeechSettingsSchema.parse(owner);
48
+ const speech = parsed.speech ?? {};
49
+ return {
50
+ provider: speech.provider?.trim() || DEFAULT_PROVIDER,
51
+ apiKey: speech.apiKey?.trim() || process.env.OPENAI_API_KEY,
52
+ baseUrl: speech.baseUrl?.trim() || process.env.OPENAI_BASE_URL || undefined,
53
+ sttModel: speech.sttModel?.trim() || DEFAULT_STT_MODEL,
54
+ ttsModel: speech.ttsModel?.trim() || DEFAULT_TTS_MODEL,
55
+ ttsVoice: speech.ttsVoice?.trim() || DEFAULT_TTS_VOICE,
56
+ ttsFormat: speech.ttsFormat ?? DEFAULT_TTS_FORMAT,
57
+ };
58
+ }
45
59
  async resolveSettings() {
60
+ const syncResult = this.resolveSettingsSync();
61
+ if (syncResult.apiKey || process.env.OPENAI_API_KEY)
62
+ return syncResult;
46
63
  const parsed = ServerSpeechSettingsSchema.parse(await this.settings.getOwner("config", "server") ?? {});
47
64
  const speech = parsed.speech ?? {};
48
65
  return {
@@ -15,6 +15,8 @@ const INSTANCE_HOST = "127.0.0.1";
15
15
  const STREAM_AGENT = new UndiciAgent({ bodyTimeout: 0, headersTimeout: 0 });
16
16
  const RECONNECT_DELAY_MS = 1000;
17
17
  const LOG_THROTTLE_MS = 50;
18
+ const ACTIVITY_THROTTLE_MS = 1000;
19
+ const activityThrottleTime = new Map();
18
20
  export class InstanceEventBridge {
19
21
  constructor(options) {
20
22
  this.options = options;
@@ -62,6 +64,7 @@ export class InstanceEventBridge {
62
64
  active.controller.abort();
63
65
  this.streams.delete(workspaceId);
64
66
  this.lastLogTime.delete(workspaceId);
67
+ activityThrottleTime.delete(workspaceId);
65
68
  this.publishStatus(workspaceId, "disconnected", reason);
66
69
  }
67
70
  async runStream(workspaceId, signal) {
@@ -124,7 +127,12 @@ export class InstanceEventBridge {
124
127
  return buffer;
125
128
  }
126
129
  processChunk(chunk, workspaceId) {
127
- this.options.workspaceManager.recordActivity(workspaceId);
130
+ const now = Date.now();
131
+ const lastTime = activityThrottleTime.get(workspaceId) ?? 0;
132
+ if (now - lastTime >= ACTIVITY_THROTTLE_MS) {
133
+ this.options.workspaceManager.recordActivity(workspaceId);
134
+ activityThrottleTime.set(workspaceId, now);
135
+ }
128
136
  const lines = chunk.split(/\r?\n/);
129
137
  const dataLines = [];
130
138
  for (const line of lines) {
@@ -103,7 +103,7 @@ export class WorkspaceManager {
103
103
  };
104
104
  this.workspaces.set(id, descriptor);
105
105
  this.options.eventBus.publish({ type: "workspace.created", workspace: descriptor });
106
- const serverConfig = await this.options.settings.getOwner("config", "server");
106
+ const serverConfig = this.options.settings.getOwnerSync("config", "server") ?? {};
107
107
  const envVars = serverConfig?.environmentVariables;
108
108
  const userEnvironment = envVars && typeof envVars === "object" && !Array.isArray(envVars) ? envVars : {};
109
109
  const serverBaseUrl = this.options.getServerBaseUrl();
@@ -480,7 +480,7 @@ export class WorkspaceManager {
480
480
  workspace.updatedAt = new Date().toISOString();
481
481
  const binary = await this.options.binaryResolver.resolveDefault();
482
482
  const resolvedBinaryPath = await this.resolveBinaryPath(binary.path);
483
- const serverConfig = await this.options.settings.getOwner("config", "server");
483
+ const serverConfig = this.options.settings.getOwnerSync("config", "server") ?? {};
484
484
  const envVars = serverConfig?.environmentVariables;
485
485
  const userEnvironment = envVars && typeof envVars === "object" && !Array.isArray(envVars) ? envVars : {};
486
486
  const serverBaseUrl = this.options.getServerBaseUrl();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyyz1993/codenomad",
3
- "version": "0.15.8",
3
+ "version": "0.15.9",
4
4
  "description": "CodeNomad Server",
5
5
  "license": "MIT",
6
6
  "author": {
package/public/index.html CHANGED
@@ -30,8 +30,8 @@
30
30
  <link rel="modulepreload" crossorigin href="/assets/git-diff-vendor-CSgooKT_.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/monaco-viewer-YY9yfE-p.js">
32
32
  <link rel="modulepreload" crossorigin href="/assets/index-BCGPLzO4.js">
33
- <link rel="stylesheet" crossorigin href="/assets/git-diff-vendor-HAZkIolJ.css">
34
33
  <link rel="stylesheet" crossorigin href="/assets/index-DXI9DoVB.css">
34
+ <link rel="stylesheet" crossorigin href="/assets/git-diff-vendor-HAZkIolJ.css">
35
35
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script>
36
36
  <meta name="theme-color" content="#1a1a1a">
37
37
  <link rel="icon" href="/favicon.ico" sizes="48x48">
@@ -20,8 +20,8 @@
20
20
  <link rel="modulepreload" crossorigin href="/assets/git-diff-vendor-CSgooKT_.js">
21
21
  <link rel="modulepreload" crossorigin href="/assets/monaco-viewer-YY9yfE-p.js">
22
22
  <link rel="modulepreload" crossorigin href="/assets/index-BCGPLzO4.js">
23
- <link rel="stylesheet" crossorigin href="/assets/index-DXI9DoVB.css">
24
23
  <link rel="stylesheet" crossorigin href="/assets/git-diff-vendor-HAZkIolJ.css">
24
+ <link rel="stylesheet" crossorigin href="/assets/index-DXI9DoVB.css">
25
25
  <link rel="stylesheet" crossorigin href="/assets/loading-CmEVQgyj.css">
26
26
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script>
27
27
  <meta name="theme-color" content="#1a1a1a">