larkin 0.2.70
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/CONTRIBUTING.md +48 -0
- package/LICENSE +201 -0
- package/README.md +122 -0
- package/SECURITY.md +14 -0
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +5488 -0
- package/dist/agent/agent-cli-capabilities.cjs +43 -0
- package/dist/agent/agent-cli-capabilities.mjs +35 -0
- package/dist/agent/agent-state-store.cjs +961 -0
- package/dist/agent/agent-state-store.mjs +923 -0
- package/dist/agent/agent-transport.cjs +201 -0
- package/dist/agent/agent-transport.mjs +194 -0
- package/dist/agent/config-cli-contract.cjs +19 -0
- package/dist/agent/config-cli-contract.mjs +16 -0
- package/dist/agent/context-prompt.cjs +159 -0
- package/dist/agent/context-prompt.mjs +155 -0
- package/dist/agent/freshness-gate.mjs +24 -0
- package/dist/agent/history-projection.cjs +72 -0
- package/dist/agent/history-projection.mjs +65 -0
- package/dist/agent/host-reminder-orchestrator.cjs +289 -0
- package/dist/agent/host-reminder-orchestrator.mjs +252 -0
- package/dist/agent/inbox-projection.cjs +92 -0
- package/dist/agent/inbox-projection.mjs +85 -0
- package/dist/agent/interaction-state-machine.cjs +710 -0
- package/dist/agent/interaction-state-machine.mjs +705 -0
- package/dist/agent/reminder-routes.cjs +192 -0
- package/dist/agent/reminder-routes.mjs +156 -0
- package/dist/agent/reminder-store.cjs +220 -0
- package/dist/agent/reminder-store.mjs +205 -0
- package/dist/agent/transport-business-context.cjs +486 -0
- package/dist/agent/transport-business-context.mjs +446 -0
- package/dist/agent/transport-shell.cjs +45 -0
- package/dist/agent/transport-shell.mjs +39 -0
- package/dist/app/agent-cli.mjs +596 -0
- package/dist/app/agent-config.mjs +633 -0
- package/dist/app/agent-lark-cli-workspace.cjs +95 -0
- package/dist/app/agent-lark-cli-workspace.mjs +85 -0
- package/dist/app/agent-readiness.cjs +42 -0
- package/dist/app/agent-readiness.mjs +38 -0
- package/dist/app/binary-entry.mjs +86 -0
- package/dist/app/cli.mjs +176 -0
- package/dist/app/dashboard.mjs +205 -0
- package/dist/app/internal-command.cjs +57 -0
- package/dist/app/internal-command.mjs +48 -0
- package/dist/app/lark-channel-secret.mjs +22 -0
- package/dist/app/lark-cli.mjs +887 -0
- package/dist/app/lark.mjs +45 -0
- package/dist/app/local-control.mjs +728 -0
- package/dist/app/official-lark-cli.cjs +138 -0
- package/dist/app/official-lark-cli.mjs +128 -0
- package/dist/app/pi-auth-cli.mjs +67 -0
- package/dist/app/run.mjs +364 -0
- package/dist/app/runtime-agent-config.mjs +416 -0
- package/dist/app/runtime-model-directory.mjs +78 -0
- package/dist/app/runtime-process.mjs +113 -0
- package/dist/app/session-cli.mjs +83 -0
- package/dist/app/setup-dashboard.mjs +26 -0
- package/dist/app/setup.mjs +166 -0
- package/dist/app/telemetry.mjs +83 -0
- package/dist/dashboard/dashboard-assets.mjs +21 -0
- package/dist/dashboard/dashboard-avatar.mjs +128 -0
- package/dist/dashboard/dashboard-config-controller.mjs +525 -0
- package/dist/dashboard/dashboard-lifecycle.cjs +97 -0
- package/dist/dashboard/dashboard-lifecycle.mjs +88 -0
- package/dist/dashboard/dashboard-template.mjs +29 -0
- package/dist/dashboard/dashboard-view-model.mjs +556 -0
- package/dist/dashboard/dashboard-workspace.mjs +104 -0
- package/dist/dashboard/web/assets/dashboard.css +3 -0
- package/dist/dashboard/web/assets/dashboard.js +49 -0
- package/dist/feishu/channel-transport.cjs +151 -0
- package/dist/feishu/channel-transport.mjs +148 -0
- package/dist/feishu/document-comment.cjs +120 -0
- package/dist/feishu/document-comment.mjs +109 -0
- package/dist/feishu/host-business-state.cjs +477 -0
- package/dist/feishu/host-business-state.mjs +462 -0
- package/dist/feishu/host-channel-business.cjs +161 -0
- package/dist/feishu/host-channel-business.mjs +157 -0
- package/dist/feishu/host-processing-eye.cjs +185 -0
- package/dist/feishu/host-processing-eye.mjs +181 -0
- package/dist/feishu/host-shell.cjs +1425 -0
- package/dist/feishu/host-shell.mjs +1385 -0
- package/dist/feishu/im-freshness-adapter.mjs +89 -0
- package/dist/feishu/interaction-orchestrator.cjs +279 -0
- package/dist/feishu/interaction-orchestrator.mjs +242 -0
- package/dist/feishu/lark-passthrough.mjs +70 -0
- package/dist/feishu/message-policy.cjs +111 -0
- package/dist/feishu/message-policy.mjs +98 -0
- package/dist/feishu/outbound-transport.cjs +221 -0
- package/dist/feishu/outbound-transport.mjs +215 -0
- package/dist/platform/build-info.cjs +25 -0
- package/dist/platform/build-info.mjs +18 -0
- package/dist/platform/callback-capability.cjs +301 -0
- package/dist/platform/callback-capability.mjs +252 -0
- package/dist/platform/check-bun-version.cjs +12 -0
- package/dist/platform/config.cjs +751 -0
- package/dist/platform/config.mjs +694 -0
- package/dist/platform/process-inspect.cjs +89 -0
- package/dist/platform/process-state.cjs +237 -0
- package/dist/platform/process-state.mjs +223 -0
- package/dist/platform/release-artifacts.mjs +61 -0
- package/dist/platform/root-layout.cjs +111 -0
- package/dist/platform/root-layout.mjs +72 -0
- package/dist/platform/telemetry-config.cjs +64 -0
- package/dist/platform/telemetry-config.mjs +56 -0
- package/dist/platform/telemetry-spool.cjs +661 -0
- package/dist/platform/telemetry-spool.mjs +653 -0
- package/dist/platform/telemetry-tracing.cjs +602 -0
- package/dist/platform/telemetry-tracing.mjs +596 -0
- package/dist/platform/telemetry-uploader.cjs +150 -0
- package/dist/platform/telemetry-uploader.mjs +146 -0
- package/dist/platform/workspace-service.cjs +469 -0
- package/dist/platform/workspace-service.mjs +430 -0
- package/dist/runtime/builtin-pi-assets.mjs +33 -0
- package/dist/runtime/claude-model-catalog.cjs +102 -0
- package/dist/runtime/claude-model-catalog.mjs +99 -0
- package/dist/runtime/codex-model-catalog.cjs +108 -0
- package/dist/runtime/codex-model-catalog.mjs +105 -0
- package/dist/runtime/pi-bash-timeout-extension.mjs +60 -0
- package/dist/runtime/pi-bash-timeout-injection.mjs +60 -0
- package/dist/runtime/pi-bash-timeout.bundle.js +43 -0
- package/dist/runtime/pi-model-catalog.cjs +116 -0
- package/dist/runtime/pi-model-catalog.mjs +75 -0
- package/dist/runtime/pi-official-auth.mjs +480 -0
- package/dist/runtime/pi-provider-config.cjs +252 -0
- package/dist/runtime/pi-provider-config.mjs +239 -0
- package/dist/runtime/pi-rpc-client.cjs +255 -0
- package/dist/runtime/pi-rpc-client.mjs +251 -0
- package/dist/runtime/pi-subagent-injection.mjs +129 -0
- package/dist/runtime/pi-subagents.bundle.js +8990 -0
- package/dist/runtime/runtime-adapters.mjs +1019 -0
- package/dist/runtime/runtime-contracts.cjs +2 -0
- package/dist/runtime/runtime-contracts.mjs +1 -0
- package/dist/runtime/runtime-host.cjs +930 -0
- package/dist/runtime/runtime-host.mjs +922 -0
- package/dist/runtime/runtime-model-catalog.cjs +43 -0
- package/dist/runtime/runtime-model-catalog.mjs +40 -0
- package/dist/runtime/runtime-readiness.cjs +177 -0
- package/dist/runtime/runtime-readiness.mjs +133 -0
- package/dist/setup/bot-register.mjs +655 -0
- package/dist/setup/grant-scopes.mjs +133 -0
- package/dist/setup/run-credential-preflight.mjs +89 -0
- package/dist/setup/setup-agent-choice.mjs +172 -0
- package/dist/setup/setup-bind.mjs +337 -0
- package/dist/setup/setup-binding.mjs +52 -0
- package/package.json +112 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TargetRootLayout = void 0;
|
|
37
|
+
exports.resolveConfigDir = resolveConfigDir;
|
|
38
|
+
exports.planRootLayout = planRootLayout;
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const APP_ID = /^cli_[A-Za-z0-9]+$/;
|
|
41
|
+
function requireConfigDir(configDir) {
|
|
42
|
+
if (typeof configDir !== "string" || !configDir.trim())
|
|
43
|
+
throw new Error("config dir must be non-empty");
|
|
44
|
+
return path.resolve(configDir);
|
|
45
|
+
}
|
|
46
|
+
function validateAgentId(agentId) {
|
|
47
|
+
if (typeof agentId !== "string" || !APP_ID.test(agentId)) {
|
|
48
|
+
throw new Error(`invalid App ID agent id: ${JSON.stringify(agentId)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function resolveConfigDir(env, home) {
|
|
52
|
+
const explicit = env?.LARKIN_CONFIG_DIR;
|
|
53
|
+
if (!explicit && (typeof home !== "string" || !home.trim()))
|
|
54
|
+
throw new Error("home must be non-empty");
|
|
55
|
+
return path.resolve(explicit || path.join(home, ".larkin"));
|
|
56
|
+
}
|
|
57
|
+
class TargetRootLayout {
|
|
58
|
+
root;
|
|
59
|
+
configDir;
|
|
60
|
+
larkinHome;
|
|
61
|
+
configFile;
|
|
62
|
+
daemonStatusFile;
|
|
63
|
+
dashboardStatusFile;
|
|
64
|
+
constructor(configDir) {
|
|
65
|
+
this.root = requireConfigDir(configDir);
|
|
66
|
+
this.configDir = this.root;
|
|
67
|
+
this.larkinHome = this.root;
|
|
68
|
+
this.configFile = path.join(this.root, "config.json");
|
|
69
|
+
this.daemonStatusFile = path.join(this.root, "daemon-status.json");
|
|
70
|
+
this.dashboardStatusFile = path.join(this.root, "dashboard-status.json");
|
|
71
|
+
Object.freeze(this);
|
|
72
|
+
}
|
|
73
|
+
static fromConfigDir(configDir) {
|
|
74
|
+
return new TargetRootLayout(configDir);
|
|
75
|
+
}
|
|
76
|
+
workspaceDir(agentId) {
|
|
77
|
+
validateAgentId(agentId);
|
|
78
|
+
return path.join(this.root, "agents", agentId);
|
|
79
|
+
}
|
|
80
|
+
agentStateDir(agentId) {
|
|
81
|
+
validateAgentId(agentId);
|
|
82
|
+
return path.join(this.root, "state", "agents", agentId);
|
|
83
|
+
}
|
|
84
|
+
agentStatePaths(agentId) {
|
|
85
|
+
const root = this.agentStateDir(agentId);
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
root,
|
|
88
|
+
agentState: path.join(root, "agent-state.json"),
|
|
89
|
+
status: path.join(root, "status.json"),
|
|
90
|
+
map: path.join(root, "feishu-map.json"),
|
|
91
|
+
replyctx: path.join(root, "feishu-replyctx.json"),
|
|
92
|
+
botIdentity: path.join(root, "bot-identity.json"),
|
|
93
|
+
senderProfiles: path.join(root, "sender-profiles.json"),
|
|
94
|
+
readReceipts: path.join(root, "feishu-read.json"),
|
|
95
|
+
pendingReact: path.join(root, "feishu-pending-react.json"),
|
|
96
|
+
runtimeDeliveries: path.join(root, "runtime-deliveries.json"),
|
|
97
|
+
inboxState: path.join(root, "inbox-state.json"),
|
|
98
|
+
freshnessState: path.join(root, "freshness-state.json"),
|
|
99
|
+
documentComments: path.join(root, "document-comments.json"),
|
|
100
|
+
interactions: path.join(root, "interactions.json"),
|
|
101
|
+
conversation: path.join(root, "conversation.ndjson"),
|
|
102
|
+
inbox: path.join(root, "feishu-inbox.ndjson"),
|
|
103
|
+
reminders: path.join(root, "reminders.json"),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.TargetRootLayout = TargetRootLayout;
|
|
108
|
+
function planRootLayout({ root, agentId }) {
|
|
109
|
+
const target = TargetRootLayout.fromConfigDir(root);
|
|
110
|
+
return { root: target.root, workspaceDir: target.workspaceDir(agentId), stateDir: target.agentStateDir(agentId) };
|
|
111
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
const APP_ID = /^cli_[A-Za-z0-9]+$/;
|
|
3
|
+
function requireConfigDir(configDir) {
|
|
4
|
+
if (typeof configDir !== "string" || !configDir.trim())
|
|
5
|
+
throw new Error("config dir must be non-empty");
|
|
6
|
+
return path.resolve(configDir);
|
|
7
|
+
}
|
|
8
|
+
function validateAgentId(agentId) {
|
|
9
|
+
if (typeof agentId !== "string" || !APP_ID.test(agentId)) {
|
|
10
|
+
throw new Error(`invalid App ID agent id: ${JSON.stringify(agentId)}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function resolveConfigDir(env, home) {
|
|
14
|
+
const explicit = env?.LARKIN_CONFIG_DIR;
|
|
15
|
+
if (!explicit && (typeof home !== "string" || !home.trim()))
|
|
16
|
+
throw new Error("home must be non-empty");
|
|
17
|
+
return path.resolve(explicit || path.join(home, ".larkin"));
|
|
18
|
+
}
|
|
19
|
+
export class TargetRootLayout {
|
|
20
|
+
root;
|
|
21
|
+
configDir;
|
|
22
|
+
larkinHome;
|
|
23
|
+
configFile;
|
|
24
|
+
daemonStatusFile;
|
|
25
|
+
dashboardStatusFile;
|
|
26
|
+
constructor(configDir) {
|
|
27
|
+
this.root = requireConfigDir(configDir);
|
|
28
|
+
this.configDir = this.root;
|
|
29
|
+
this.larkinHome = this.root;
|
|
30
|
+
this.configFile = path.join(this.root, "config.json");
|
|
31
|
+
this.daemonStatusFile = path.join(this.root, "daemon-status.json");
|
|
32
|
+
this.dashboardStatusFile = path.join(this.root, "dashboard-status.json");
|
|
33
|
+
Object.freeze(this);
|
|
34
|
+
}
|
|
35
|
+
static fromConfigDir(configDir) {
|
|
36
|
+
return new TargetRootLayout(configDir);
|
|
37
|
+
}
|
|
38
|
+
workspaceDir(agentId) {
|
|
39
|
+
validateAgentId(agentId);
|
|
40
|
+
return path.join(this.root, "agents", agentId);
|
|
41
|
+
}
|
|
42
|
+
agentStateDir(agentId) {
|
|
43
|
+
validateAgentId(agentId);
|
|
44
|
+
return path.join(this.root, "state", "agents", agentId);
|
|
45
|
+
}
|
|
46
|
+
agentStatePaths(agentId) {
|
|
47
|
+
const root = this.agentStateDir(agentId);
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
root,
|
|
50
|
+
agentState: path.join(root, "agent-state.json"),
|
|
51
|
+
status: path.join(root, "status.json"),
|
|
52
|
+
map: path.join(root, "feishu-map.json"),
|
|
53
|
+
replyctx: path.join(root, "feishu-replyctx.json"),
|
|
54
|
+
botIdentity: path.join(root, "bot-identity.json"),
|
|
55
|
+
senderProfiles: path.join(root, "sender-profiles.json"),
|
|
56
|
+
readReceipts: path.join(root, "feishu-read.json"),
|
|
57
|
+
pendingReact: path.join(root, "feishu-pending-react.json"),
|
|
58
|
+
runtimeDeliveries: path.join(root, "runtime-deliveries.json"),
|
|
59
|
+
inboxState: path.join(root, "inbox-state.json"),
|
|
60
|
+
freshnessState: path.join(root, "freshness-state.json"),
|
|
61
|
+
documentComments: path.join(root, "document-comments.json"),
|
|
62
|
+
interactions: path.join(root, "interactions.json"),
|
|
63
|
+
conversation: path.join(root, "conversation.ndjson"),
|
|
64
|
+
inbox: path.join(root, "feishu-inbox.ndjson"),
|
|
65
|
+
reminders: path.join(root, "reminders.json"),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function planRootLayout({ root, agentId }) {
|
|
70
|
+
const target = TargetRootLayout.fromConfigDir(root);
|
|
71
|
+
return { root: target.root, workspaceDir: target.workspaceDir(agentId), stateDir: target.agentStateDir(agentId) };
|
|
72
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateOtlpEndpoint = validateOtlpEndpoint;
|
|
7
|
+
exports.loadTelemetryConfig = loadTelemetryConfig;
|
|
8
|
+
exports.safeEndpointLabel = safeEndpointLabel;
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const root_layout_js_1 = require("./root-layout.cjs");
|
|
12
|
+
const positiveInteger = (value, fallback) => {
|
|
13
|
+
const parsed = Number(value);
|
|
14
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
15
|
+
};
|
|
16
|
+
function validateOtlpEndpoint(value) {
|
|
17
|
+
const url = new URL(value);
|
|
18
|
+
if (!['http:', 'https:'].includes(url.protocol))
|
|
19
|
+
throw new Error("OTLP endpoint must use http or https");
|
|
20
|
+
if (url.username || url.password)
|
|
21
|
+
throw new Error("OTLP endpoint must not contain userinfo");
|
|
22
|
+
if (url.hash)
|
|
23
|
+
throw new Error("OTLP endpoint must not contain a fragment");
|
|
24
|
+
if (url.pathname === "/" || url.pathname === "")
|
|
25
|
+
url.pathname = "/v1/traces";
|
|
26
|
+
return url.toString();
|
|
27
|
+
}
|
|
28
|
+
function parseHeaders(value) {
|
|
29
|
+
if (!value)
|
|
30
|
+
return {};
|
|
31
|
+
const headers = {};
|
|
32
|
+
for (const field of value.split(",")) {
|
|
33
|
+
const separator = field.indexOf("=");
|
|
34
|
+
if (separator <= 0)
|
|
35
|
+
throw new Error("OTLP headers must use name=value pairs");
|
|
36
|
+
const name = field.slice(0, separator).trim();
|
|
37
|
+
const headerValue = decodeURIComponent(field.slice(separator + 1).trim());
|
|
38
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\r\n]/.test(headerValue)) {
|
|
39
|
+
throw new Error("OTLP header is invalid");
|
|
40
|
+
}
|
|
41
|
+
headers[name] = headerValue;
|
|
42
|
+
}
|
|
43
|
+
return headers;
|
|
44
|
+
}
|
|
45
|
+
function loadTelemetryConfig(env = process.env) {
|
|
46
|
+
const home = env.LARKIN_HOME || (0, root_layout_js_1.resolveConfigDir)(env, node_os_1.default.homedir());
|
|
47
|
+
const endpointValue = env.LARKIN_TELEMETRY_OTLP_ENDPOINT || env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
|
|
48
|
+
return {
|
|
49
|
+
spoolDir: node_path_1.default.resolve(env.LARKIN_TELEMETRY_SPOOL_DIR || node_path_1.default.join(home, "telemetry", "spool")),
|
|
50
|
+
...(endpointValue ? { endpoint: validateOtlpEndpoint(endpointValue) } : {}),
|
|
51
|
+
headers: parseHeaders(env.LARKIN_TELEMETRY_OTLP_HEADERS || env.OTEL_EXPORTER_OTLP_HEADERS),
|
|
52
|
+
maxBytes: positiveInteger(env.LARKIN_TELEMETRY_MAX_BYTES, 64 * 1024 * 1024),
|
|
53
|
+
maxFiles: positiveInteger(env.LARKIN_TELEMETRY_MAX_FILES, 10_000),
|
|
54
|
+
maxAgeMs: positiveInteger(env.LARKIN_TELEMETRY_MAX_AGE_MS, 14 * 24 * 60 * 60 * 1000),
|
|
55
|
+
uploadIntervalMs: positiveInteger(env.LARKIN_TELEMETRY_UPLOAD_INTERVAL_MS, 10_000),
|
|
56
|
+
requestTimeoutMs: positiveInteger(env.LARKIN_TELEMETRY_REQUEST_TIMEOUT_MS, 10_000),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function safeEndpointLabel(endpoint) {
|
|
60
|
+
if (!endpoint)
|
|
61
|
+
return null;
|
|
62
|
+
const url = new URL(endpoint);
|
|
63
|
+
return `${url.protocol}//${url.host}`;
|
|
64
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { resolveConfigDir } from "./root-layout.mjs";
|
|
4
|
+
const positiveInteger = (value, fallback) => {
|
|
5
|
+
const parsed = Number(value);
|
|
6
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
7
|
+
};
|
|
8
|
+
export function validateOtlpEndpoint(value) {
|
|
9
|
+
const url = new URL(value);
|
|
10
|
+
if (!['http:', 'https:'].includes(url.protocol))
|
|
11
|
+
throw new Error("OTLP endpoint must use http or https");
|
|
12
|
+
if (url.username || url.password)
|
|
13
|
+
throw new Error("OTLP endpoint must not contain userinfo");
|
|
14
|
+
if (url.hash)
|
|
15
|
+
throw new Error("OTLP endpoint must not contain a fragment");
|
|
16
|
+
if (url.pathname === "/" || url.pathname === "")
|
|
17
|
+
url.pathname = "/v1/traces";
|
|
18
|
+
return url.toString();
|
|
19
|
+
}
|
|
20
|
+
function parseHeaders(value) {
|
|
21
|
+
if (!value)
|
|
22
|
+
return {};
|
|
23
|
+
const headers = {};
|
|
24
|
+
for (const field of value.split(",")) {
|
|
25
|
+
const separator = field.indexOf("=");
|
|
26
|
+
if (separator <= 0)
|
|
27
|
+
throw new Error("OTLP headers must use name=value pairs");
|
|
28
|
+
const name = field.slice(0, separator).trim();
|
|
29
|
+
const headerValue = decodeURIComponent(field.slice(separator + 1).trim());
|
|
30
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\r\n]/.test(headerValue)) {
|
|
31
|
+
throw new Error("OTLP header is invalid");
|
|
32
|
+
}
|
|
33
|
+
headers[name] = headerValue;
|
|
34
|
+
}
|
|
35
|
+
return headers;
|
|
36
|
+
}
|
|
37
|
+
export function loadTelemetryConfig(env = process.env) {
|
|
38
|
+
const home = env.LARKIN_HOME || resolveConfigDir(env, os.homedir());
|
|
39
|
+
const endpointValue = env.LARKIN_TELEMETRY_OTLP_ENDPOINT || env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
|
|
40
|
+
return {
|
|
41
|
+
spoolDir: path.resolve(env.LARKIN_TELEMETRY_SPOOL_DIR || path.join(home, "telemetry", "spool")),
|
|
42
|
+
...(endpointValue ? { endpoint: validateOtlpEndpoint(endpointValue) } : {}),
|
|
43
|
+
headers: parseHeaders(env.LARKIN_TELEMETRY_OTLP_HEADERS || env.OTEL_EXPORTER_OTLP_HEADERS),
|
|
44
|
+
maxBytes: positiveInteger(env.LARKIN_TELEMETRY_MAX_BYTES, 64 * 1024 * 1024),
|
|
45
|
+
maxFiles: positiveInteger(env.LARKIN_TELEMETRY_MAX_FILES, 10_000),
|
|
46
|
+
maxAgeMs: positiveInteger(env.LARKIN_TELEMETRY_MAX_AGE_MS, 14 * 24 * 60 * 60 * 1000),
|
|
47
|
+
uploadIntervalMs: positiveInteger(env.LARKIN_TELEMETRY_UPLOAD_INTERVAL_MS, 10_000),
|
|
48
|
+
requestTimeoutMs: positiveInteger(env.LARKIN_TELEMETRY_REQUEST_TIMEOUT_MS, 10_000),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function safeEndpointLabel(endpoint) {
|
|
52
|
+
if (!endpoint)
|
|
53
|
+
return null;
|
|
54
|
+
const url = new URL(endpoint);
|
|
55
|
+
return `${url.protocol}//${url.host}`;
|
|
56
|
+
}
|