@duckmind/dm-darwin-x64 0.33.0 → 0.33.1
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/dm +0 -0
- package/extensions/.dm-extensions.json +1 -76
- package/package.json +1 -1
- package/theme/theme-alps.json +93 -0
- package/extensions/dm-chime/README.md +0 -11
- package/extensions/dm-chime/docs/protocols.md +0 -107
- package/extensions/dm-chime/index.ts +0 -205
- package/extensions/dm-chime/package.json +0 -33
- package/extensions/dm-phone/README.md +0 -24
- package/extensions/dm-phone/index.ts +0 -12
- package/extensions/dm-phone/node_modules/.package-lock.json +0 -29
- package/extensions/dm-phone/node_modules/ws/LICENSE +0 -20
- package/extensions/dm-phone/node_modules/ws/README.md +0 -548
- package/extensions/dm-phone/node_modules/ws/browser.js +0 -8
- package/extensions/dm-phone/node_modules/ws/index.js +0 -22
- package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +0 -131
- package/extensions/dm-phone/node_modules/ws/lib/constants.js +0 -19
- package/extensions/dm-phone/node_modules/ws/lib/event-target.js +0 -292
- package/extensions/dm-phone/node_modules/ws/lib/extension.js +0 -203
- package/extensions/dm-phone/node_modules/ws/lib/limiter.js +0 -55
- package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +0 -528
- package/extensions/dm-phone/node_modules/ws/lib/receiver.js +0 -760
- package/extensions/dm-phone/node_modules/ws/lib/sender.js +0 -607
- package/extensions/dm-phone/node_modules/ws/lib/stream.js +0 -161
- package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +0 -62
- package/extensions/dm-phone/node_modules/ws/lib/validation.js +0 -152
- package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +0 -562
- package/extensions/dm-phone/node_modules/ws/lib/websocket.js +0 -1407
- package/extensions/dm-phone/node_modules/ws/package.json +0 -70
- package/extensions/dm-phone/node_modules/ws/wrapper.mjs +0 -21
- package/extensions/dm-phone/package-lock.json +0 -66
- package/extensions/dm-phone/package.json +0 -35
- package/extensions/dm-phone/phone-session-pool.ts +0 -8
- package/extensions/dm-phone/public/app/attachments.js +0 -233
- package/extensions/dm-phone/public/app/autocomplete-controller.js +0 -81
- package/extensions/dm-phone/public/app/autocomplete.js +0 -135
- package/extensions/dm-phone/public/app/bindings.js +0 -178
- package/extensions/dm-phone/public/app/command-catalog.js +0 -76
- package/extensions/dm-phone/public/app/commands.js +0 -376
- package/extensions/dm-phone/public/app/constants.js +0 -60
- package/extensions/dm-phone/public/app/formatters.js +0 -131
- package/extensions/dm-phone/public/app/handlers.js +0 -442
- package/extensions/dm-phone/public/app/main.js +0 -6
- package/extensions/dm-phone/public/app/markdown.js +0 -105
- package/extensions/dm-phone/public/app/messages.js +0 -418
- package/extensions/dm-phone/public/app/sheet-actions.js +0 -113
- package/extensions/dm-phone/public/app/sheet-navigation.js +0 -19
- package/extensions/dm-phone/public/app/sheets-view.js +0 -287
- package/extensions/dm-phone/public/app/state.js +0 -95
- package/extensions/dm-phone/public/app/tool-rendering.js +0 -562
- package/extensions/dm-phone/public/app/transport.js +0 -176
- package/extensions/dm-phone/public/app/ui.js +0 -417
- package/extensions/dm-phone/public/app.js +0 -1
- package/extensions/dm-phone/public/icon.svg +0 -15
- package/extensions/dm-phone/public/index.html +0 -146
- package/extensions/dm-phone/public/manifest.webmanifest +0 -17
- package/extensions/dm-phone/public/styles.css +0 -1139
- package/extensions/dm-phone/public/sw.js +0 -78
- package/extensions/dm-phone/src/extension/duckmind-models.js +0 -264
- package/extensions/dm-phone/src/extension/phone-args.ts +0 -121
- package/extensions/dm-phone/src/extension/phone-paths.ts +0 -250
- package/extensions/dm-phone/src/extension/phone-quota.ts +0 -188
- package/extensions/dm-phone/src/extension/phone-runtime.ts +0 -154
- package/extensions/dm-phone/src/extension/phone-server-runtime.ts +0 -1217
- package/extensions/dm-phone/src/extension/phone-sessions.ts +0 -139
- package/extensions/dm-phone/src/extension/phone-static.ts +0 -30
- package/extensions/dm-phone/src/extension/phone-tailscale.ts +0 -148
- package/extensions/dm-phone/src/extension/phone-theme.ts +0 -85
- package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +0 -112
- package/extensions/dm-phone/src/extension/register-phone-extension.ts +0 -106
- package/extensions/dm-phone/src/extension/types.ts +0 -73
- package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +0 -882
- package/extensions/dm-phone/src/session-pool/session-pool.ts +0 -470
- package/extensions/dm-phone/src/session-pool/session-worker.ts +0 -739
- package/extensions/dm-phone/src/session-pool/types.ts +0 -111
- package/extensions/dm-phone/src/session-pool/utils.ts +0 -23
- package/extensions/dm-phone/test/duckmind-models.test.js +0 -147
- package/extensions/dm-thinking-timer/LICENSE +0 -21
- package/extensions/dm-thinking-timer/README.md +0 -7
- package/extensions/dm-thinking-timer/package.json +0 -20
- package/extensions/dm-thinking-timer/thinking-timer.ts +0 -250
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import type {
|
|
4
|
-
CodexUsageResponse,
|
|
5
|
-
PhoneQuotaResponse,
|
|
6
|
-
PhoneQuotaWindow,
|
|
7
|
-
RateLimitBucket,
|
|
8
|
-
UsageWindow,
|
|
9
|
-
} from "./types";
|
|
10
|
-
|
|
11
|
-
const agentDirFromEnv = process.env.DM_CODING_AGENT_DIR?.trim() || process.env.PI_CODING_AGENT_DIR?.trim();
|
|
12
|
-
const agentDir = agentDirFromEnv
|
|
13
|
-
? agentDirFromEnv
|
|
14
|
-
: join(process.env.HOME || process.env.USERPROFILE || process.cwd(), ".dm", "agent");
|
|
15
|
-
const authFile = join(agentDir, "auth.json");
|
|
16
|
-
const codexUsageUrl = "https://chatgpt.com/backend-api/wham/usage";
|
|
17
|
-
const sparkModelId = "gpt-5.3-codex-spark";
|
|
18
|
-
const sparkLimitName = "GPT-5.3-Codex-Spark";
|
|
19
|
-
const missingAuthErrorPrefix = "Missing openai-codex OAuth access/accountId";
|
|
20
|
-
|
|
21
|
-
function clampPercent(value: number): number {
|
|
22
|
-
return Math.min(100, Math.max(0, value));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function usedToLeftPercent(value: number | null | undefined): number | null {
|
|
26
|
-
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
27
|
-
return clampPercent(100 - value);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function asObject(value: unknown): Record<string, unknown> | null {
|
|
31
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
32
|
-
return value as Record<string, unknown>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalizeRateLimitBucket(value: unknown): RateLimitBucket | null {
|
|
36
|
-
const record = asObject(value);
|
|
37
|
-
if (!record) return null;
|
|
38
|
-
if (!("primary_window" in record || "secondary_window" in record || "limit_reached" in record || "allowed" in record)) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
return record as RateLimitBucket;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function extractSparkRateLimitFromEntry(value: unknown): RateLimitBucket | null {
|
|
45
|
-
const record = asObject(value);
|
|
46
|
-
if (!record) return null;
|
|
47
|
-
if (typeof record.limit_name !== "string" || record.limit_name.trim() !== sparkLimitName) return null;
|
|
48
|
-
return normalizeRateLimitBucket(record.rate_limit);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function findSparkRateLimitBucket(data: CodexUsageResponse): RateLimitBucket | null {
|
|
52
|
-
const additional = data.additional_rate_limits;
|
|
53
|
-
|
|
54
|
-
if (Array.isArray(additional)) {
|
|
55
|
-
for (const entry of additional) {
|
|
56
|
-
const bucket = extractSparkRateLimitFromEntry(entry);
|
|
57
|
-
if (bucket) return bucket;
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const additionalMap = asObject(additional);
|
|
63
|
-
if (!additionalMap) return null;
|
|
64
|
-
|
|
65
|
-
for (const value of Object.values(additionalMap)) {
|
|
66
|
-
const bucket = extractSparkRateLimitFromEntry(value);
|
|
67
|
-
if (bucket) return bucket;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function selectRateLimitBucket(data: CodexUsageResponse, modelId: string): RateLimitBucket | null {
|
|
74
|
-
if (modelId === sparkModelId) {
|
|
75
|
-
return findSparkRateLimitBucket(data);
|
|
76
|
-
}
|
|
77
|
-
return normalizeRateLimitBucket(data.rate_limit);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getResetSeconds(window: UsageWindow | null | undefined): number | null {
|
|
81
|
-
const resetAfterSeconds = window?.reset_after_seconds;
|
|
82
|
-
if (typeof resetAfterSeconds === "number" && !Number.isNaN(resetAfterSeconds)) {
|
|
83
|
-
return resetAfterSeconds;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const resetAt = window?.reset_at;
|
|
87
|
-
if (typeof resetAt !== "number" || Number.isNaN(resetAt)) return null;
|
|
88
|
-
|
|
89
|
-
const resetAtSeconds = resetAt > 100_000_000_000 ? resetAt / 1000 : resetAt;
|
|
90
|
-
return Math.max(0, resetAtSeconds - Date.now() / 1000);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function buildQuotaWindow(label: "5h" | "7d", window: UsageWindow | null | undefined): PhoneQuotaWindow | null {
|
|
94
|
-
const leftPercent = usedToLeftPercent(window?.used_percent);
|
|
95
|
-
if (leftPercent === null) return null;
|
|
96
|
-
|
|
97
|
-
const roundedLeftPercent = Math.round(leftPercent);
|
|
98
|
-
const roundedUsedPercent = Math.round(clampPercent(typeof window?.used_percent === "number" ? window.used_percent : 100 - leftPercent));
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
label,
|
|
102
|
-
leftPercent: roundedLeftPercent,
|
|
103
|
-
usedPercent: roundedUsedPercent,
|
|
104
|
-
resetAfterSeconds: getResetSeconds(window),
|
|
105
|
-
text: `${roundedLeftPercent}%`,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function shouldShowQuotaForModel(provider: string | null | undefined, modelId: string | null | undefined): boolean {
|
|
110
|
-
return provider === "openai-codex" && typeof modelId === "string" && /^gpt-/i.test(modelId);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function loadCodexAuthCredentials(): Promise<{ accessToken: string; accountId: string }> {
|
|
114
|
-
const authRaw = await readFile(authFile, "utf8");
|
|
115
|
-
const auth = JSON.parse(authRaw) as Record<
|
|
116
|
-
string,
|
|
117
|
-
| {
|
|
118
|
-
type?: string;
|
|
119
|
-
access?: string | null;
|
|
120
|
-
accountId?: string | null;
|
|
121
|
-
account_id?: string | null;
|
|
122
|
-
}
|
|
123
|
-
| undefined
|
|
124
|
-
>;
|
|
125
|
-
|
|
126
|
-
const codexEntry = auth["openai-codex"];
|
|
127
|
-
const authEntry = codexEntry?.type === "oauth" ? codexEntry : undefined;
|
|
128
|
-
const accessToken = authEntry?.access?.trim();
|
|
129
|
-
const accountId = (authEntry?.accountId ?? authEntry?.account_id)?.trim();
|
|
130
|
-
|
|
131
|
-
if (!accessToken || !accountId) {
|
|
132
|
-
throw new Error(`${missingAuthErrorPrefix} in ${authFile}`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return { accessToken, accountId };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async function requestCodexUsageJson(): Promise<CodexUsageResponse> {
|
|
139
|
-
const credentials = await loadCodexAuthCredentials();
|
|
140
|
-
const response = await fetch(codexUsageUrl, {
|
|
141
|
-
headers: {
|
|
142
|
-
accept: "*/*",
|
|
143
|
-
authorization: `Bearer ${credentials.accessToken}`,
|
|
144
|
-
"chatgpt-account-id": credentials.accountId,
|
|
145
|
-
"content-type": "application/json",
|
|
146
|
-
"user-agent": "codex-cli",
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
if (!response.ok) {
|
|
151
|
-
throw new Error(`Codex usage request failed (${response.status})`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return (await response.json()) as CodexUsageResponse;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export async function getQuotaForModel(provider: string | null | undefined, modelId: string | null | undefined): Promise<PhoneQuotaResponse> {
|
|
158
|
-
if (!shouldShowQuotaForModel(provider, modelId)) {
|
|
159
|
-
return {
|
|
160
|
-
visible: false,
|
|
161
|
-
limited: false,
|
|
162
|
-
primaryWindow: null,
|
|
163
|
-
secondaryWindow: null,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const usage = await requestCodexUsageJson();
|
|
169
|
-
const selectedBucket = selectRateLimitBucket(usage, modelId || "");
|
|
170
|
-
const primaryWindow = buildQuotaWindow("5h", selectedBucket?.primary_window);
|
|
171
|
-
const secondaryWindow = buildQuotaWindow("7d", selectedBucket?.secondary_window);
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
visible: Boolean(primaryWindow || secondaryWindow),
|
|
175
|
-
limited: selectedBucket?.limit_reached === true || selectedBucket?.allowed === false,
|
|
176
|
-
primaryWindow,
|
|
177
|
-
secondaryWindow,
|
|
178
|
-
};
|
|
179
|
-
} catch (error) {
|
|
180
|
-
return {
|
|
181
|
-
visible: false,
|
|
182
|
-
limited: false,
|
|
183
|
-
primaryWindow: null,
|
|
184
|
-
secondaryWindow: null,
|
|
185
|
-
error: error instanceof Error ? error.message : String(error),
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import type { PersistedPhoneRuntime } from "./types";
|
|
5
|
-
|
|
6
|
-
const runtimeStateDir = join(tmpdir(), "dm-phone-extension");
|
|
7
|
-
export const phoneControlStopPath = "/__dm_phone__/control/stop";
|
|
8
|
-
|
|
9
|
-
function normalizeControlHost(host: string) {
|
|
10
|
-
if (!host || host === "0.0.0.0") return "127.0.0.1";
|
|
11
|
-
if (host === "::" || host === "[::]") return "[::1]";
|
|
12
|
-
if (host.includes(":") && !host.startsWith("[")) return `[${host}]`;
|
|
13
|
-
return host;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function isLoopbackAddress(address: string | undefined | null) {
|
|
17
|
-
if (!address) return false;
|
|
18
|
-
return address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function buildControlUrl(host: string, port: number, controlToken: string, pathname = phoneControlStopPath) {
|
|
22
|
-
const url = new URL(`http://${normalizeControlHost(host)}:${port}`);
|
|
23
|
-
url.pathname = pathname;
|
|
24
|
-
url.searchParams.set("token", controlToken);
|
|
25
|
-
return url;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function getPersistedRuntimeStatePath(host: string, port: number) {
|
|
29
|
-
const hostKey = ["127.0.0.1", "localhost", "::1", "[::1]", "0.0.0.0", "::", "[::]"].includes(host)
|
|
30
|
-
? "local"
|
|
31
|
-
: encodeURIComponent(host);
|
|
32
|
-
return join(runtimeStateDir, `${hostKey}-${port}.json`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function readPersistedRuntimeState(host: string, port: number): Promise<PersistedPhoneRuntime | null> {
|
|
36
|
-
try {
|
|
37
|
-
const payload = await readFile(getPersistedRuntimeStatePath(host, port), "utf8");
|
|
38
|
-
const parsed = JSON.parse(payload);
|
|
39
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
40
|
-
if (typeof parsed.host !== "string" || typeof parsed.port !== "number" || typeof parsed.controlToken !== "string") {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
pid: typeof parsed.pid === "number" ? parsed.pid : 0,
|
|
46
|
-
host: parsed.host,
|
|
47
|
-
port: parsed.port,
|
|
48
|
-
controlToken: parsed.controlToken,
|
|
49
|
-
startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : "",
|
|
50
|
-
};
|
|
51
|
-
} catch (error: any) {
|
|
52
|
-
if (error?.code === "ENOENT") return null;
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function writePersistedRuntimeState(host: string, port: number, controlToken: string) {
|
|
58
|
-
const nextPath = getPersistedRuntimeStatePath(host, port);
|
|
59
|
-
await mkdir(runtimeStateDir, { recursive: true });
|
|
60
|
-
await writeFile(nextPath, JSON.stringify({
|
|
61
|
-
pid: process.pid,
|
|
62
|
-
host,
|
|
63
|
-
port,
|
|
64
|
-
controlToken,
|
|
65
|
-
startedAt: new Date().toISOString(),
|
|
66
|
-
} satisfies PersistedPhoneRuntime, null, 2), "utf8");
|
|
67
|
-
return nextPath;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export async function removePersistedRuntimeState(pathToRemove: string | null) {
|
|
71
|
-
if (!pathToRemove) return;
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
await unlink(pathToRemove);
|
|
75
|
-
} catch (error: any) {
|
|
76
|
-
if (error?.code !== "ENOENT") {
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function isProcessRunning(pid: number) {
|
|
83
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
84
|
-
try {
|
|
85
|
-
process.kill(pid, 0);
|
|
86
|
-
return true;
|
|
87
|
-
} catch {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function waitForPersistedRuntimeShutdown(runtime: PersistedPhoneRuntime, timeoutMs = 2000) {
|
|
93
|
-
const deadline = Date.now() + timeoutMs;
|
|
94
|
-
while (Date.now() < deadline) {
|
|
95
|
-
await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));
|
|
96
|
-
try {
|
|
97
|
-
const healthUrl = buildControlUrl(runtime.host, runtime.port, runtime.controlToken, "/api/health");
|
|
98
|
-
healthUrl.searchParams.delete("token");
|
|
99
|
-
await fetch(healthUrl, { method: "GET" });
|
|
100
|
-
} catch {
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function stopPersistedRuntime(host: string, port: number) {
|
|
108
|
-
const runtimeStatePath = getPersistedRuntimeStatePath(host, port);
|
|
109
|
-
const runtime = await readPersistedRuntimeState(host, port);
|
|
110
|
-
if (!runtime) {
|
|
111
|
-
return { stopped: false, found: false, message: "No running DM Phone instance was found for this port." };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const response = await fetch(buildControlUrl(runtime.host, runtime.port, runtime.controlToken), {
|
|
116
|
-
method: "POST",
|
|
117
|
-
});
|
|
118
|
-
const payload = await response.json().catch(() => null);
|
|
119
|
-
if (!response.ok || !payload || payload.ok !== true) {
|
|
120
|
-
return {
|
|
121
|
-
stopped: false,
|
|
122
|
-
found: true,
|
|
123
|
-
message: `DM Phone stop request failed with HTTP ${response.status}.`,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const stopped = await waitForPersistedRuntimeShutdown(runtime);
|
|
128
|
-
if (!stopped) {
|
|
129
|
-
return {
|
|
130
|
-
stopped: false,
|
|
131
|
-
found: true,
|
|
132
|
-
message: "DM Phone received the stop request but is still shutting down. Try /phone-start again in a moment.",
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
await removePersistedRuntimeState(runtimeStatePath);
|
|
137
|
-
return { stopped: true, found: true, message: "Stopped the other DM Phone instance." };
|
|
138
|
-
} catch (error) {
|
|
139
|
-
if (!isProcessRunning(runtime.pid)) {
|
|
140
|
-
await removePersistedRuntimeState(runtimeStatePath);
|
|
141
|
-
return {
|
|
142
|
-
stopped: false,
|
|
143
|
-
found: true,
|
|
144
|
-
message: "Removed stale DM Phone runtime state. Nothing was listening anymore.",
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
stopped: false,
|
|
150
|
-
found: true,
|
|
151
|
-
message: error instanceof Error ? error.message : String(error),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|