@karmaniverous/jeeves 0.4.5 → 0.4.7
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/dist/cli/jeeves/index.js +2 -2
- package/dist/cli/plugin/index.js +68 -15
- package/dist/cli/service/index.js +40 -1
- package/dist/index.d.ts +164 -7
- package/dist/index.js +484 -38
- package/package.json +1 -1
package/dist/cli/jeeves/index.js
CHANGED
|
@@ -261,14 +261,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
261
261
|
* Core library version, inlined at build time.
|
|
262
262
|
*
|
|
263
263
|
* @remarks
|
|
264
|
-
* The `0.4.
|
|
264
|
+
* The `0.4.6` placeholder is replaced by
|
|
265
265
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
266
266
|
* from `package.json`. This ensures the correct version survives
|
|
267
267
|
* when consumers bundle core into their own dist (where runtime
|
|
268
268
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
269
269
|
*/
|
|
270
270
|
/** The core library version from package.json (inlined at build time). */
|
|
271
|
-
const CORE_VERSION = '0.4.
|
|
271
|
+
const CORE_VERSION = '0.4.6';
|
|
272
272
|
|
|
273
273
|
/**
|
|
274
274
|
* Dynamic discovery of installed Jeeves component CLIs.
|
package/dist/cli/plugin/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { writeFileSync, renameSync, unlinkSync, existsSync,
|
|
2
|
+
import { writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync, readFileSync, readdirSync, copyFileSync, rmSync } from 'node:fs';
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
4
|
import * as commander from 'commander';
|
|
5
|
+
import { packageDirectorySync } from 'package-directory';
|
|
5
6
|
import { lock } from 'proper-lockfile';
|
|
6
7
|
import 'semver';
|
|
7
8
|
import { homedir } from 'node:os';
|
|
@@ -119,14 +120,14 @@ const COMPONENT_VERSIONS_FILE = 'component-versions.json';
|
|
|
119
120
|
* Core library version, inlined at build time.
|
|
120
121
|
*
|
|
121
122
|
* @remarks
|
|
122
|
-
* The `0.4.
|
|
123
|
+
* The `0.4.6` placeholder is replaced by
|
|
123
124
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
124
125
|
* from `package.json`. This ensures the correct version survives
|
|
125
126
|
* when consumers bundle core into their own dist (where runtime
|
|
126
127
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
127
128
|
*/
|
|
128
129
|
/** The core library version from package.json (inlined at build time). */
|
|
129
|
-
const CORE_VERSION = '0.4.
|
|
130
|
+
const CORE_VERSION = '0.4.6';
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
133
|
* Shared file I/O helpers for managed section operations.
|
|
@@ -224,6 +225,30 @@ function readComponentVersions(coreConfigDir) {
|
|
|
224
225
|
return {};
|
|
225
226
|
}
|
|
226
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Write a component's version entry to the shared state file.
|
|
230
|
+
*
|
|
231
|
+
* @remarks
|
|
232
|
+
* Reads the existing file, merges the new entry, and writes atomically.
|
|
233
|
+
*
|
|
234
|
+
* @param coreConfigDir - Path to the core config directory.
|
|
235
|
+
* @param options - Component version data to write.
|
|
236
|
+
*/
|
|
237
|
+
function writeComponentVersion(coreConfigDir, options) {
|
|
238
|
+
const existing = readComponentVersions(coreConfigDir);
|
|
239
|
+
existing[options.componentName] = {
|
|
240
|
+
pluginVersion: options.pluginVersion,
|
|
241
|
+
servicePackage: options.servicePackage,
|
|
242
|
+
pluginPackage: options.pluginPackage,
|
|
243
|
+
updatedAt: new Date().toISOString(),
|
|
244
|
+
};
|
|
245
|
+
const filePath = join(coreConfigDir, COMPONENT_VERSIONS_FILE);
|
|
246
|
+
const dir = dirname(filePath);
|
|
247
|
+
if (!existsSync(dir)) {
|
|
248
|
+
mkdirSync(dir, { recursive: true });
|
|
249
|
+
}
|
|
250
|
+
atomicWrite(filePath, JSON.stringify(existing, null, 2) + '\n');
|
|
251
|
+
}
|
|
227
252
|
/**
|
|
228
253
|
* Remove a component's version entry from the shared state file.
|
|
229
254
|
*
|
|
@@ -802,13 +827,9 @@ function patchConfig(config, pluginId, mode) {
|
|
|
802
827
|
}
|
|
803
828
|
|
|
804
829
|
/**
|
|
805
|
-
*
|
|
830
|
+
* Internal helpers for the plugin installer CLI.
|
|
806
831
|
*
|
|
807
|
-
* @
|
|
808
|
-
* Produces a Commander program with `install` and `uninstall` commands
|
|
809
|
-
* that handle the full plugin lifecycle: copy dist to extensions,
|
|
810
|
-
* patch OpenClaw config, manage HEARTBEAT entries, and clean up
|
|
811
|
-
* managed sections on uninstall.
|
|
832
|
+
* @module
|
|
812
833
|
*/
|
|
813
834
|
/**
|
|
814
835
|
* Derive a component name from a plugin ID.
|
|
@@ -823,7 +844,7 @@ function deriveComponentName(pluginId) {
|
|
|
823
844
|
return pluginId.replace(/^jeeves-/, '').replace(/-openclaw$/, '');
|
|
824
845
|
}
|
|
825
846
|
/**
|
|
826
|
-
* Copy all files from source directory to destination.
|
|
847
|
+
* Copy all files from source directory to destination, recursively.
|
|
827
848
|
*
|
|
828
849
|
* @param srcDir - Source directory.
|
|
829
850
|
* @param destDir - Destination directory.
|
|
@@ -857,6 +878,12 @@ function readJsonFile(filePath) {
|
|
|
857
878
|
return {};
|
|
858
879
|
}
|
|
859
880
|
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Factory for the standard `-openclaw` plugin installer CLI.
|
|
884
|
+
*
|
|
885
|
+
* @module
|
|
886
|
+
*/
|
|
860
887
|
/**
|
|
861
888
|
* Create a standard plugin installer CLI program.
|
|
862
889
|
*
|
|
@@ -882,6 +909,16 @@ function createPluginCli(options) {
|
|
|
882
909
|
const extensionsDir = join(openClawHome, 'extensions', pluginId);
|
|
883
910
|
console.log(`Copying dist to ${extensionsDir}...`);
|
|
884
911
|
copyDistFiles(distDir, extensionsDir);
|
|
912
|
+
// Copy package.json and openclaw.plugin.json from package root
|
|
913
|
+
const pkgRoot = packageDirectorySync({ cwd: distDir });
|
|
914
|
+
if (pkgRoot) {
|
|
915
|
+
for (const file of ['package.json', 'openclaw.plugin.json']) {
|
|
916
|
+
const src = join(pkgRoot, file);
|
|
917
|
+
if (existsSync(src)) {
|
|
918
|
+
copyFileSync(src, join(extensionsDir, file));
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
885
922
|
console.log(' ✓ Dist files copied');
|
|
886
923
|
// 2. Patch openclaw.json
|
|
887
924
|
console.log('Patching OpenClaw config...');
|
|
@@ -925,7 +962,6 @@ function createPluginCli(options) {
|
|
|
925
962
|
: '';
|
|
926
963
|
const parsed = parseHeartbeat(existing);
|
|
927
964
|
const fullName = `jeeves-${componentName}`;
|
|
928
|
-
// Only add if not already present
|
|
929
965
|
const hasEntry = parsed.entries.some((e) => e.name === fullName);
|
|
930
966
|
if (!hasEntry) {
|
|
931
967
|
parsed.entries.push({
|
|
@@ -946,6 +982,25 @@ function createPluginCli(options) {
|
|
|
946
982
|
catch {
|
|
947
983
|
// HEARTBEAT is best-effort during install
|
|
948
984
|
}
|
|
985
|
+
// 5. Write component version
|
|
986
|
+
try {
|
|
987
|
+
init({
|
|
988
|
+
workspacePath: opts.workspace ?? '.',
|
|
989
|
+
configRoot: opts.configRoot,
|
|
990
|
+
});
|
|
991
|
+
const pkgJsonPath = join(extensionsDir, 'package.json');
|
|
992
|
+
const pkgJson = readJsonFile(pkgJsonPath);
|
|
993
|
+
const pluginVersion = typeof pkgJson.version === 'string' ? pkgJson.version : undefined;
|
|
994
|
+
writeComponentVersion(getCoreConfigDir(), {
|
|
995
|
+
componentName,
|
|
996
|
+
pluginPackage,
|
|
997
|
+
pluginVersion,
|
|
998
|
+
});
|
|
999
|
+
console.log(' ✓ Component version written');
|
|
1000
|
+
}
|
|
1001
|
+
catch {
|
|
1002
|
+
console.log(' ⚠ Could not write component version');
|
|
1003
|
+
}
|
|
949
1004
|
console.log();
|
|
950
1005
|
console.log(`✅ ${pluginPackage} installed.`);
|
|
951
1006
|
});
|
|
@@ -974,10 +1029,9 @@ function createPluginCli(options) {
|
|
|
974
1029
|
}
|
|
975
1030
|
// 3. Remove TOOLS.md section
|
|
976
1031
|
try {
|
|
977
|
-
const cfgRoot = opts.configRoot;
|
|
978
1032
|
const ws = opts.workspace;
|
|
979
1033
|
if (ws) {
|
|
980
|
-
init({ workspacePath: ws, configRoot:
|
|
1034
|
+
init({ workspacePath: ws, configRoot: opts.configRoot });
|
|
981
1035
|
const sectionId = componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
982
1036
|
const toolsPath = join(ws, WORKSPACE_FILES.tools);
|
|
983
1037
|
if (existsSync(toolsPath)) {
|
|
@@ -994,10 +1048,9 @@ function createPluginCli(options) {
|
|
|
994
1048
|
}
|
|
995
1049
|
// 4. Remove component-versions.json entry
|
|
996
1050
|
try {
|
|
997
|
-
const cfgRoot = opts.configRoot;
|
|
998
1051
|
init({
|
|
999
1052
|
workspacePath: opts.workspace ?? '.',
|
|
1000
|
-
configRoot:
|
|
1053
|
+
configRoot: opts.configRoot,
|
|
1001
1054
|
});
|
|
1002
1055
|
removeComponentVersion(getCoreConfigDir(), componentName);
|
|
1003
1056
|
console.log(' ✓ Component version entry removed');
|
|
@@ -232,6 +232,14 @@ function getEffectiveServiceName(descriptor) {
|
|
|
232
232
|
return descriptor.serviceName ?? `jeeves-${descriptor.name}`;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Directory and file path conventions for the Jeeves platform.
|
|
237
|
+
*/
|
|
238
|
+
/** Core config directory name within the config root. */
|
|
239
|
+
const CORE_CONFIG_DIR = 'jeeves-core';
|
|
240
|
+
/** Prefix for component config directories: `jeeves-{name}`. */
|
|
241
|
+
const COMPONENT_CONFIG_PREFIX = 'jeeves-';
|
|
242
|
+
|
|
235
243
|
/**
|
|
236
244
|
* Workspace and config root initialization.
|
|
237
245
|
*
|
|
@@ -242,6 +250,19 @@ function getEffectiveServiceName(descriptor) {
|
|
|
242
250
|
* - `{configRoot}/jeeves-core/` for core config
|
|
243
251
|
* - `{configRoot}/jeeves-{name}/` for each component
|
|
244
252
|
*/
|
|
253
|
+
let state;
|
|
254
|
+
/**
|
|
255
|
+
* Initialize the core library with workspace and config root paths.
|
|
256
|
+
*
|
|
257
|
+
* @param options - Workspace and config root paths.
|
|
258
|
+
*/
|
|
259
|
+
function init(options) {
|
|
260
|
+
state = {
|
|
261
|
+
workspacePath: options.workspacePath,
|
|
262
|
+
configRoot: options.configRoot,
|
|
263
|
+
coreConfigDir: join(options.configRoot, CORE_CONFIG_DIR),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
245
266
|
/**
|
|
246
267
|
* Derive the component config directory from the component name.
|
|
247
268
|
*
|
|
@@ -250,7 +271,9 @@ function getEffectiveServiceName(descriptor) {
|
|
|
250
271
|
* @throws Error if `init()` has not been called.
|
|
251
272
|
*/
|
|
252
273
|
function getComponentConfigDir(componentName) {
|
|
253
|
-
|
|
274
|
+
if (!state)
|
|
275
|
+
throw new Error('jeeves-core: init() must be called first');
|
|
276
|
+
return join(state.configRoot, `${COMPONENT_CONFIG_PREFIX}${componentName}`);
|
|
254
277
|
}
|
|
255
278
|
|
|
256
279
|
/**
|
|
@@ -673,6 +696,19 @@ function createServiceManager(descriptor) {
|
|
|
673
696
|
}
|
|
674
697
|
}
|
|
675
698
|
|
|
699
|
+
/**
|
|
700
|
+
* Shared CLI defaults and option registration for Jeeves CLI commands.
|
|
701
|
+
*
|
|
702
|
+
* @remarks
|
|
703
|
+
* All three CLI commands (install, uninstall, status) share the same
|
|
704
|
+
* `--workspace` and `--config-root` options with the same defaults.
|
|
705
|
+
* This module centralizes them to eliminate duplication.
|
|
706
|
+
*/
|
|
707
|
+
/** Default workspace path (current directory). */
|
|
708
|
+
const DEFAULT_WORKSPACE = '.';
|
|
709
|
+
/** Default config root path. */
|
|
710
|
+
const DEFAULT_CONFIG_ROOT = './config';
|
|
711
|
+
|
|
676
712
|
/**
|
|
677
713
|
* Factory for the standard Jeeves service CLI.
|
|
678
714
|
*
|
|
@@ -715,7 +751,10 @@ function createServiceCli(descriptor) {
|
|
|
715
751
|
.command('start')
|
|
716
752
|
.description('Launch the service process (foreground)')
|
|
717
753
|
.requiredOption('-c, --config <path>', 'Config file path')
|
|
754
|
+
.option('-w, --workspace <path>', 'Workspace root path', DEFAULT_WORKSPACE)
|
|
755
|
+
.option('--config-root <path>', 'Platform config root path', DEFAULT_CONFIG_ROOT)
|
|
718
756
|
.action(async (opts) => {
|
|
757
|
+
init({ workspacePath: opts.workspace, configRoot: opts.configRoot });
|
|
719
758
|
await descriptor.run(opts.config);
|
|
720
759
|
});
|
|
721
760
|
// --- status ---
|
package/dist/index.d.ts
CHANGED
|
@@ -267,11 +267,7 @@ declare function createStatusHandler(options: CreateStatusHandlerOptions): Statu
|
|
|
267
267
|
/**
|
|
268
268
|
* Factory for the standard `-openclaw` plugin installer CLI.
|
|
269
269
|
*
|
|
270
|
-
* @
|
|
271
|
-
* Produces a Commander program with `install` and `uninstall` commands
|
|
272
|
-
* that handle the full plugin lifecycle: copy dist to extensions,
|
|
273
|
-
* patch OpenClaw config, manage HEARTBEAT entries, and clean up
|
|
274
|
-
* managed sections on uninstall.
|
|
270
|
+
* @module
|
|
275
271
|
*/
|
|
276
272
|
|
|
277
273
|
/** Options for creating a plugin installer CLI. */
|
|
@@ -1230,6 +1226,20 @@ declare function seedContent(options: SeedContentOptions): Promise<void>;
|
|
|
1230
1226
|
*/
|
|
1231
1227
|
declare function createPluginToolset(descriptor: JeevesComponentDescriptor): ToolDescriptor[];
|
|
1232
1228
|
|
|
1229
|
+
/**
|
|
1230
|
+
* Resolve the version of a package from its `import.meta.url`.
|
|
1231
|
+
*
|
|
1232
|
+
* @module
|
|
1233
|
+
*/
|
|
1234
|
+
/**
|
|
1235
|
+
* Get the version string from the nearest `package.json` relative to the
|
|
1236
|
+
* caller's module URL.
|
|
1237
|
+
*
|
|
1238
|
+
* @param importMetaUrl - The `import.meta.url` of the calling module.
|
|
1239
|
+
* @returns The `version` field, or `'unknown'` on any error.
|
|
1240
|
+
*/
|
|
1241
|
+
declare function getPackageVersion(importMetaUrl: string): string;
|
|
1242
|
+
|
|
1233
1243
|
/**
|
|
1234
1244
|
* HTTP helpers for the OpenClaw plugin SDK.
|
|
1235
1245
|
*
|
|
@@ -1394,6 +1404,153 @@ declare function fail(error: unknown): ToolResult;
|
|
|
1394
1404
|
*/
|
|
1395
1405
|
declare function connectionFail(error: unknown, baseUrl: string, pluginId: string): ToolResult;
|
|
1396
1406
|
|
|
1407
|
+
/**
|
|
1408
|
+
* Shared filesystem utilities for runner job scripts.
|
|
1409
|
+
*
|
|
1410
|
+
* @module
|
|
1411
|
+
*/
|
|
1412
|
+
/** Return current time as ISO 8601 string. */
|
|
1413
|
+
declare function nowIso(): string;
|
|
1414
|
+
/** Generate a random UUID v4. */
|
|
1415
|
+
declare function uuid(): string;
|
|
1416
|
+
/** Create a directory and any missing parents. */
|
|
1417
|
+
declare function ensureDir(p: string): void;
|
|
1418
|
+
/** Read and parse a JSON file, returning `fallback` on any error. */
|
|
1419
|
+
declare function readJson<T>(p: string, fallback: T): T;
|
|
1420
|
+
/** Atomically write a JSON file (write to .tmp, then rename). */
|
|
1421
|
+
declare function writeJsonAtomic(p: string, obj: unknown): void;
|
|
1422
|
+
/** Append a single JSON object as a JSONL line. */
|
|
1423
|
+
declare function appendJsonl(p: string, obj: unknown): void;
|
|
1424
|
+
/** Read a JSONL file into an array of parsed objects. */
|
|
1425
|
+
declare function readJsonl<T = unknown>(p: string): T[];
|
|
1426
|
+
/** Overwrite a file with an array of objects as JSONL. */
|
|
1427
|
+
declare function writeJsonl(p: string, entries: unknown[]): void;
|
|
1428
|
+
/** Synchronous sleep using Atomics.wait. */
|
|
1429
|
+
declare function sleepMs(ms: number): void;
|
|
1430
|
+
/** Async sleep via setTimeout. */
|
|
1431
|
+
declare function sleepAsync(ms: number): Promise<void>;
|
|
1432
|
+
/** Load a .env-style key=value file into process.env. */
|
|
1433
|
+
declare function loadEnvFile(envPath: string): void;
|
|
1434
|
+
/** Parse --key=value arguments from argv into a record. */
|
|
1435
|
+
declare function parseArgs(argv?: string[]): Record<string, string>;
|
|
1436
|
+
/** Get the value following a named flag, or a default. */
|
|
1437
|
+
declare function getArg(argv: string[], name: string, defaultValue: string): string;
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Google API auth helpers for runner job scripts.
|
|
1441
|
+
* Supports OAuth refresh tokens and service account impersonation.
|
|
1442
|
+
*
|
|
1443
|
+
* @module
|
|
1444
|
+
*/
|
|
1445
|
+
/** Service account config object specifying a key file path. */
|
|
1446
|
+
interface ServiceAccountFileConfig {
|
|
1447
|
+
/** Path to the service account JSON key file. */
|
|
1448
|
+
file: string;
|
|
1449
|
+
}
|
|
1450
|
+
/** Configuration for a Google account's auth method. */
|
|
1451
|
+
interface AccountConfig {
|
|
1452
|
+
/** Google account email address. */
|
|
1453
|
+
email: string;
|
|
1454
|
+
/** Path to refresh token file (relative to credentialsDir). */
|
|
1455
|
+
tokenFile?: string;
|
|
1456
|
+
/** Service account key file path or config object. */
|
|
1457
|
+
serviceAccount?: string | ServiceAccountFileConfig;
|
|
1458
|
+
}
|
|
1459
|
+
/** Options for the Google auth helper. */
|
|
1460
|
+
interface GoogleAuthOptions {
|
|
1461
|
+
/** Path to the OAuth client credentials JSON file. */
|
|
1462
|
+
clientCredentialsPath: string;
|
|
1463
|
+
/** Base directory for credential files. */
|
|
1464
|
+
credentialsDir: string;
|
|
1465
|
+
/** Directory containing service account JSON files. */
|
|
1466
|
+
serviceAccountDir?: string;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Create a Google auth helper with the given configuration.
|
|
1470
|
+
* Returns a function that resolves an access token for a given account and scopes.
|
|
1471
|
+
*/
|
|
1472
|
+
declare function createGoogleAuth(options: GoogleAuthOptions): {
|
|
1473
|
+
/** Get an access token for the given account and scopes. */
|
|
1474
|
+
getAccessToken: (account: AccountConfig, scopes: string[]) => Promise<string>;
|
|
1475
|
+
};
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Shared crash-handler wrapper for runner job scripts.
|
|
1479
|
+
* Catches uncaught errors, logs them, and exits with code 1.
|
|
1480
|
+
*
|
|
1481
|
+
* @module
|
|
1482
|
+
*/
|
|
1483
|
+
/**
|
|
1484
|
+
* Wrap a script's main function with crash handling.
|
|
1485
|
+
* On uncaught errors, appends to `_crash.log` in `crashDir` and exits.
|
|
1486
|
+
*
|
|
1487
|
+
* @param name - Script identifier for the crash log.
|
|
1488
|
+
* @param fn - Main function to execute (sync or async).
|
|
1489
|
+
* @param crashDir - Directory for crash logs (default: current working directory).
|
|
1490
|
+
*/
|
|
1491
|
+
declare function runScript(name: string, fn: () => void | Promise<void>, crashDir?: string): void;
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Shell execution utilities for runner job scripts.
|
|
1495
|
+
*
|
|
1496
|
+
* @module
|
|
1497
|
+
*/
|
|
1498
|
+
/** Options for synchronous command execution. */
|
|
1499
|
+
interface RunOptions {
|
|
1500
|
+
/** Output encoding (default utf8). */
|
|
1501
|
+
encoding?: BufferEncoding;
|
|
1502
|
+
/** Max stdout/stderr buffer size in bytes. */
|
|
1503
|
+
maxBuffer?: number;
|
|
1504
|
+
/** Execution timeout in milliseconds. */
|
|
1505
|
+
timeout?: number;
|
|
1506
|
+
/** Run command through the shell. */
|
|
1507
|
+
shell?: boolean;
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Run a command synchronously and return trimmed stdout.
|
|
1511
|
+
* Throws on non-zero exit code.
|
|
1512
|
+
*/
|
|
1513
|
+
declare function run(cmd: string, args: string[], opts?: RunOptions): string;
|
|
1514
|
+
/** Options for retry-enabled command execution. */
|
|
1515
|
+
interface RetryOptions {
|
|
1516
|
+
/** Number of retries (default 2). */
|
|
1517
|
+
retries?: number;
|
|
1518
|
+
/** Base backoff interval in milliseconds (default 5000). */
|
|
1519
|
+
backoffMs?: number;
|
|
1520
|
+
/** Custom predicate to determine if an error is retryable. */
|
|
1521
|
+
isRetryable?: (error: unknown) => boolean;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Run a command with automatic retries on transient failures.
|
|
1525
|
+
* Uses exponential backoff between attempts.
|
|
1526
|
+
*/
|
|
1527
|
+
declare function runWithRetry(cmd: string, args: string[], opts?: RetryOptions & RunOptions): string;
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Slack channel → workspace mapping cache.
|
|
1531
|
+
* Resolves which Slack workspace owns a given channel.
|
|
1532
|
+
*
|
|
1533
|
+
* @module
|
|
1534
|
+
*/
|
|
1535
|
+
/** Options for the Slack workspace resolver. */
|
|
1536
|
+
interface SlackWorkspaceOptions {
|
|
1537
|
+
/** Path to the channel→workspace cache JSON file. */
|
|
1538
|
+
cachePath: string;
|
|
1539
|
+
/** Default workspace team ID when resolution fails. */
|
|
1540
|
+
defaultWorkspace: string;
|
|
1541
|
+
}
|
|
1542
|
+
/** Flush pending cache changes to disk. */
|
|
1543
|
+
declare function saveCache(): void;
|
|
1544
|
+
/**
|
|
1545
|
+
* Resolve the workspace team ID that owns a Slack channel.
|
|
1546
|
+
* Results are cached to disk.
|
|
1547
|
+
*
|
|
1548
|
+
* @param channelId - Slack channel ID.
|
|
1549
|
+
* @param token - Slack bot token for API calls.
|
|
1550
|
+
* @param options - Cache path and default workspace.
|
|
1551
|
+
*/
|
|
1552
|
+
declare function getChannelWorkspace(channelId: string, token: string, options: SlackWorkspaceOptions): Promise<string>;
|
|
1553
|
+
|
|
1397
1554
|
/**
|
|
1398
1555
|
* Factory for platform-aware service lifecycle management.
|
|
1399
1556
|
*
|
|
@@ -1437,5 +1594,5 @@ interface ServiceManager {
|
|
|
1437
1594
|
*/
|
|
1438
1595
|
declare function createServiceManager(descriptor: JeevesComponentDescriptor): ServiceManager;
|
|
1439
1596
|
|
|
1440
|
-
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_FILES, atomicWrite, buildHeartbeatSection, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, getBindAddress, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, needsCleanup, ok, orchestrateHeartbeat, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, seedContent, shingles, shouldWrite, updateManagedSection, withFileLock, writeComponentVersion, writeHeartbeatSection };
|
|
1441
|
-
export type { AsyncContentCacheOptions, ComponentDependencies, ComponentState, ComponentVersionEntry, ComponentVersionsState, ConfigApplyHandler, ConfigApplyRequest, ConfigApplyResult, ConfigQueryHandler, ConfigQueryResponse, CoreConfig, CreatePluginCliOptions, CreateStatusHandlerOptions, HeartbeatEntry, InitOptions, JeevesComponentDescriptor, ManagedMarkers, ManagedSection, OrchestrateHeartbeatOptions, ParseManagedResult, ParsedHeartbeat, PlatformComponent, PluginApi, RefreshPlatformContentOptions, RemoveManagedSectionOptions, SectionId, SeedContentOptions, ServiceManager, ServiceManagerOptions, ServiceState, StatusHandler, StatusHandlerResult, StatusResponse, ToolDescriptor, ToolRegistrationOptions, ToolResult, UpdateManagedSectionOptions, VersionStamp, WriteComponentVersionOptions };
|
|
1597
|
+
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_FILES, appendJsonl, atomicWrite, buildHeartbeatSection, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
|
1598
|
+
export type { AccountConfig, AsyncContentCacheOptions, ComponentDependencies, ComponentState, ComponentVersionEntry, ComponentVersionsState, ConfigApplyHandler, ConfigApplyRequest, ConfigApplyResult, ConfigQueryHandler, ConfigQueryResponse, CoreConfig, CreatePluginCliOptions, CreateStatusHandlerOptions, GoogleAuthOptions, HeartbeatEntry, InitOptions, JeevesComponentDescriptor, ManagedMarkers, ManagedSection, OrchestrateHeartbeatOptions, ParseManagedResult, ParsedHeartbeat, PlatformComponent, PluginApi, RefreshPlatformContentOptions, RemoveManagedSectionOptions, RetryOptions, RunOptions, SectionId, SeedContentOptions, ServiceAccountFileConfig, ServiceManager, ServiceManagerOptions, ServiceState, SlackWorkspaceOptions, StatusHandler, StatusHandlerResult, StatusResponse, ToolDescriptor, ToolRegistrationOptions, ToolResult, UpdateManagedSectionOptions, VersionStamp, WriteComponentVersionOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { writeFileSync, renameSync, unlinkSync, existsSync, readFileSync, mkdirSync,
|
|
2
|
-
import { join, dirname, resolve } from 'node:path';
|
|
1
|
+
import fs, { writeFileSync, renameSync, unlinkSync, existsSync, readFileSync, mkdirSync, readdirSync, copyFileSync, rmSync, cpSync } from 'node:fs';
|
|
2
|
+
import path, { join, dirname, resolve } from 'node:path';
|
|
3
3
|
import { lock } from 'proper-lockfile';
|
|
4
4
|
import { JSONPath } from 'jsonpath-plus';
|
|
5
5
|
import * as commander from 'commander';
|
|
6
|
+
import { packageDirectorySync } from 'package-directory';
|
|
6
7
|
import { valid, gte, gt } from 'semver';
|
|
7
8
|
import { homedir } from 'node:os';
|
|
8
9
|
import { z } from 'zod';
|
|
9
|
-
import { execSync } from 'node:child_process';
|
|
10
|
+
import cp, { execSync } from 'node:child_process';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import
|
|
12
|
+
import crypto from 'node:crypto';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Comment markers for managed content blocks.
|
|
@@ -176,14 +177,14 @@ const PLATFORM_COMPONENTS = [
|
|
|
176
177
|
* Core library version, inlined at build time.
|
|
177
178
|
*
|
|
178
179
|
* @remarks
|
|
179
|
-
* The `0.4.
|
|
180
|
+
* The `0.4.6` placeholder is replaced by
|
|
180
181
|
* `@rollup/plugin-replace` during the build with the actual version
|
|
181
182
|
* from `package.json`. This ensures the correct version survives
|
|
182
183
|
* when consumers bundle core into their own dist (where runtime
|
|
183
184
|
* `import.meta.url`-based resolution would find the wrong package.json).
|
|
184
185
|
*/
|
|
185
186
|
/** The core library version from package.json (inlined at build time). */
|
|
186
|
-
const CORE_VERSION = '0.4.
|
|
187
|
+
const CORE_VERSION = '0.4.6';
|
|
187
188
|
|
|
188
189
|
/**
|
|
189
190
|
* Workspace and config root initialization.
|
|
@@ -1239,13 +1240,9 @@ function patchConfig(config, pluginId, mode) {
|
|
|
1239
1240
|
}
|
|
1240
1241
|
|
|
1241
1242
|
/**
|
|
1242
|
-
*
|
|
1243
|
+
* Internal helpers for the plugin installer CLI.
|
|
1243
1244
|
*
|
|
1244
|
-
* @
|
|
1245
|
-
* Produces a Commander program with `install` and `uninstall` commands
|
|
1246
|
-
* that handle the full plugin lifecycle: copy dist to extensions,
|
|
1247
|
-
* patch OpenClaw config, manage HEARTBEAT entries, and clean up
|
|
1248
|
-
* managed sections on uninstall.
|
|
1245
|
+
* @module
|
|
1249
1246
|
*/
|
|
1250
1247
|
/**
|
|
1251
1248
|
* Derive a component name from a plugin ID.
|
|
@@ -1260,7 +1257,7 @@ function deriveComponentName(pluginId) {
|
|
|
1260
1257
|
return pluginId.replace(/^jeeves-/, '').replace(/-openclaw$/, '');
|
|
1261
1258
|
}
|
|
1262
1259
|
/**
|
|
1263
|
-
* Copy all files from source directory to destination.
|
|
1260
|
+
* Copy all files from source directory to destination, recursively.
|
|
1264
1261
|
*
|
|
1265
1262
|
* @param srcDir - Source directory.
|
|
1266
1263
|
* @param destDir - Destination directory.
|
|
@@ -1294,6 +1291,12 @@ function readJsonFile(filePath) {
|
|
|
1294
1291
|
return {};
|
|
1295
1292
|
}
|
|
1296
1293
|
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Factory for the standard `-openclaw` plugin installer CLI.
|
|
1297
|
+
*
|
|
1298
|
+
* @module
|
|
1299
|
+
*/
|
|
1297
1300
|
/**
|
|
1298
1301
|
* Create a standard plugin installer CLI program.
|
|
1299
1302
|
*
|
|
@@ -1319,6 +1322,16 @@ function createPluginCli(options) {
|
|
|
1319
1322
|
const extensionsDir = join(openClawHome, 'extensions', pluginId);
|
|
1320
1323
|
console.log(`Copying dist to ${extensionsDir}...`);
|
|
1321
1324
|
copyDistFiles(distDir, extensionsDir);
|
|
1325
|
+
// Copy package.json and openclaw.plugin.json from package root
|
|
1326
|
+
const pkgRoot = packageDirectorySync({ cwd: distDir });
|
|
1327
|
+
if (pkgRoot) {
|
|
1328
|
+
for (const file of ['package.json', 'openclaw.plugin.json']) {
|
|
1329
|
+
const src = join(pkgRoot, file);
|
|
1330
|
+
if (existsSync(src)) {
|
|
1331
|
+
copyFileSync(src, join(extensionsDir, file));
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1322
1335
|
console.log(' ✓ Dist files copied');
|
|
1323
1336
|
// 2. Patch openclaw.json
|
|
1324
1337
|
console.log('Patching OpenClaw config...');
|
|
@@ -1362,7 +1375,6 @@ function createPluginCli(options) {
|
|
|
1362
1375
|
: '';
|
|
1363
1376
|
const parsed = parseHeartbeat(existing);
|
|
1364
1377
|
const fullName = `jeeves-${componentName}`;
|
|
1365
|
-
// Only add if not already present
|
|
1366
1378
|
const hasEntry = parsed.entries.some((e) => e.name === fullName);
|
|
1367
1379
|
if (!hasEntry) {
|
|
1368
1380
|
parsed.entries.push({
|
|
@@ -1383,6 +1395,25 @@ function createPluginCli(options) {
|
|
|
1383
1395
|
catch {
|
|
1384
1396
|
// HEARTBEAT is best-effort during install
|
|
1385
1397
|
}
|
|
1398
|
+
// 5. Write component version
|
|
1399
|
+
try {
|
|
1400
|
+
init({
|
|
1401
|
+
workspacePath: opts.workspace ?? '.',
|
|
1402
|
+
configRoot: opts.configRoot,
|
|
1403
|
+
});
|
|
1404
|
+
const pkgJsonPath = join(extensionsDir, 'package.json');
|
|
1405
|
+
const pkgJson = readJsonFile(pkgJsonPath);
|
|
1406
|
+
const pluginVersion = typeof pkgJson.version === 'string' ? pkgJson.version : undefined;
|
|
1407
|
+
writeComponentVersion(getCoreConfigDir(), {
|
|
1408
|
+
componentName,
|
|
1409
|
+
pluginPackage,
|
|
1410
|
+
pluginVersion,
|
|
1411
|
+
});
|
|
1412
|
+
console.log(' ✓ Component version written');
|
|
1413
|
+
}
|
|
1414
|
+
catch {
|
|
1415
|
+
console.log(' ⚠ Could not write component version');
|
|
1416
|
+
}
|
|
1386
1417
|
console.log();
|
|
1387
1418
|
console.log(`✅ ${pluginPackage} installed.`);
|
|
1388
1419
|
});
|
|
@@ -1411,10 +1442,9 @@ function createPluginCli(options) {
|
|
|
1411
1442
|
}
|
|
1412
1443
|
// 3. Remove TOOLS.md section
|
|
1413
1444
|
try {
|
|
1414
|
-
const cfgRoot = opts.configRoot;
|
|
1415
1445
|
const ws = opts.workspace;
|
|
1416
1446
|
if (ws) {
|
|
1417
|
-
init({ workspacePath: ws, configRoot:
|
|
1447
|
+
init({ workspacePath: ws, configRoot: opts.configRoot });
|
|
1418
1448
|
const sectionId = componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
1419
1449
|
const toolsPath = join(ws, WORKSPACE_FILES.tools);
|
|
1420
1450
|
if (existsSync(toolsPath)) {
|
|
@@ -1431,10 +1461,9 @@ function createPluginCli(options) {
|
|
|
1431
1461
|
}
|
|
1432
1462
|
// 4. Remove component-versions.json entry
|
|
1433
1463
|
try {
|
|
1434
|
-
const cfgRoot = opts.configRoot;
|
|
1435
1464
|
init({
|
|
1436
1465
|
workspacePath: opts.workspace ?? '.',
|
|
1437
|
-
configRoot:
|
|
1466
|
+
configRoot: opts.configRoot,
|
|
1438
1467
|
});
|
|
1439
1468
|
removeComponentVersion(getCoreConfigDir(), componentName);
|
|
1440
1469
|
console.log(' ✓ Component version entry removed');
|
|
@@ -1770,7 +1799,7 @@ function isExecError(err) {
|
|
|
1770
1799
|
* (Windows), systemd (Linux), or launchd (macOS) based on platform.
|
|
1771
1800
|
*/
|
|
1772
1801
|
/** Exec helper that returns stdout. */
|
|
1773
|
-
function run(cmd) {
|
|
1802
|
+
function run$1(cmd) {
|
|
1774
1803
|
return execSync(cmd, {
|
|
1775
1804
|
encoding: 'utf-8',
|
|
1776
1805
|
timeout: 30_000,
|
|
@@ -1823,31 +1852,31 @@ function createWindowsManager(descriptor) {
|
|
|
1823
1852
|
const cmdArgs = descriptor.startCommand(cfgPath);
|
|
1824
1853
|
const appPath = cmdArgs[0];
|
|
1825
1854
|
const appArgs = cmdArgs.slice(1).join(' ');
|
|
1826
|
-
run(`nssm install ${svcName} ${appPath}`);
|
|
1855
|
+
run$1(`nssm install ${svcName} ${appPath}`);
|
|
1827
1856
|
if (appArgs) {
|
|
1828
|
-
run(`nssm set ${svcName} AppParameters ${appArgs}`);
|
|
1857
|
+
run$1(`nssm set ${svcName} AppParameters ${appArgs}`);
|
|
1829
1858
|
}
|
|
1830
|
-
run(`nssm set ${svcName} AppStdout ${join(homedir(), `${svcName}.log`)}`);
|
|
1831
|
-
run(`nssm set ${svcName} AppStderr ${join(homedir(), `${svcName}.log`)}`);
|
|
1832
|
-
run(`nssm set ${svcName} AppRotateFiles 1`);
|
|
1833
|
-
run(`nssm set ${svcName} AppRotateBytes 1048576`);
|
|
1859
|
+
run$1(`nssm set ${svcName} AppStdout ${join(homedir(), `${svcName}.log`)}`);
|
|
1860
|
+
run$1(`nssm set ${svcName} AppStderr ${join(homedir(), `${svcName}.log`)}`);
|
|
1861
|
+
run$1(`nssm set ${svcName} AppRotateFiles 1`);
|
|
1862
|
+
run$1(`nssm set ${svcName} AppRotateBytes 1048576`);
|
|
1834
1863
|
},
|
|
1835
1864
|
uninstall(options) {
|
|
1836
1865
|
const svcName = resolveServiceName(descriptor, options);
|
|
1837
1866
|
runQuiet(`nssm stop ${svcName}`);
|
|
1838
|
-
run(`nssm remove ${svcName} confirm`);
|
|
1867
|
+
run$1(`nssm remove ${svcName} confirm`);
|
|
1839
1868
|
},
|
|
1840
1869
|
start(options) {
|
|
1841
1870
|
const svcName = resolveServiceName(descriptor, options);
|
|
1842
|
-
run(`nssm start ${svcName}`);
|
|
1871
|
+
run$1(`nssm start ${svcName}`);
|
|
1843
1872
|
},
|
|
1844
1873
|
stop(options) {
|
|
1845
1874
|
const svcName = resolveServiceName(descriptor, options);
|
|
1846
|
-
run(`nssm stop ${svcName}`);
|
|
1875
|
+
run$1(`nssm stop ${svcName}`);
|
|
1847
1876
|
},
|
|
1848
1877
|
restart(options) {
|
|
1849
1878
|
const svcName = resolveServiceName(descriptor, options);
|
|
1850
|
-
run(`nssm restart ${svcName}`);
|
|
1879
|
+
run$1(`nssm restart ${svcName}`);
|
|
1851
1880
|
},
|
|
1852
1881
|
status(options) {
|
|
1853
1882
|
const svcName = resolveServiceName(descriptor, options);
|
|
@@ -1892,8 +1921,8 @@ function createLinuxManager(descriptor) {
|
|
|
1892
1921
|
const cmdArgs = descriptor.startCommand(cfgPath);
|
|
1893
1922
|
mkdirSync(unitDir, { recursive: true });
|
|
1894
1923
|
writeFileSync(unitPath(svcName), buildSystemdUnit(svcName, cmdArgs));
|
|
1895
|
-
run('systemctl --user daemon-reload');
|
|
1896
|
-
run(`systemctl --user enable ${svcName}.service`);
|
|
1924
|
+
run$1('systemctl --user daemon-reload');
|
|
1925
|
+
run$1(`systemctl --user enable ${svcName}.service`);
|
|
1897
1926
|
},
|
|
1898
1927
|
uninstall(options) {
|
|
1899
1928
|
const svcName = resolveServiceName(descriptor, options);
|
|
@@ -1906,15 +1935,15 @@ function createLinuxManager(descriptor) {
|
|
|
1906
1935
|
},
|
|
1907
1936
|
start(options) {
|
|
1908
1937
|
const svcName = resolveServiceName(descriptor, options);
|
|
1909
|
-
run(`systemctl --user start ${svcName}.service`);
|
|
1938
|
+
run$1(`systemctl --user start ${svcName}.service`);
|
|
1910
1939
|
},
|
|
1911
1940
|
stop(options) {
|
|
1912
1941
|
const svcName = resolveServiceName(descriptor, options);
|
|
1913
|
-
run(`systemctl --user stop ${svcName}.service`);
|
|
1942
|
+
run$1(`systemctl --user stop ${svcName}.service`);
|
|
1914
1943
|
},
|
|
1915
1944
|
restart(options) {
|
|
1916
1945
|
const svcName = resolveServiceName(descriptor, options);
|
|
1917
|
-
run(`systemctl --user restart ${svcName}.service`);
|
|
1946
|
+
run$1(`systemctl --user restart ${svcName}.service`);
|
|
1918
1947
|
},
|
|
1919
1948
|
status(options) {
|
|
1920
1949
|
const svcName = resolveServiceName(descriptor, options);
|
|
@@ -1978,16 +2007,16 @@ function createMacOSManager(descriptor) {
|
|
|
1978
2007
|
},
|
|
1979
2008
|
start(options) {
|
|
1980
2009
|
const svcName = resolveServiceName(descriptor, options);
|
|
1981
|
-
run(`launchctl load ${plistPath(svcName)}`);
|
|
2010
|
+
run$1(`launchctl load ${plistPath(svcName)}`);
|
|
1982
2011
|
},
|
|
1983
2012
|
stop(options) {
|
|
1984
2013
|
const svcName = resolveServiceName(descriptor, options);
|
|
1985
|
-
run(`launchctl unload ${plistPath(svcName)}`);
|
|
2014
|
+
run$1(`launchctl unload ${plistPath(svcName)}`);
|
|
1986
2015
|
},
|
|
1987
2016
|
restart(options) {
|
|
1988
2017
|
const svcName = resolveServiceName(descriptor, options);
|
|
1989
2018
|
runQuiet(`launchctl unload ${plistPath(svcName)}`);
|
|
1990
|
-
run(`launchctl load ${plistPath(svcName)}`);
|
|
2019
|
+
run$1(`launchctl load ${plistPath(svcName)}`);
|
|
1991
2020
|
},
|
|
1992
2021
|
status(options) {
|
|
1993
2022
|
const svcName = resolveServiceName(descriptor, options);
|
|
@@ -2016,6 +2045,19 @@ function createServiceManager(descriptor) {
|
|
|
2016
2045
|
}
|
|
2017
2046
|
}
|
|
2018
2047
|
|
|
2048
|
+
/**
|
|
2049
|
+
* Shared CLI defaults and option registration for Jeeves CLI commands.
|
|
2050
|
+
*
|
|
2051
|
+
* @remarks
|
|
2052
|
+
* All three CLI commands (install, uninstall, status) share the same
|
|
2053
|
+
* `--workspace` and `--config-root` options with the same defaults.
|
|
2054
|
+
* This module centralizes them to eliminate duplication.
|
|
2055
|
+
*/
|
|
2056
|
+
/** Default workspace path (current directory). */
|
|
2057
|
+
const DEFAULT_WORKSPACE = '.';
|
|
2058
|
+
/** Default config root path. */
|
|
2059
|
+
const DEFAULT_CONFIG_ROOT = './config';
|
|
2060
|
+
|
|
2019
2061
|
/**
|
|
2020
2062
|
* Factory for the standard Jeeves service CLI.
|
|
2021
2063
|
*
|
|
@@ -2058,7 +2100,10 @@ function createServiceCli(descriptor) {
|
|
|
2058
2100
|
.command('start')
|
|
2059
2101
|
.description('Launch the service process (foreground)')
|
|
2060
2102
|
.requiredOption('-c, --config <path>', 'Config file path')
|
|
2103
|
+
.option('-w, --workspace <path>', 'Workspace root path', DEFAULT_WORKSPACE)
|
|
2104
|
+
.option('--config-root <path>', 'Platform config root path', DEFAULT_CONFIG_ROOT)
|
|
2061
2105
|
.action(async (opts) => {
|
|
2106
|
+
init({ workspacePath: opts.workspace, configRoot: opts.configRoot });
|
|
2062
2107
|
await descriptor.run(opts.config);
|
|
2063
2108
|
});
|
|
2064
2109
|
// --- status ---
|
|
@@ -3951,6 +3996,33 @@ function createPluginToolset(descriptor) {
|
|
|
3951
3996
|
return [statusTool, configTool, configApplyTool, serviceTool];
|
|
3952
3997
|
}
|
|
3953
3998
|
|
|
3999
|
+
/**
|
|
4000
|
+
* Resolve the version of a package from its `import.meta.url`.
|
|
4001
|
+
*
|
|
4002
|
+
* @module
|
|
4003
|
+
*/
|
|
4004
|
+
/**
|
|
4005
|
+
* Get the version string from the nearest `package.json` relative to the
|
|
4006
|
+
* caller's module URL.
|
|
4007
|
+
*
|
|
4008
|
+
* @param importMetaUrl - The `import.meta.url` of the calling module.
|
|
4009
|
+
* @returns The `version` field, or `'unknown'` on any error.
|
|
4010
|
+
*/
|
|
4011
|
+
function getPackageVersion(importMetaUrl) {
|
|
4012
|
+
try {
|
|
4013
|
+
const dir = fileURLToPath(importMetaUrl);
|
|
4014
|
+
const pkgRoot = packageDirectorySync({ cwd: dir });
|
|
4015
|
+
if (!pkgRoot)
|
|
4016
|
+
return 'unknown';
|
|
4017
|
+
const raw = readFileSync(join(pkgRoot, 'package.json'), 'utf-8');
|
|
4018
|
+
const pkg = JSON.parse(raw);
|
|
4019
|
+
return typeof pkg.version === 'string' ? pkg.version : 'unknown';
|
|
4020
|
+
}
|
|
4021
|
+
catch {
|
|
4022
|
+
return 'unknown';
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
|
|
3954
4026
|
/**
|
|
3955
4027
|
* Plugin resolution helpers for the OpenClaw plugin SDK.
|
|
3956
4028
|
*
|
|
@@ -4022,4 +4094,378 @@ function resolveOptionalPluginSetting(api, pluginId, key, envVar) {
|
|
|
4022
4094
|
return undefined;
|
|
4023
4095
|
}
|
|
4024
4096
|
|
|
4025
|
-
|
|
4097
|
+
/**
|
|
4098
|
+
* Shared filesystem utilities for runner job scripts.
|
|
4099
|
+
*
|
|
4100
|
+
* @module
|
|
4101
|
+
*/
|
|
4102
|
+
// ========== Time & UUID ==========
|
|
4103
|
+
/** Return current time as ISO 8601 string. */
|
|
4104
|
+
function nowIso() {
|
|
4105
|
+
return new Date().toISOString();
|
|
4106
|
+
}
|
|
4107
|
+
/** Generate a random UUID v4. */
|
|
4108
|
+
function uuid() {
|
|
4109
|
+
return crypto.randomUUID();
|
|
4110
|
+
}
|
|
4111
|
+
// ========== File System ==========
|
|
4112
|
+
/** Create a directory and any missing parents. */
|
|
4113
|
+
function ensureDir(p) {
|
|
4114
|
+
fs.mkdirSync(p, { recursive: true });
|
|
4115
|
+
}
|
|
4116
|
+
/** Read and parse a JSON file, returning `fallback` on any error. */
|
|
4117
|
+
function readJson(p, fallback) {
|
|
4118
|
+
try {
|
|
4119
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
4120
|
+
}
|
|
4121
|
+
catch {
|
|
4122
|
+
return fallback;
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
4125
|
+
/** Atomically write a JSON file (write to .tmp, then rename). */
|
|
4126
|
+
function writeJsonAtomic(p, obj) {
|
|
4127
|
+
ensureDir(path.dirname(p));
|
|
4128
|
+
const tmp = p + '.tmp';
|
|
4129
|
+
fs.writeFileSync(tmp, JSON.stringify(obj, null, 2) + '\n', 'utf8');
|
|
4130
|
+
fs.renameSync(tmp, p);
|
|
4131
|
+
}
|
|
4132
|
+
/** Append a single JSON object as a JSONL line. */
|
|
4133
|
+
function appendJsonl(p, obj) {
|
|
4134
|
+
ensureDir(path.dirname(p));
|
|
4135
|
+
fs.appendFileSync(p, JSON.stringify(obj) + '\n', 'utf8');
|
|
4136
|
+
}
|
|
4137
|
+
/** Read a JSONL file into an array of parsed objects. */
|
|
4138
|
+
function readJsonl(p) {
|
|
4139
|
+
try {
|
|
4140
|
+
const content = fs.readFileSync(p, 'utf8');
|
|
4141
|
+
return content
|
|
4142
|
+
.split('\n')
|
|
4143
|
+
.filter((line) => line.trim())
|
|
4144
|
+
.map((line) => JSON.parse(line));
|
|
4145
|
+
}
|
|
4146
|
+
catch {
|
|
4147
|
+
return [];
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
/** Overwrite a file with an array of objects as JSONL. */
|
|
4151
|
+
function writeJsonl(p, entries) {
|
|
4152
|
+
ensureDir(path.dirname(p));
|
|
4153
|
+
const content = entries.map((e) => JSON.stringify(e)).join('\n') + '\n';
|
|
4154
|
+
fs.writeFileSync(p, content, 'utf8');
|
|
4155
|
+
}
|
|
4156
|
+
// ========== Process Control ==========
|
|
4157
|
+
/** Synchronous sleep using Atomics.wait. */
|
|
4158
|
+
function sleepMs(ms) {
|
|
4159
|
+
const sab = new SharedArrayBuffer(4);
|
|
4160
|
+
const ia = new Int32Array(sab);
|
|
4161
|
+
Atomics.wait(ia, 0, 0, ms);
|
|
4162
|
+
}
|
|
4163
|
+
/** Async sleep via setTimeout. */
|
|
4164
|
+
function sleepAsync(ms) {
|
|
4165
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
4166
|
+
}
|
|
4167
|
+
// ========== Env File Loader ==========
|
|
4168
|
+
/** Load a .env-style key=value file into process.env. */
|
|
4169
|
+
function loadEnvFile(envPath) {
|
|
4170
|
+
if (!fs.existsSync(envPath))
|
|
4171
|
+
throw new Error(`Missing secret file: ${envPath}`);
|
|
4172
|
+
for (const line of fs.readFileSync(envPath, 'utf8').split(/\r?\n/)) {
|
|
4173
|
+
const trimmed = line.trim();
|
|
4174
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
4175
|
+
continue;
|
|
4176
|
+
const idx = trimmed.indexOf('=');
|
|
4177
|
+
if (idx > 0) {
|
|
4178
|
+
process.env[trimmed.slice(0, idx).trim()] = trimmed.slice(idx + 1).trim();
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
// ========== CLI Arg Parsing ==========
|
|
4183
|
+
/** Parse --key=value arguments from argv into a record. */
|
|
4184
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
4185
|
+
const args = {};
|
|
4186
|
+
for (const arg of argv) {
|
|
4187
|
+
const match = /^--([^=]+)=(.*)$/.exec(arg);
|
|
4188
|
+
if (match) {
|
|
4189
|
+
args[match[1]] = match[2];
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
return args;
|
|
4193
|
+
}
|
|
4194
|
+
/** Get the value following a named flag, or a default. */
|
|
4195
|
+
function getArg(argv, name, defaultValue) {
|
|
4196
|
+
const i = argv.indexOf(name);
|
|
4197
|
+
if (i >= 0 && i + 1 < argv.length)
|
|
4198
|
+
return argv[i + 1];
|
|
4199
|
+
return defaultValue;
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
/**
|
|
4203
|
+
* Google API auth helpers for runner job scripts.
|
|
4204
|
+
* Supports OAuth refresh tokens and service account impersonation.
|
|
4205
|
+
*
|
|
4206
|
+
* @module
|
|
4207
|
+
*/
|
|
4208
|
+
// ========== JWT / Service Account ==========
|
|
4209
|
+
function base64url(buf) {
|
|
4210
|
+
return buf
|
|
4211
|
+
.toString('base64')
|
|
4212
|
+
.replace(/=/g, '')
|
|
4213
|
+
.replace(/\+/g, '-')
|
|
4214
|
+
.replace(/\//g, '_');
|
|
4215
|
+
}
|
|
4216
|
+
function createJwt(serviceAccount, scopes, subject) {
|
|
4217
|
+
const now = Math.floor(Date.now() / 1000);
|
|
4218
|
+
const header = { alg: 'RS256', typ: 'JWT' };
|
|
4219
|
+
const payload = {
|
|
4220
|
+
iss: serviceAccount.client_email,
|
|
4221
|
+
sub: subject,
|
|
4222
|
+
scope: scopes.join(' '),
|
|
4223
|
+
aud: serviceAccount.token_uri,
|
|
4224
|
+
iat: now,
|
|
4225
|
+
exp: now + 3600,
|
|
4226
|
+
};
|
|
4227
|
+
const headerB64 = base64url(Buffer.from(JSON.stringify(header)));
|
|
4228
|
+
const payloadB64 = base64url(Buffer.from(JSON.stringify(payload)));
|
|
4229
|
+
const unsigned = `${headerB64}.${payloadB64}`;
|
|
4230
|
+
const sign = crypto.createSign('RSA-SHA256');
|
|
4231
|
+
sign.update(unsigned);
|
|
4232
|
+
const signature = base64url(sign.sign(serviceAccount.private_key));
|
|
4233
|
+
return `${unsigned}.${signature}`;
|
|
4234
|
+
}
|
|
4235
|
+
async function getServiceAccountToken(serviceAccount, scopes, subject) {
|
|
4236
|
+
const jwt = createJwt(serviceAccount, scopes, subject);
|
|
4237
|
+
const resp = await fetch(serviceAccount.token_uri, {
|
|
4238
|
+
method: 'POST',
|
|
4239
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
4240
|
+
body: new URLSearchParams({
|
|
4241
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
4242
|
+
assertion: jwt,
|
|
4243
|
+
}),
|
|
4244
|
+
});
|
|
4245
|
+
if (!resp.ok) {
|
|
4246
|
+
const body = await resp.text();
|
|
4247
|
+
throw new Error(`Service account token failed (${String(resp.status)}): ${body}`);
|
|
4248
|
+
}
|
|
4249
|
+
const data = (await resp.json());
|
|
4250
|
+
return data.access_token;
|
|
4251
|
+
}
|
|
4252
|
+
// ========== OAuth Refresh ==========
|
|
4253
|
+
async function getOAuthToken(refreshToken, client) {
|
|
4254
|
+
const resp = await fetch('https://oauth2.googleapis.com/token', {
|
|
4255
|
+
method: 'POST',
|
|
4256
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
4257
|
+
body: new URLSearchParams({
|
|
4258
|
+
client_id: client.client_id,
|
|
4259
|
+
client_secret: client.client_secret,
|
|
4260
|
+
refresh_token: refreshToken,
|
|
4261
|
+
grant_type: 'refresh_token',
|
|
4262
|
+
}),
|
|
4263
|
+
});
|
|
4264
|
+
if (!resp.ok) {
|
|
4265
|
+
const body = await resp.text();
|
|
4266
|
+
throw new Error(`OAuth token refresh failed (${String(resp.status)}): ${body}`);
|
|
4267
|
+
}
|
|
4268
|
+
const data = (await resp.json());
|
|
4269
|
+
return data.access_token;
|
|
4270
|
+
}
|
|
4271
|
+
// ========== Unified Auth ==========
|
|
4272
|
+
/**
|
|
4273
|
+
* Create a Google auth helper with the given configuration.
|
|
4274
|
+
* Returns a function that resolves an access token for a given account and scopes.
|
|
4275
|
+
*/
|
|
4276
|
+
function createGoogleAuth(options) {
|
|
4277
|
+
const { clientCredentialsPath, credentialsDir, serviceAccountDir } = options;
|
|
4278
|
+
let _oauthClient = null;
|
|
4279
|
+
function getOAuthClient() {
|
|
4280
|
+
if (!_oauthClient) {
|
|
4281
|
+
_oauthClient = JSON.parse(fs.readFileSync(clientCredentialsPath, 'utf8'));
|
|
4282
|
+
}
|
|
4283
|
+
return _oauthClient;
|
|
4284
|
+
}
|
|
4285
|
+
/**
|
|
4286
|
+
* Get an access token for the given account and scopes.
|
|
4287
|
+
*/
|
|
4288
|
+
async function getAccessToken(account, scopes) {
|
|
4289
|
+
if (account.serviceAccount) {
|
|
4290
|
+
const saDir = serviceAccountDir ?? credentialsDir;
|
|
4291
|
+
const saPath = typeof account.serviceAccount === 'string'
|
|
4292
|
+
? account.serviceAccount
|
|
4293
|
+
: path.join(saDir, account.serviceAccount.file);
|
|
4294
|
+
const sa = JSON.parse(fs.readFileSync(saPath, 'utf8'));
|
|
4295
|
+
return getServiceAccountToken(sa, scopes, account.email);
|
|
4296
|
+
}
|
|
4297
|
+
if (account.tokenFile) {
|
|
4298
|
+
const tokenPath = path.join(credentialsDir, account.tokenFile);
|
|
4299
|
+
const tokenData = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
|
|
4300
|
+
return getOAuthToken(tokenData.refresh_token, getOAuthClient());
|
|
4301
|
+
}
|
|
4302
|
+
throw new Error(`No auth method configured for ${account.email}`);
|
|
4303
|
+
}
|
|
4304
|
+
return {
|
|
4305
|
+
/** Get an access token for the given account and scopes. */
|
|
4306
|
+
getAccessToken,
|
|
4307
|
+
};
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
/**
|
|
4311
|
+
* Shared crash-handler wrapper for runner job scripts.
|
|
4312
|
+
* Catches uncaught errors, logs them, and exits with code 1.
|
|
4313
|
+
*
|
|
4314
|
+
* @module
|
|
4315
|
+
*/
|
|
4316
|
+
/**
|
|
4317
|
+
* Wrap a script's main function with crash handling.
|
|
4318
|
+
* On uncaught errors, appends to `_crash.log` in `crashDir` and exits.
|
|
4319
|
+
*
|
|
4320
|
+
* @param name - Script identifier for the crash log.
|
|
4321
|
+
* @param fn - Main function to execute (sync or async).
|
|
4322
|
+
* @param crashDir - Directory for crash logs (default: current working directory).
|
|
4323
|
+
*/
|
|
4324
|
+
function runScript(name, fn, crashDir = process.cwd()) {
|
|
4325
|
+
const execute = () => {
|
|
4326
|
+
const result = fn();
|
|
4327
|
+
if (result instanceof Promise) {
|
|
4328
|
+
result.catch((err) => {
|
|
4329
|
+
handleCrash(name, err, crashDir);
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
};
|
|
4333
|
+
try {
|
|
4334
|
+
execute();
|
|
4335
|
+
}
|
|
4336
|
+
catch (err) {
|
|
4337
|
+
handleCrash(name, err, crashDir);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
function handleCrash(name, err, crashDir) {
|
|
4341
|
+
const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
4342
|
+
const entry = `[${new Date().toISOString()}] CRASH (${name}): ${message}\n`;
|
|
4343
|
+
try {
|
|
4344
|
+
fs.mkdirSync(crashDir, { recursive: true });
|
|
4345
|
+
fs.appendFileSync(path.join(crashDir, '_crash.log'), entry);
|
|
4346
|
+
}
|
|
4347
|
+
catch {
|
|
4348
|
+
// Best effort — don't crash the crash handler.
|
|
4349
|
+
}
|
|
4350
|
+
console.error(entry);
|
|
4351
|
+
process.exit(1);
|
|
4352
|
+
}
|
|
4353
|
+
|
|
4354
|
+
/**
|
|
4355
|
+
* Shell execution utilities for runner job scripts.
|
|
4356
|
+
*
|
|
4357
|
+
* @module
|
|
4358
|
+
*/
|
|
4359
|
+
/**
|
|
4360
|
+
* Run a command synchronously and return trimmed stdout.
|
|
4361
|
+
* Throws on non-zero exit code.
|
|
4362
|
+
*/
|
|
4363
|
+
function run(cmd, args, opts = {}) {
|
|
4364
|
+
const r = cp.spawnSync(cmd, args, {
|
|
4365
|
+
encoding: 'utf8',
|
|
4366
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
4367
|
+
...opts,
|
|
4368
|
+
});
|
|
4369
|
+
if (r.error)
|
|
4370
|
+
throw r.error;
|
|
4371
|
+
const stdout = r.stdout || '';
|
|
4372
|
+
const stderr = r.stderr || '';
|
|
4373
|
+
if (r.status !== 0) {
|
|
4374
|
+
const msg = (stderr || stdout).trim();
|
|
4375
|
+
throw new Error(`${cmd} ${args.join(' ')} failed (exit ${String(r.status)}): ${msg}`);
|
|
4376
|
+
}
|
|
4377
|
+
return stdout.trim();
|
|
4378
|
+
}
|
|
4379
|
+
/**
|
|
4380
|
+
* Run a command with automatic retries on transient failures.
|
|
4381
|
+
* Uses exponential backoff between attempts.
|
|
4382
|
+
*/
|
|
4383
|
+
function runWithRetry(cmd, args, opts = {}) {
|
|
4384
|
+
const { retries = 2, backoffMs = 5000, isRetryable, ...runOpts } = opts;
|
|
4385
|
+
let lastErr;
|
|
4386
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
4387
|
+
try {
|
|
4388
|
+
return run(cmd, args, runOpts);
|
|
4389
|
+
}
|
|
4390
|
+
catch (e) {
|
|
4391
|
+
lastErr = e;
|
|
4392
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4393
|
+
const retryable = isRetryable
|
|
4394
|
+
? isRetryable(e)
|
|
4395
|
+
: /context deadline exceeded|timed out|timeout/i.test(msg);
|
|
4396
|
+
if (!retryable || attempt === retries)
|
|
4397
|
+
throw lastErr;
|
|
4398
|
+
sleepMs(backoffMs * Math.pow(2, attempt));
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
throw lastErr;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
/**
|
|
4405
|
+
* Slack channel → workspace mapping cache.
|
|
4406
|
+
* Resolves which Slack workspace owns a given channel.
|
|
4407
|
+
*
|
|
4408
|
+
* @module
|
|
4409
|
+
*/
|
|
4410
|
+
let _cache = null;
|
|
4411
|
+
let _dirty = false;
|
|
4412
|
+
let _cachePath = '';
|
|
4413
|
+
function loadCache(cachePath) {
|
|
4414
|
+
if (!_cache || _cachePath !== cachePath) {
|
|
4415
|
+
_cachePath = cachePath;
|
|
4416
|
+
try {
|
|
4417
|
+
_cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
4418
|
+
}
|
|
4419
|
+
catch {
|
|
4420
|
+
_cache = {};
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
return _cache;
|
|
4424
|
+
}
|
|
4425
|
+
/** Flush pending cache changes to disk. */
|
|
4426
|
+
function saveCache() {
|
|
4427
|
+
if (_dirty && _cache && _cachePath) {
|
|
4428
|
+
fs.writeFileSync(_cachePath, JSON.stringify(_cache, null, 2) + '\n');
|
|
4429
|
+
_dirty = false;
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
async function queryChannelWorkspace(channelId, token, defaultWorkspace) {
|
|
4433
|
+
const url = `https://slack.com/api/conversations.info?channel=${channelId}`;
|
|
4434
|
+
try {
|
|
4435
|
+
const res = await fetch(url, {
|
|
4436
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4437
|
+
});
|
|
4438
|
+
if (!res.ok)
|
|
4439
|
+
return defaultWorkspace;
|
|
4440
|
+
const j = (await res.json());
|
|
4441
|
+
if (!j.ok)
|
|
4442
|
+
return defaultWorkspace;
|
|
4443
|
+
const shared = j.channel?.shared_team_ids ?? [];
|
|
4444
|
+
if (shared.length > 0 && !shared.includes(defaultWorkspace)) {
|
|
4445
|
+
return shared[0];
|
|
4446
|
+
}
|
|
4447
|
+
return defaultWorkspace;
|
|
4448
|
+
}
|
|
4449
|
+
catch {
|
|
4450
|
+
return defaultWorkspace;
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
/**
|
|
4454
|
+
* Resolve the workspace team ID that owns a Slack channel.
|
|
4455
|
+
* Results are cached to disk.
|
|
4456
|
+
*
|
|
4457
|
+
* @param channelId - Slack channel ID.
|
|
4458
|
+
* @param token - Slack bot token for API calls.
|
|
4459
|
+
* @param options - Cache path and default workspace.
|
|
4460
|
+
*/
|
|
4461
|
+
async function getChannelWorkspace(channelId, token, options) {
|
|
4462
|
+
const cache = loadCache(options.cachePath);
|
|
4463
|
+
if (cache[channelId])
|
|
4464
|
+
return cache[channelId];
|
|
4465
|
+
const teamId = await queryChannelWorkspace(channelId, token, options.defaultWorkspace);
|
|
4466
|
+
cache[channelId] = teamId;
|
|
4467
|
+
_dirty = true;
|
|
4468
|
+
return teamId;
|
|
4469
|
+
}
|
|
4470
|
+
|
|
4471
|
+
export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_FILES, appendJsonl, atomicWrite, buildHeartbeatSection, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
|
package/package.json
CHANGED