@useorgx/openclaw-plugin 0.4.6 → 0.4.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.
Files changed (137) hide show
  1. package/README.md +310 -24
  2. package/dashboard/dist/assets/B5NEElEI.css +1 -0
  3. package/dashboard/dist/assets/BhapSNAs.js +215 -0
  4. package/dashboard/dist/assets/iFdvE7lx.js +1 -0
  5. package/dashboard/dist/assets/jRJsmpYM.js +1 -0
  6. package/dashboard/dist/index.html +2 -2
  7. package/dist/activity-actor-fields.d.ts +3 -0
  8. package/dist/activity-actor-fields.js +128 -0
  9. package/dist/activity-store.js +12 -19
  10. package/dist/agent-context-store.js +5 -25
  11. package/dist/agent-run-store.js +5 -25
  12. package/dist/agent-suite.js +1 -8
  13. package/dist/artifacts/register-artifact.d.ts +47 -0
  14. package/dist/artifacts/register-artifact.js +271 -0
  15. package/dist/auth/flows.d.ts +47 -0
  16. package/dist/auth/flows.js +169 -0
  17. package/dist/auth-store.js +14 -39
  18. package/dist/byok-store.js +5 -19
  19. package/dist/cli/orgx.d.ts +66 -0
  20. package/dist/cli/orgx.js +91 -0
  21. package/dist/config/refresh.d.ts +32 -0
  22. package/dist/config/refresh.js +55 -0
  23. package/dist/config/resolution.d.ts +37 -0
  24. package/dist/config/resolution.js +178 -0
  25. package/dist/contracts/client.d.ts +1 -0
  26. package/dist/contracts/client.js +7 -5
  27. package/dist/contracts/shared-types.d.ts +147 -0
  28. package/dist/contracts/shared-types.js +3 -0
  29. package/dist/contracts/types.d.ts +1 -130
  30. package/dist/contracts/types.js +5 -0
  31. package/dist/entities/auto-assignment.d.ts +36 -0
  32. package/dist/entities/auto-assignment.js +115 -0
  33. package/dist/entity-comment-store.js +5 -25
  34. package/dist/hash-utils.d.ts +2 -0
  35. package/dist/hash-utils.js +12 -0
  36. package/dist/http/helpers/activity-headline.d.ts +10 -0
  37. package/dist/http/helpers/activity-headline.js +192 -0
  38. package/dist/http/helpers/artifact-fallback.d.ts +13 -0
  39. package/dist/http/helpers/artifact-fallback.js +148 -0
  40. package/dist/http/helpers/auto-continue-engine.d.ts +298 -0
  41. package/dist/http/helpers/auto-continue-engine.js +1218 -0
  42. package/dist/http/helpers/autopilot-operations.d.ts +157 -0
  43. package/dist/http/helpers/autopilot-operations.js +403 -0
  44. package/dist/http/helpers/autopilot-runtime.d.ts +42 -0
  45. package/dist/http/helpers/autopilot-runtime.js +319 -0
  46. package/dist/http/helpers/autopilot-slice-utils.d.ts +38 -0
  47. package/dist/http/helpers/autopilot-slice-utils.js +476 -0
  48. package/dist/http/helpers/decision-mapper.d.ts +12 -0
  49. package/dist/http/helpers/decision-mapper.js +44 -0
  50. package/dist/http/helpers/dispatch-lifecycle.d.ts +102 -0
  51. package/dist/http/helpers/dispatch-lifecycle.js +604 -0
  52. package/dist/http/helpers/hash-utils.d.ts +1 -0
  53. package/dist/http/helpers/hash-utils.js +1 -0
  54. package/dist/http/helpers/kickoff-context.d.ts +12 -0
  55. package/dist/http/helpers/kickoff-context.js +154 -0
  56. package/dist/http/helpers/mission-control.d.ts +94 -0
  57. package/dist/http/helpers/mission-control.js +894 -0
  58. package/dist/http/helpers/openclaw-cli.d.ts +37 -0
  59. package/dist/http/helpers/openclaw-cli.js +283 -0
  60. package/dist/http/helpers/runtime-sse.d.ts +20 -0
  61. package/dist/http/helpers/runtime-sse.js +110 -0
  62. package/dist/http/helpers/value-utils.d.ts +6 -0
  63. package/dist/http/helpers/value-utils.js +67 -0
  64. package/dist/http/index.d.ts +88 -0
  65. package/dist/http/index.js +2353 -0
  66. package/dist/http/router.d.ts +23 -0
  67. package/dist/http/router.js +23 -0
  68. package/dist/http/routes/agent-control.d.ts +79 -0
  69. package/dist/http/routes/agent-control.js +684 -0
  70. package/dist/http/routes/agent-suite.d.ts +29 -0
  71. package/dist/http/routes/agent-suite.js +198 -0
  72. package/dist/http/routes/agents-catalog.d.ts +40 -0
  73. package/dist/http/routes/agents-catalog.js +83 -0
  74. package/dist/http/routes/billing.d.ts +23 -0
  75. package/dist/http/routes/billing.js +55 -0
  76. package/dist/http/routes/debug.d.ts +14 -0
  77. package/dist/http/routes/debug.js +21 -0
  78. package/dist/http/routes/decision-actions.d.ts +13 -0
  79. package/dist/http/routes/decision-actions.js +66 -0
  80. package/dist/http/routes/delegation.d.ts +19 -0
  81. package/dist/http/routes/delegation.js +32 -0
  82. package/dist/http/routes/entities.d.ts +47 -0
  83. package/dist/http/routes/entities.js +152 -0
  84. package/dist/http/routes/entity-dynamic.d.ts +25 -0
  85. package/dist/http/routes/entity-dynamic.js +191 -0
  86. package/dist/http/routes/health.d.ts +22 -0
  87. package/dist/http/routes/health.js +49 -0
  88. package/dist/http/routes/live-legacy.d.ts +110 -0
  89. package/dist/http/routes/live-legacy.js +598 -0
  90. package/dist/http/routes/live-misc.d.ts +69 -0
  91. package/dist/http/routes/live-misc.js +206 -0
  92. package/dist/http/routes/live-snapshot.d.ts +90 -0
  93. package/dist/http/routes/live-snapshot.js +297 -0
  94. package/dist/http/routes/mission-control-actions.d.ts +83 -0
  95. package/dist/http/routes/mission-control-actions.js +541 -0
  96. package/dist/http/routes/mission-control-read.d.ts +28 -0
  97. package/dist/http/routes/mission-control-read.js +67 -0
  98. package/dist/http/routes/onboarding.d.ts +34 -0
  99. package/dist/http/routes/onboarding.js +101 -0
  100. package/dist/http/routes/run-control.d.ts +24 -0
  101. package/dist/http/routes/run-control.js +86 -0
  102. package/dist/http/routes/runtime-hooks.d.ts +69 -0
  103. package/dist/http/routes/runtime-hooks.js +437 -0
  104. package/dist/http/routes/settings-byok.d.ts +23 -0
  105. package/dist/http/routes/settings-byok.js +163 -0
  106. package/dist/http/routes/summary.d.ts +18 -0
  107. package/dist/http/routes/summary.js +42 -0
  108. package/dist/http/routes/work-artifacts.d.ts +9 -0
  109. package/dist/http/routes/work-artifacts.js +36 -0
  110. package/dist/http/shared-state.d.ts +16 -0
  111. package/dist/http/shared-state.js +1 -0
  112. package/dist/http-handler.d.ts +1 -88
  113. package/dist/http-handler.js +1 -9664
  114. package/dist/index.js +122 -2121
  115. package/dist/json-utils.d.ts +1 -0
  116. package/dist/json-utils.js +8 -0
  117. package/dist/local-openclaw.js +8 -0
  118. package/dist/mcp-client-setup.js +75 -90
  119. package/dist/next-up-queue-store.js +4 -18
  120. package/dist/runtime-instance-store.js +8 -34
  121. package/dist/services/background.d.ts +23 -0
  122. package/dist/services/background.js +23 -0
  123. package/dist/services/instrumentation.d.ts +29 -0
  124. package/dist/services/instrumentation.js +136 -0
  125. package/dist/snapshot-store.js +5 -25
  126. package/dist/stores/json-store.d.ts +11 -0
  127. package/dist/stores/json-store.js +42 -0
  128. package/dist/sync/outbox-replay.d.ts +55 -0
  129. package/dist/sync/outbox-replay.js +514 -0
  130. package/dist/tools/core-tools.d.ts +76 -0
  131. package/dist/tools/core-tools.js +1005 -0
  132. package/dist/worker-supervisor.js +15 -0
  133. package/package.json +6 -1
  134. package/dashboard/dist/assets/0tOC3wSN.js +0 -214
  135. package/dashboard/dist/assets/Bm8QnMJ_.js +0 -1
  136. package/dashboard/dist/assets/CyxZio4Y.js +0 -1
  137. package/dashboard/dist/assets/DaAIOik3.css +0 -1
@@ -0,0 +1,47 @@
1
+ import { type ResolvedConfig } from "../config/resolution.js";
2
+ import type { OnboardingState } from "../types.js";
3
+ type LoggerLike = {
4
+ info?: (msg: string, meta?: Record<string, unknown>) => void;
5
+ warn?: (msg: string, meta?: Record<string, unknown>) => void;
6
+ debug?: (msg: string, meta?: Record<string, unknown>) => void;
7
+ };
8
+ type RuntimeConfigState = Pick<ResolvedConfig, "apiKey" | "apiKeySource" | "userId" | "baseUrl" | "installationId">;
9
+ export declare function applyRuntimeApiKey(input: {
10
+ config: RuntimeConfigState;
11
+ apiKey: string;
12
+ source: "manual" | "browser_pairing";
13
+ workspaceName?: string | null;
14
+ keyPrefix?: string | null;
15
+ userId?: string | null;
16
+ currentWorkspaceName: string | null;
17
+ updateOnboardingState: (updates: Partial<OnboardingState>) => unknown;
18
+ setCredentials: (credentials: {
19
+ apiKey: string;
20
+ userId: string;
21
+ baseUrl: string;
22
+ }) => void;
23
+ logger?: LoggerLike;
24
+ }): void;
25
+ export declare function isAuthRequiredError(result: {
26
+ status: number;
27
+ error: string;
28
+ }): boolean;
29
+ export declare function buildManualKeyConnectUrl(baseApiUrl: string): string;
30
+ export declare function fetchOrgxJson<T>(input: {
31
+ baseApiUrl: string;
32
+ method: "GET" | "POST";
33
+ path: string;
34
+ body?: unknown;
35
+ options?: {
36
+ timeoutMs?: number;
37
+ };
38
+ toErrorMessage: (err: unknown) => string;
39
+ }): Promise<{
40
+ ok: true;
41
+ data: T;
42
+ } | {
43
+ ok: false;
44
+ status: number;
45
+ error: string;
46
+ }>;
47
+ export {};
@@ -0,0 +1,169 @@
1
+ import { saveAuthStore } from "../auth-store.js";
2
+ import { resolveRuntimeUserId, } from "../config/resolution.js";
3
+ import { autoConfigureDetectedMcpClients } from "../mcp-client-setup.js";
4
+ import { readOpenClawGatewayPort, readOpenClawSettingsSnapshot, } from "../openclaw-settings.js";
5
+ export function applyRuntimeApiKey(input) {
6
+ const nextApiKey = input.apiKey.trim();
7
+ input.config.apiKey = nextApiKey;
8
+ input.config.apiKeySource = "persisted";
9
+ input.config.userId = resolveRuntimeUserId(nextApiKey, [input.userId, input.config.userId]);
10
+ input.setCredentials({
11
+ apiKey: input.config.apiKey,
12
+ userId: input.config.userId,
13
+ baseUrl: input.config.baseUrl,
14
+ });
15
+ saveAuthStore({
16
+ installationId: input.config.installationId,
17
+ apiKey: nextApiKey,
18
+ userId: input.config.userId || null,
19
+ workspaceName: input.workspaceName ?? null,
20
+ keyPrefix: input.keyPrefix ?? null,
21
+ source: input.source,
22
+ });
23
+ input.updateOnboardingState({
24
+ hasApiKey: true,
25
+ keySource: "persisted",
26
+ installationId: input.config.installationId,
27
+ workspaceName: input.workspaceName ?? input.currentWorkspaceName,
28
+ });
29
+ if (input.source === "browser_pairing" &&
30
+ process.env.ORGX_DISABLE_MCP_CLIENT_AUTOCONFIG !== "1") {
31
+ try {
32
+ const snapshot = readOpenClawSettingsSnapshot();
33
+ const port = readOpenClawGatewayPort(snapshot.raw);
34
+ const localMcpUrl = `http://127.0.0.1:${port}/orgx/mcp`;
35
+ void autoConfigureDetectedMcpClients({
36
+ localMcpUrl,
37
+ logger: input.logger ?? {},
38
+ }).catch(() => {
39
+ // best effort
40
+ });
41
+ }
42
+ catch {
43
+ // best effort
44
+ }
45
+ }
46
+ }
47
+ export function isAuthRequiredError(result) {
48
+ if (result.status !== 401) {
49
+ return false;
50
+ }
51
+ return /auth|unauthorized|token/i.test(result.error);
52
+ }
53
+ export function buildManualKeyConnectUrl(baseApiUrl) {
54
+ try {
55
+ // Deep-link into the Security section where API keys live.
56
+ return new URL("/settings#security", baseApiUrl).toString();
57
+ }
58
+ catch {
59
+ return "https://www.useorgx.com/settings#security";
60
+ }
61
+ }
62
+ export async function fetchOrgxJson(input) {
63
+ try {
64
+ const controller = new AbortController();
65
+ const timeoutMs = typeof input.options?.timeoutMs === "number" &&
66
+ Number.isFinite(input.options.timeoutMs)
67
+ ? Math.max(1_000, Math.floor(input.options.timeoutMs))
68
+ : 12_000;
69
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
70
+ let response;
71
+ let rawText = "";
72
+ try {
73
+ response = await fetch(`${input.baseApiUrl}${input.path}`, {
74
+ method: input.method,
75
+ signal: controller.signal,
76
+ headers: {
77
+ Accept: "application/json",
78
+ "Content-Type": "application/json",
79
+ },
80
+ body: input.body ? JSON.stringify(input.body) : undefined,
81
+ });
82
+ rawText = await response.text().catch(() => "");
83
+ }
84
+ finally {
85
+ clearTimeout(timeout);
86
+ }
87
+ const payload = (() => {
88
+ if (!rawText)
89
+ return null;
90
+ try {
91
+ return JSON.parse(rawText);
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ })();
97
+ if (!response.ok) {
98
+ const rawError = payload?.error ?? payload?.message;
99
+ let errorMessage;
100
+ if (typeof rawError === "string") {
101
+ errorMessage = rawError;
102
+ }
103
+ else if (rawError &&
104
+ typeof rawError === "object" &&
105
+ "message" in rawError &&
106
+ typeof rawError.message === "string") {
107
+ errorMessage = rawError.message;
108
+ }
109
+ else if (rawText && rawText.trim().length > 0) {
110
+ // Avoid dumping HTML (Cloudflare / Next.js error pages) into UI; keep it short.
111
+ const sanitized = rawText
112
+ .replace(/\s+/g, " ")
113
+ .replace(/<[^>]+>/g, "")
114
+ .trim();
115
+ errorMessage =
116
+ sanitized.length > 0
117
+ ? sanitized.slice(0, 180)
118
+ : `OrgX request failed (${response.status})`;
119
+ }
120
+ else {
121
+ errorMessage = `OrgX request failed (${response.status})`;
122
+ }
123
+ const statusToken = `HTTP ${response.status}`;
124
+ if (response.status &&
125
+ !errorMessage.toLowerCase().includes(statusToken.toLowerCase()) &&
126
+ !errorMessage.includes(`(${response.status})`)) {
127
+ errorMessage = `${errorMessage} (HTTP ${response.status})`;
128
+ }
129
+ const debugParts = [];
130
+ const requestId = response.headers.get("x-request-id");
131
+ const vercelId = response.headers.get("x-vercel-id");
132
+ const cfRay = response.headers.get("cf-ray");
133
+ const clerkStatus = response.headers.get("x-clerk-auth-status");
134
+ const clerkReason = response.headers.get("x-clerk-auth-reason");
135
+ if (requestId)
136
+ debugParts.push(`req=${requestId}`);
137
+ if (vercelId && vercelId !== requestId)
138
+ debugParts.push(`vercel=${vercelId}`);
139
+ if (cfRay)
140
+ debugParts.push(`cf-ray=${cfRay}`);
141
+ if (clerkStatus)
142
+ debugParts.push(`clerk=${clerkStatus}`);
143
+ if (clerkReason)
144
+ debugParts.push(`clerk_reason=${clerkReason}`);
145
+ const debugSuffix = debugParts.length > 0 ? ` (${debugParts.join(", ")})` : "";
146
+ return {
147
+ ok: false,
148
+ status: response.status,
149
+ error: `${errorMessage}${debugSuffix}`,
150
+ };
151
+ }
152
+ if (payload?.data !== undefined) {
153
+ return { ok: true, data: payload.data };
154
+ }
155
+ if (payload !== null) {
156
+ return { ok: true, data: payload };
157
+ }
158
+ return { ok: true, data: rawText };
159
+ }
160
+ catch (err) {
161
+ const message = err &&
162
+ typeof err === "object" &&
163
+ "name" in err &&
164
+ err.name === "AbortError"
165
+ ? `OrgX request timed out (method=${input.method}, path=${input.path})`
166
+ : input.toErrorMessage(err);
167
+ return { ok: false, status: 0, error: message };
168
+ }
169
+ }
@@ -1,7 +1,8 @@
1
- import { mkdirSync, readFileSync, chmodSync, existsSync, rmSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from './paths.js';
4
4
  import { backupCorruptFileSync, writeJsonFileAtomicSync } from './fs-utils.js';
5
+ import { clearStoreFileSync, ensureStoreDirSync, parseJsonSafe, } from "./stores/json-store.js";
5
6
  function authDir() {
6
7
  return getOrgxPluginConfigDir();
7
8
  }
@@ -14,23 +15,11 @@ function installationFile() {
14
15
  function isUserScopedApiKey(apiKey) {
15
16
  return apiKey.trim().toLowerCase().startsWith('oxk_');
16
17
  }
17
- function ensureAuthDir() {
18
- const dir = authDir();
19
- mkdirSync(dir, { recursive: true, mode: 0o700 });
20
- try {
21
- chmodSync(dir, 0o700);
22
- }
23
- catch {
24
- // best effort
25
- }
18
+ function isUuid(value) {
19
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
26
20
  }
27
- function parseJson(value) {
28
- try {
29
- return JSON.parse(value);
30
- }
31
- catch {
32
- return null;
33
- }
21
+ function ensureAuthDir() {
22
+ ensureStoreDirSync(authDir());
34
23
  }
35
24
  export function getAuthFilePath() {
36
25
  return authFile();
@@ -41,7 +30,7 @@ export function readPersistedAuth() {
41
30
  if (!existsSync(file))
42
31
  return null;
43
32
  const raw = readFileSync(file, 'utf8');
44
- const parsed = parseJson(raw);
33
+ const parsed = parseJsonSafe(raw);
45
34
  if (!parsed) {
46
35
  backupCorruptFileSync(file);
47
36
  return null;
@@ -49,17 +38,6 @@ export function readPersistedAuth() {
49
38
  if (!parsed || typeof parsed.apiKey !== 'string' || parsed.apiKey.trim().length === 0) {
50
39
  return null;
51
40
  }
52
- if (isUserScopedApiKey(parsed.apiKey) &&
53
- typeof parsed.userId === 'string' &&
54
- parsed.userId.trim().length > 0) {
55
- const sanitized = {
56
- ...parsed,
57
- userId: null,
58
- updatedAt: new Date().toISOString(),
59
- };
60
- writeJsonFileAtomicSync(file, sanitized, 0o600);
61
- return sanitized;
62
- }
63
41
  return parsed;
64
42
  }
65
43
  catch {
@@ -70,9 +48,12 @@ export function writePersistedAuth(input) {
70
48
  ensureAuthDir();
71
49
  const now = new Date().toISOString();
72
50
  const existing = readPersistedAuth();
73
- const normalizedUserId = isUserScopedApiKey(input.apiKey)
51
+ const rawUserId = typeof input.userId === 'string' ? input.userId.trim() : '';
52
+ const normalizedUserId = rawUserId.length === 0
74
53
  ? null
75
- : input.userId ?? null;
54
+ : isUserScopedApiKey(input.apiKey)
55
+ ? (isUuid(rawUserId) ? rawUserId : null)
56
+ : rawUserId;
76
57
  const next = {
77
58
  apiKey: input.apiKey,
78
59
  source: input.source,
@@ -88,13 +69,7 @@ export function writePersistedAuth(input) {
88
69
  return next;
89
70
  }
90
71
  export function clearPersistedAuth() {
91
- const file = authFile();
92
- try {
93
- rmSync(file, { force: true });
94
- }
95
- catch {
96
- // best effort
97
- }
72
+ clearStoreFileSync(authFile());
98
73
  }
99
74
  function readInstallationRecord() {
100
75
  const file = installationFile();
@@ -102,7 +77,7 @@ function readInstallationRecord() {
102
77
  if (!existsSync(file))
103
78
  return null;
104
79
  const raw = readFileSync(file, 'utf8');
105
- const parsed = parseJson(raw);
80
+ const parsed = parseJsonSafe(raw);
106
81
  if (!parsed) {
107
82
  backupCorruptFileSync(file);
108
83
  return null;
@@ -1,7 +1,8 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { getOpenClawDir } from "./paths.js";
4
4
  import { backupCorruptFileSync, writeJsonFileAtomicSync } from "./fs-utils.js";
5
+ import { ensureStoreDirSync, parseJsonSafe, } from "./stores/json-store.js";
5
6
  const PROVIDER_PROFILE_MAP = {
6
7
  openaiApiKey: { profileId: "openai-codex", provider: "openai-codex" },
7
8
  anthropicApiKey: { profileId: "anthropic", provider: "anthropic" },
@@ -18,14 +19,6 @@ function isSafePathSegment(value) {
18
19
  return false;
19
20
  return true;
20
21
  }
21
- function parseJson(value) {
22
- try {
23
- return JSON.parse(value);
24
- }
25
- catch {
26
- return null;
27
- }
28
- }
29
22
  function readObject(value) {
30
23
  return value && typeof value === "object" && !Array.isArray(value)
31
24
  ? value
@@ -36,7 +29,7 @@ function resolveDefaultAgentId() {
36
29
  const configPath = join(getOpenClawDir(), "openclaw.json");
37
30
  if (!existsSync(configPath))
38
31
  return "main";
39
- const raw = parseJson(readFileSync(configPath, "utf8"));
32
+ const raw = parseJsonSafe(readFileSync(configPath, "utf8"));
40
33
  const agents = readObject(raw?.agents);
41
34
  const list = Array.isArray(agents.list) ? agents.list : [];
42
35
  for (const entry of list) {
@@ -70,14 +63,7 @@ function authProfilesFile() {
70
63
  return join(authProfilesDir(), "auth-profiles.json");
71
64
  }
72
65
  function ensureAuthProfilesDir() {
73
- const dir = authProfilesDir();
74
- mkdirSync(dir, { recursive: true, mode: 0o700 });
75
- try {
76
- chmodSync(dir, 0o700);
77
- }
78
- catch {
79
- // best effort
80
- }
66
+ ensureStoreDirSync(authProfilesDir());
81
67
  }
82
68
  function normalizeAuthProfileEntry(value) {
83
69
  if (!value || typeof value !== "object")
@@ -96,7 +82,7 @@ function readAuthProfiles() {
96
82
  if (!existsSync(file))
97
83
  return { file, parsed: null };
98
84
  const raw = readFileSync(file, "utf8");
99
- const parsed = parseJson(raw);
85
+ const parsed = parseJsonSafe(raw);
100
86
  if (!parsed || typeof parsed !== "object") {
101
87
  backupCorruptFileSync(file);
102
88
  return { file, parsed: null };
@@ -0,0 +1,66 @@
1
+ import type { OrgXClient } from "../api.js";
2
+ import type { OnboardingState, OrgSnapshot } from "../types.js";
3
+ import type { ResolvedConfig } from "../config/resolution.js";
4
+ type DoctorCheckStatus = "pass" | "warn" | "fail";
5
+ type ReplayStatus = "idle" | "running" | "success" | "error";
6
+ interface DoctorCheck {
7
+ id: string;
8
+ status: DoctorCheckStatus;
9
+ message: string;
10
+ }
11
+ export interface HealthReport {
12
+ ok: boolean;
13
+ status: "ok" | "degraded" | "error";
14
+ generatedAt: string;
15
+ checks: DoctorCheck[];
16
+ plugin: {
17
+ version: string;
18
+ installationId: string;
19
+ enabled: boolean;
20
+ dashboardEnabled: boolean;
21
+ baseUrl: string;
22
+ };
23
+ auth: {
24
+ hasApiKey: boolean;
25
+ keySource: ResolvedConfig["apiKeySource"];
26
+ userIdConfigured: boolean;
27
+ onboardingStatus: OnboardingState["status"];
28
+ };
29
+ sync: {
30
+ serviceRunning: boolean;
31
+ inFlight: boolean;
32
+ lastSnapshotAt: string | null;
33
+ };
34
+ outbox: {
35
+ pendingTotal: number;
36
+ pendingByQueue: Record<string, number>;
37
+ oldestEventAt: string | null;
38
+ newestEventAt: string | null;
39
+ replayStatus: ReplayStatus;
40
+ lastReplayAttemptAt: string | null;
41
+ lastReplaySuccessAt: string | null;
42
+ lastReplayFailureAt: string | null;
43
+ lastReplayError: string | null;
44
+ };
45
+ remote: {
46
+ enabled: boolean;
47
+ reachable: boolean | null;
48
+ latencyMs: number | null;
49
+ error: string | null;
50
+ };
51
+ }
52
+ export interface RegisterOrgxCliDeps {
53
+ registerCli: (fn: (ctx: {
54
+ program: any;
55
+ }) => void, options?: {
56
+ commands?: string[];
57
+ }) => void;
58
+ client: OrgXClient;
59
+ formatSnapshot: (snapshot: OrgSnapshot) => string;
60
+ buildHealthReport: (input?: {
61
+ probeRemote?: boolean;
62
+ }) => Promise<HealthReport>;
63
+ apiKeySourceLabel: (source: ResolvedConfig["apiKeySource"]) => string;
64
+ }
65
+ export declare function registerOrgxCli(deps: RegisterOrgxCliDeps): void;
66
+ export {};
@@ -0,0 +1,91 @@
1
+ export function registerOrgxCli(deps) {
2
+ deps.registerCli(({ program }) => {
3
+ const cmd = program.command("orgx").description("OrgX integration commands");
4
+ cmd
5
+ .command("status")
6
+ .description("Show current OrgX org status")
7
+ .action(async () => {
8
+ try {
9
+ const snap = await deps.client.getOrgSnapshot();
10
+ console.log(deps.formatSnapshot(snap));
11
+ }
12
+ catch (err) {
13
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
14
+ process.exit(1);
15
+ }
16
+ });
17
+ cmd
18
+ .command("sync")
19
+ .description("Trigger manual memory sync")
20
+ .option("--memory <text>", "Memory to push")
21
+ .option("--daily-log <text>", "Daily log to push")
22
+ .action(async (opts = {}) => {
23
+ try {
24
+ const resp = await deps.client.syncMemory({
25
+ memory: opts.memory,
26
+ dailyLog: opts.dailyLog,
27
+ });
28
+ console.log("Sync complete:");
29
+ console.log(` Initiatives: ${resp.initiatives?.length ?? 0}`);
30
+ console.log(` Active tasks: ${resp.activeTasks?.length ?? 0}`);
31
+ console.log(` Pending decisions: ${resp.pendingDecisions?.length ?? 0}`);
32
+ }
33
+ catch (err) {
34
+ console.error(`Sync failed: ${err instanceof Error ? err.message : err}`);
35
+ process.exit(1);
36
+ }
37
+ });
38
+ cmd
39
+ .command("doctor")
40
+ .description("Run plugin diagnostics and connectivity checks")
41
+ .option("--json", "Print the report as JSON")
42
+ .option("--no-remote", "Skip remote OrgX API reachability probe")
43
+ .action(async (opts = {}) => {
44
+ try {
45
+ const report = await deps.buildHealthReport({
46
+ probeRemote: opts.remote !== false,
47
+ });
48
+ if (opts.json) {
49
+ console.log(JSON.stringify(report, null, 2));
50
+ if (!report.ok)
51
+ process.exit(1);
52
+ return;
53
+ }
54
+ console.log("OrgX Doctor");
55
+ console.log(` Status: ${report.status.toUpperCase()}`);
56
+ console.log(` Plugin: v${report.plugin.version}`);
57
+ console.log(` Base URL: ${report.plugin.baseUrl}`);
58
+ console.log(` API Key Source: ${deps.apiKeySourceLabel(report.auth.keySource)}`);
59
+ console.log(` Outbox Pending: ${report.outbox.pendingTotal}`);
60
+ console.log("");
61
+ console.log("Checks:");
62
+ for (const check of report.checks) {
63
+ const prefix = check.status === "pass"
64
+ ? "[PASS]"
65
+ : check.status === "warn"
66
+ ? "[WARN]"
67
+ : "[FAIL]";
68
+ console.log(` ${prefix} ${check.message}`);
69
+ }
70
+ if (report.remote.enabled) {
71
+ if (report.remote.reachable === true) {
72
+ console.log(` Remote probe latency: ${report.remote.latencyMs ?? "?"}ms`);
73
+ }
74
+ else if (report.remote.reachable === false) {
75
+ console.log(` Remote probe error: ${report.remote.error ?? "Unknown error"}`);
76
+ }
77
+ else {
78
+ console.log(" Remote probe: skipped");
79
+ }
80
+ }
81
+ if (!report.ok) {
82
+ process.exit(1);
83
+ }
84
+ }
85
+ catch (err) {
86
+ console.error(`Doctor failed: ${err instanceof Error ? err.message : err}`);
87
+ process.exit(1);
88
+ }
89
+ });
90
+ }, { commands: ["orgx"] });
91
+ }
@@ -0,0 +1,32 @@
1
+ import type { OnboardingState } from "../types.js";
2
+ import type { PluginApiLike, ResolvedConfig } from "./resolution.js";
3
+ type AuthStoreState = {
4
+ apiKey?: string | null;
5
+ userId?: string | null;
6
+ } | null;
7
+ type RefreshInput = {
8
+ reason?: string;
9
+ allowApiKeyChanges?: boolean;
10
+ };
11
+ type RefreshDeps = {
12
+ api: PluginApiLike;
13
+ config: ResolvedConfig;
14
+ loadAuthStore: () => AuthStoreState;
15
+ resolveConfig: (api: PluginApiLike, input: {
16
+ installationId: string;
17
+ persistedApiKey: string | null;
18
+ persistedUserId: string | null;
19
+ }) => ResolvedConfig;
20
+ updateOnboardingState: (updates: Partial<OnboardingState>) => unknown;
21
+ setCredentials: (input: {
22
+ apiKey: string;
23
+ userId: string;
24
+ baseUrl: string;
25
+ }) => void;
26
+ logInfo?: (message: string, meta?: Record<string, unknown>) => void;
27
+ };
28
+ export declare function refreshResolvedConfig(deps: RefreshDeps, input?: RefreshInput): {
29
+ changed: boolean;
30
+ baseApiUrl: string;
31
+ };
32
+ export {};
@@ -0,0 +1,55 @@
1
+ export function refreshResolvedConfig(deps, input) {
2
+ const allowApiKeyChanges = input?.allowApiKeyChanges !== false;
3
+ const previousApiKey = deps.config.apiKey;
4
+ const previousBaseUrl = deps.config.baseUrl;
5
+ const previousUserId = deps.config.userId;
6
+ const previousDocsUrl = deps.config.docsUrl;
7
+ const previousKeySource = deps.config.apiKeySource;
8
+ const latestPersisted = deps.loadAuthStore();
9
+ const next = deps.resolveConfig(deps.api, {
10
+ installationId: deps.config.installationId,
11
+ persistedApiKey: latestPersisted?.apiKey ?? null,
12
+ persistedUserId: latestPersisted?.userId ?? null,
13
+ });
14
+ const nextApiKey = allowApiKeyChanges ? next.apiKey : previousApiKey;
15
+ const nextUserId = allowApiKeyChanges ? next.userId : previousUserId;
16
+ const changed = nextApiKey !== previousApiKey ||
17
+ next.baseUrl !== previousBaseUrl ||
18
+ nextUserId !== previousUserId ||
19
+ next.docsUrl !== previousDocsUrl ||
20
+ next.apiKeySource !== previousKeySource;
21
+ if (!changed) {
22
+ return {
23
+ changed: false,
24
+ baseApiUrl: deps.config.baseUrl.replace(/\/+$/, ""),
25
+ };
26
+ }
27
+ if (allowApiKeyChanges) {
28
+ deps.config.apiKey = nextApiKey;
29
+ deps.config.userId = nextUserId;
30
+ deps.config.apiKeySource = next.apiKeySource;
31
+ }
32
+ deps.config.baseUrl = next.baseUrl;
33
+ deps.config.docsUrl = next.docsUrl;
34
+ deps.setCredentials({
35
+ apiKey: deps.config.apiKey,
36
+ userId: deps.config.userId,
37
+ baseUrl: deps.config.baseUrl,
38
+ });
39
+ deps.updateOnboardingState({
40
+ hasApiKey: Boolean(deps.config.apiKey),
41
+ keySource: deps.config.apiKeySource,
42
+ docsUrl: deps.config.docsUrl,
43
+ installationId: deps.config.installationId,
44
+ });
45
+ deps.logInfo?.("[orgx] Config refreshed", {
46
+ reason: input?.reason ?? "runtime_refresh",
47
+ baseUrl: deps.config.baseUrl,
48
+ hasApiKey: Boolean(deps.config.apiKey),
49
+ apiKeySource: deps.config.apiKeySource,
50
+ });
51
+ return {
52
+ changed: true,
53
+ baseApiUrl: deps.config.baseUrl.replace(/\/+$/, ""),
54
+ };
55
+ }
@@ -0,0 +1,37 @@
1
+ import type { OrgXConfig } from "../types.js";
2
+ export interface ResolvedConfig extends OrgXConfig {
3
+ dashboardEnabled: boolean;
4
+ installationId: string;
5
+ pluginVersion: string;
6
+ docsUrl: string;
7
+ apiKeySource: "config" | "environment" | "persisted" | "openclaw-config-file" | "legacy-dev" | "none";
8
+ }
9
+ export interface PluginApiLike {
10
+ config?: {
11
+ plugins?: {
12
+ entries?: {
13
+ orgx?: {
14
+ config?: Partial<OrgXConfig & {
15
+ dashboardEnabled: boolean;
16
+ }>;
17
+ };
18
+ };
19
+ };
20
+ };
21
+ }
22
+ export declare function isUserScopedApiKey(apiKey: string): boolean;
23
+ export declare function resolveRuntimeUserId(apiKey: string, candidates: Array<string | null | undefined>): string;
24
+ export declare function normalizeBaseUrl(raw: string | undefined): string;
25
+ export declare function readLegacyEnvValue(keyPattern: RegExp): string;
26
+ export declare function readOpenClawOrgxConfig(): {
27
+ apiKey: string;
28
+ userId: string;
29
+ baseUrl: string;
30
+ };
31
+ export declare function resolvePluginVersion(): string;
32
+ export declare function resolveDocsUrl(baseUrl: string): string;
33
+ export declare function resolveConfig(api: PluginApiLike, input: {
34
+ installationId: string;
35
+ persistedApiKey: string | null;
36
+ persistedUserId: string | null;
37
+ }): ResolvedConfig;