@intutic/cli 1.10.0 → 2.0.0
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/README.md +1 -0
- package/dist/cli.js +868 -30
- package/dist/cli.js.map +1 -1
- package/dist/commands/attenuate.d.ts +39 -0
- package/dist/commands/attenuate.d.ts.map +1 -0
- package/dist/commands/attenuate.js +98 -0
- package/dist/commands/attenuate.js.map +1 -0
- package/dist/commands/budget.d.ts +80 -0
- package/dist/commands/budget.d.ts.map +1 -1
- package/dist/commands/budget.js +119 -3
- package/dist/commands/budget.js.map +1 -1
- package/dist/commands/connect.d.ts +10 -0
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +358 -48
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/credentials.d.ts +31 -0
- package/dist/commands/credentials.d.ts.map +1 -0
- package/dist/commands/credentials.js +134 -0
- package/dist/commands/credentials.js.map +1 -0
- package/dist/commands/decision.d.ts +37 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +56 -0
- package/dist/commands/decision.js.map +1 -0
- package/dist/commands/doctor.d.ts +38 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +228 -18
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enforce.d.ts +33 -0
- package/dist/commands/enforce.d.ts.map +1 -0
- package/dist/commands/enforce.js +150 -0
- package/dist/commands/enforce.js.map +1 -0
- package/dist/commands/enterprise.d.ts +29 -0
- package/dist/commands/enterprise.d.ts.map +1 -0
- package/dist/commands/enterprise.js +141 -0
- package/dist/commands/enterprise.js.map +1 -0
- package/dist/commands/env.d.ts +7 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +55 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/exec.d.ts +38 -2
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +247 -10
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/findings.d.ts +84 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +329 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/gateway.d.ts +62 -0
- package/dist/commands/gateway.d.ts.map +1 -0
- package/dist/commands/gateway.js +298 -0
- package/dist/commands/gateway.js.map +1 -0
- package/dist/commands/guardrails.d.ts +81 -0
- package/dist/commands/guardrails.d.ts.map +1 -0
- package/dist/commands/guardrails.js +735 -0
- package/dist/commands/guardrails.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-daemon.d.ts +70 -3
- package/dist/commands/install-daemon.d.ts.map +1 -1
- package/dist/commands/install-daemon.js +482 -47
- package/dist/commands/install-daemon.js.map +1 -1
- package/dist/commands/integrity.d.ts +205 -0
- package/dist/commands/integrity.d.ts.map +1 -0
- package/dist/commands/integrity.js +620 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/judge.d.ts +31 -0
- package/dist/commands/judge.d.ts.map +1 -0
- package/dist/commands/judge.js +118 -0
- package/dist/commands/judge.js.map +1 -0
- package/dist/commands/org.d.ts +31 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +159 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/policy.d.ts +138 -4
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +538 -97
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/predict-cost.d.ts +45 -0
- package/dist/commands/predict-cost.d.ts.map +1 -0
- package/dist/commands/predict-cost.js +123 -0
- package/dist/commands/predict-cost.js.map +1 -0
- package/dist/commands/rollback.d.ts +5 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +142 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/routing.d.ts +62 -0
- package/dist/commands/routing.d.ts.map +1 -0
- package/dist/commands/routing.js +121 -0
- package/dist/commands/routing.js.map +1 -0
- package/dist/commands/setup.d.ts +62 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +238 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/skill.d.ts +268 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +744 -39
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/sops.d.ts +95 -0
- package/dist/commands/sops.d.ts.map +1 -1
- package/dist/commands/sops.js +334 -34
- package/dist/commands/sops.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +1 -18
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +44 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/team.d.ts +38 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +140 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/traces.d.ts +8 -1
- package/dist/commands/traces.d.ts.map +1 -1
- package/dist/commands/traces.js +114 -15
- package/dist/commands/traces.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +2 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/keychain.d.ts +10 -2
- package/dist/config/keychain.d.ts.map +1 -1
- package/dist/config/keychain.js +83 -14
- package/dist/config/keychain.js.map +1 -1
- package/dist/config/paths.d.ts +27 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +37 -6
- package/dist/config/paths.js.map +1 -1
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +19 -3
- package/dist/config/store.js.map +1 -1
- package/dist/harness/ag2.d.ts +22 -0
- package/dist/harness/ag2.d.ts.map +1 -0
- package/dist/harness/ag2.js +34 -0
- package/dist/harness/ag2.js.map +1 -0
- package/dist/harness/agentcore.d.ts +55 -0
- package/dist/harness/agentcore.d.ts.map +1 -0
- package/dist/harness/agentcore.js +119 -0
- package/dist/harness/agentcore.js.map +1 -0
- package/dist/harness/agenticOrchestrator.d.ts +52 -0
- package/dist/harness/agenticOrchestrator.d.ts.map +1 -0
- package/dist/harness/agenticOrchestrator.js +115 -0
- package/dist/harness/agenticOrchestrator.js.map +1 -0
- package/dist/harness/aiSdkHarness.d.ts +30 -0
- package/dist/harness/aiSdkHarness.d.ts.map +1 -0
- package/dist/harness/aiSdkHarness.js +51 -0
- package/dist/harness/aiSdkHarness.js.map +1 -0
- package/dist/harness/aiSdkWorkflow.d.ts +26 -0
- package/dist/harness/aiSdkWorkflow.d.ts.map +1 -0
- package/dist/harness/aiSdkWorkflow.js +39 -0
- package/dist/harness/aiSdkWorkflow.js.map +1 -0
- package/dist/harness/autogen.d.ts +24 -0
- package/dist/harness/autogen.d.ts.map +1 -0
- package/dist/harness/autogen.js +36 -0
- package/dist/harness/autogen.js.map +1 -0
- package/dist/harness/base.js.map +1 -1
- package/dist/harness/continue.d.ts.map +1 -1
- package/dist/harness/continue.js +2 -1
- package/dist/harness/continue.js.map +1 -1
- package/dist/harness/crewai.d.ts +15 -0
- package/dist/harness/crewai.d.ts.map +1 -0
- package/dist/harness/crewai.js +26 -0
- package/dist/harness/crewai.js.map +1 -0
- package/dist/harness/detector.d.ts +1 -1
- package/dist/harness/detector.d.ts.map +1 -1
- package/dist/harness/detector.js +47 -1
- package/dist/harness/detector.js.map +1 -1
- package/dist/harness/dsh.d.ts +19 -0
- package/dist/harness/dsh.d.ts.map +1 -0
- package/dist/harness/dsh.js +93 -0
- package/dist/harness/dsh.js.map +1 -0
- package/dist/harness/eve.d.ts +40 -0
- package/dist/harness/eve.d.ts.map +1 -0
- package/dist/harness/eve.js +67 -0
- package/dist/harness/eve.js.map +1 -0
- package/dist/harness/googleAdk.d.ts +18 -0
- package/dist/harness/googleAdk.d.ts.map +1 -0
- package/dist/harness/googleAdk.js +29 -0
- package/dist/harness/googleAdk.js.map +1 -0
- package/dist/harness/goose.d.ts.map +1 -1
- package/dist/harness/goose.js +5 -1
- package/dist/harness/goose.js.map +1 -1
- package/dist/harness/grok.d.ts +16 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +78 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts +102 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.js +139 -0
- package/dist/harness/jsSdkGatedAdapter.js.map +1 -0
- package/dist/harness/langchain.d.ts +22 -0
- package/dist/harness/langchain.d.ts.map +1 -0
- package/dist/harness/langchain.js +33 -0
- package/dist/harness/langchain.js.map +1 -0
- package/dist/harness/langgraph.d.ts +22 -0
- package/dist/harness/langgraph.d.ts.map +1 -0
- package/dist/harness/langgraph.js +80 -0
- package/dist/harness/langgraph.js.map +1 -0
- package/dist/harness/mastra.d.ts +21 -0
- package/dist/harness/mastra.d.ts.map +1 -0
- package/dist/harness/mastra.js +33 -0
- package/dist/harness/mastra.js.map +1 -0
- package/dist/harness/muse.d.ts +22 -0
- package/dist/harness/muse.d.ts.map +1 -0
- package/dist/harness/muse.js +92 -0
- package/dist/harness/muse.js.map +1 -0
- package/dist/harness/n8n.d.ts.map +1 -1
- package/dist/harness/n8n.js +36 -11
- package/dist/harness/n8n.js.map +1 -1
- package/dist/harness/openaiAgents.d.ts +40 -0
- package/dist/harness/openaiAgents.d.ts.map +1 -0
- package/dist/harness/openaiAgents.js +107 -0
- package/dist/harness/openaiAgents.js.map +1 -0
- package/dist/harness/opencode.d.ts +24 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/pydanticAi.d.ts +19 -0
- package/dist/harness/pydanticAi.d.ts.map +1 -0
- package/dist/harness/pydanticAi.js +30 -0
- package/dist/harness/pydanticAi.js.map +1 -0
- package/dist/harness/sdkGatedAdapter.d.ts +55 -0
- package/dist/harness/sdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/sdkGatedAdapter.js +85 -0
- package/dist/harness/sdkGatedAdapter.js.map +1 -0
- package/dist/harness/smolagents.d.ts +22 -0
- package/dist/harness/smolagents.d.ts.map +1 -0
- package/dist/harness/smolagents.js +33 -0
- package/dist/harness/smolagents.js.map +1 -0
- package/dist/harness/strands.d.ts +22 -0
- package/dist/harness/strands.d.ts.map +1 -0
- package/dist/harness/strands.js +35 -0
- package/dist/harness/strands.js.map +1 -0
- package/dist/harness/trueforge.d.ts +25 -0
- package/dist/harness/trueforge.d.ts.map +1 -0
- package/dist/harness/trueforge.js +40 -0
- package/dist/harness/trueforge.js.map +1 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +70 -0
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/vercelAiSdk.d.ts +31 -0
- package/dist/harness/vercelAiSdk.d.ts.map +1 -0
- package/dist/harness/vercelAiSdk.js +44 -0
- package/dist/harness/vercelAiSdk.js.map +1 -0
- package/dist/harness/xirp.d.ts +43 -0
- package/dist/harness/xirp.d.ts.map +1 -0
- package/dist/harness/xirp.js +95 -0
- package/dist/harness/xirp.js.map +1 -0
- package/dist/lib/api.d.ts +68 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +108 -67
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/authMessages.d.ts +19 -0
- package/dist/lib/authMessages.d.ts.map +1 -0
- package/dist/lib/authMessages.js +20 -0
- package/dist/lib/authMessages.js.map +1 -0
- package/dist/lib/binaryChecksum.d.ts +12 -0
- package/dist/lib/binaryChecksum.d.ts.map +1 -0
- package/dist/lib/binaryChecksum.js +45 -0
- package/dist/lib/binaryChecksum.js.map +1 -0
- package/dist/lib/binaryOnPath.d.ts +24 -0
- package/dist/lib/binaryOnPath.d.ts.map +1 -0
- package/dist/lib/binaryOnPath.js +31 -0
- package/dist/lib/binaryOnPath.js.map +1 -0
- package/dist/lib/caTrust.d.ts +69 -0
- package/dist/lib/caTrust.d.ts.map +1 -0
- package/dist/lib/caTrust.js +169 -0
- package/dist/lib/caTrust.js.map +1 -0
- package/dist/lib/ciscoScanner.d.ts +178 -0
- package/dist/lib/ciscoScanner.d.ts.map +1 -0
- package/dist/lib/ciscoScanner.js +292 -0
- package/dist/lib/ciscoScanner.js.map +1 -0
- package/dist/lib/decisionsLogRefresh.d.ts +28 -0
- package/dist/lib/decisionsLogRefresh.d.ts.map +1 -0
- package/dist/lib/decisionsLogRefresh.js +54 -0
- package/dist/lib/decisionsLogRefresh.js.map +1 -0
- package/dist/lib/deviceReport.d.ts +26 -0
- package/dist/lib/deviceReport.d.ts.map +1 -0
- package/dist/lib/deviceReport.js +55 -0
- package/dist/lib/deviceReport.js.map +1 -0
- package/dist/lib/elevation.d.ts +58 -0
- package/dist/lib/elevation.d.ts.map +1 -0
- package/dist/lib/elevation.js +83 -0
- package/dist/lib/elevation.js.map +1 -0
- package/dist/lib/enforcementState.d.ts +69 -0
- package/dist/lib/enforcementState.d.ts.map +1 -0
- package/dist/lib/enforcementState.js +113 -0
- package/dist/lib/enforcementState.js.map +1 -0
- package/dist/lib/ensureValkey.d.ts.map +1 -1
- package/dist/lib/ensureValkey.js +2 -2
- package/dist/lib/ensureValkey.js.map +1 -1
- package/dist/lib/envInjector.d.ts.map +1 -1
- package/dist/lib/envInjector.js +22 -4
- package/dist/lib/envInjector.js.map +1 -1
- package/dist/lib/gitHooks.d.ts +7 -2
- package/dist/lib/gitHooks.d.ts.map +1 -1
- package/dist/lib/gitHooks.js +180 -13
- package/dist/lib/gitHooks.js.map +1 -1
- package/dist/lib/gracefulShutdown.d.ts +37 -0
- package/dist/lib/gracefulShutdown.d.ts.map +1 -0
- package/dist/lib/gracefulShutdown.js +57 -0
- package/dist/lib/gracefulShutdown.js.map +1 -0
- package/dist/lib/graphIdentity.d.ts +80 -0
- package/dist/lib/graphIdentity.d.ts.map +1 -0
- package/dist/lib/graphIdentity.js +120 -0
- package/dist/lib/graphIdentity.js.map +1 -0
- package/dist/lib/localTraces.d.ts +61 -0
- package/dist/lib/localTraces.d.ts.map +1 -0
- package/dist/lib/localTraces.js +173 -0
- package/dist/lib/localTraces.js.map +1 -0
- package/dist/lib/mdmManifest.d.ts +73 -0
- package/dist/lib/mdmManifest.d.ts.map +1 -0
- package/dist/lib/mdmManifest.js +190 -0
- package/dist/lib/mdmManifest.js.map +1 -0
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +60 -1
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/otelPropagation.d.ts +26 -0
- package/dist/lib/otelPropagation.d.ts.map +1 -0
- package/dist/lib/otelPropagation.js +40 -0
- package/dist/lib/otelPropagation.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +92 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +173 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/process.d.ts.map +1 -1
- package/dist/lib/process.js +60 -0
- package/dist/lib/process.js.map +1 -1
- package/dist/lib/providerProbe.d.ts +28 -0
- package/dist/lib/providerProbe.d.ts.map +1 -0
- package/dist/lib/providerProbe.js +57 -0
- package/dist/lib/providerProbe.js.map +1 -0
- package/dist/lib/proxyBinary.d.ts +19 -0
- package/dist/lib/proxyBinary.d.ts.map +1 -0
- package/dist/lib/proxyBinary.js +30 -0
- package/dist/lib/proxyBinary.js.map +1 -0
- package/dist/lib/sandbox/firecracker.d.ts +89 -0
- package/dist/lib/sandbox/firecracker.d.ts.map +1 -0
- package/dist/lib/sandbox/firecracker.js +135 -0
- package/dist/lib/sandbox/firecracker.js.map +1 -0
- package/dist/lib/sandbox/index.d.ts +34 -0
- package/dist/lib/sandbox/index.d.ts.map +1 -0
- package/dist/lib/sandbox/index.js +54 -0
- package/dist/lib/sandbox/index.js.map +1 -0
- package/dist/lib/sandbox/oci.d.ts +38 -0
- package/dist/lib/sandbox/oci.d.ts.map +1 -0
- package/dist/lib/sandbox/oci.js +108 -0
- package/dist/lib/sandbox/oci.js.map +1 -0
- package/dist/lib/sandbox/types.d.ts +48 -0
- package/dist/lib/sandbox/types.d.ts.map +1 -0
- package/dist/lib/sandbox/types.js +12 -0
- package/dist/lib/sandbox/types.js.map +1 -0
- package/dist/lib/sopFrontMatter.d.ts +50 -0
- package/dist/lib/sopFrontMatter.d.ts.map +1 -0
- package/dist/lib/sopFrontMatter.js +129 -0
- package/dist/lib/sopFrontMatter.js.map +1 -0
- package/package.json +11 -6
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* install-daemon.ts — System-level sync-daemon
|
|
2
|
+
* install-daemon.ts — System-level sync-daemon, mcp-daemon & proxy persistence installer.
|
|
3
3
|
*
|
|
4
|
-
* Registers the Intutic sync-daemon
|
|
5
|
-
* auto-start on login/boot and restart
|
|
4
|
+
* Registers the Intutic sync-daemon, mcp-daemon, or the standalone `intutic-proxy`
|
|
5
|
+
* binary as system-level services so they auto-start on login/boot and restart
|
|
6
|
+
* automatically on any exit.
|
|
6
7
|
*
|
|
7
8
|
* Platform support:
|
|
8
9
|
* - macOS : LaunchAgent/LaunchDaemon (LaunchDaemon requires root/system flag)
|
|
@@ -16,9 +17,8 @@ import * as fs from 'node:fs/promises';
|
|
|
16
17
|
import * as path from 'node:path';
|
|
17
18
|
import * as os from 'node:os';
|
|
18
19
|
import { execFileSync } from 'node:child_process';
|
|
19
|
-
import { createLogger } from '@intutic/logger';
|
|
20
20
|
import { loadConfig } from '../config/store.js';
|
|
21
|
-
|
|
21
|
+
import { localProxyBinary } from '../lib/proxyBinary.js';
|
|
22
22
|
// ── Error definitions ──────────────────────────────────────────────────
|
|
23
23
|
export class ElevationRequiredError extends Error {
|
|
24
24
|
constructor(targetPath) {
|
|
@@ -32,9 +32,13 @@ export function checkRootPrivileges(system, targetPath = '/Library/LaunchDaemons
|
|
|
32
32
|
throw new ElevationRequiredError(targetPath);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const SERVICE_NAMES = {
|
|
36
|
+
sync: { label: 'ai.intutic.sync-daemon', unitName: 'intutic-sync-daemon.service', logStem: 'sync-daemon' },
|
|
37
|
+
mcp: { label: 'ai.intutic.mcp-daemon', unitName: 'intutic-mcp-daemon.service', logStem: 'mcp-daemon' },
|
|
38
|
+
proxy: { label: 'ai.intutic.proxy', unitName: 'intutic-proxy.service', logStem: 'proxy' },
|
|
39
|
+
};
|
|
40
|
+
export function getServicePaths(system, target, platform = process.platform) {
|
|
41
|
+
const { label, unitName, logStem } = SERVICE_NAMES[target];
|
|
38
42
|
if (platform === 'darwin') {
|
|
39
43
|
const dir = system ? '/Library/LaunchDaemons' : path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
40
44
|
const plistPath = path.join(dir, `${label}.plist`);
|
|
@@ -44,8 +48,8 @@ export function getPaths(system, isMcp, platform = process.platform) {
|
|
|
44
48
|
targetDir: dir,
|
|
45
49
|
targetPath: plistPath,
|
|
46
50
|
logsDir,
|
|
47
|
-
logPath: path.join(logsDir,
|
|
48
|
-
errPath: path.join(logsDir,
|
|
51
|
+
logPath: path.join(logsDir, `${logStem}.log`),
|
|
52
|
+
errPath: path.join(logsDir, `${logStem}.err`),
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
else {
|
|
@@ -58,11 +62,15 @@ export function getPaths(system, isMcp, platform = process.platform) {
|
|
|
58
62
|
targetDir: dir,
|
|
59
63
|
targetPath: unitPath,
|
|
60
64
|
logsDir,
|
|
61
|
-
logPath: path.join(logsDir,
|
|
62
|
-
errPath: path.join(logsDir,
|
|
65
|
+
logPath: path.join(logsDir, `${logStem}.log`),
|
|
66
|
+
errPath: path.join(logsDir, `${logStem}.err`),
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
}
|
|
70
|
+
/** The two-daemon shape every existing caller uses; `getServicePaths` is the general form. */
|
|
71
|
+
export function getPaths(system, isMcp, platform = process.platform) {
|
|
72
|
+
return getServicePaths(system, isMcp ? 'mcp' : 'sync', platform);
|
|
73
|
+
}
|
|
66
74
|
// ── macOS LaunchAgent & LaunchDaemon ──────────────────────────────────
|
|
67
75
|
export function buildPlist(opts, system = false) {
|
|
68
76
|
const paths = getPaths(system, false, 'darwin');
|
|
@@ -122,19 +130,41 @@ ${argStrings.join('\n')}
|
|
|
122
130
|
</plist>
|
|
123
131
|
`;
|
|
124
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the argv used to launch the MCP daemon from a service definition.
|
|
135
|
+
*
|
|
136
|
+
* Both launchd and systemd exec the program directly rather than through a
|
|
137
|
+
* shell, and neither searches PATH: launchd requires an absolute path in
|
|
138
|
+
* `ProgramArguments[0]`, and systemd rejects a unit whose `ExecStart` does not
|
|
139
|
+
* start with an absolute path. So `which` is run for its *output* — the
|
|
140
|
+
* absolute location — not merely as a presence check.
|
|
141
|
+
*
|
|
142
|
+
* Falls back to running the bundled daemon entry point under the current Node
|
|
143
|
+
* when `intutic-mcp-daemon` is not installed on PATH.
|
|
144
|
+
*/
|
|
145
|
+
function resolveMcpDaemonExec(daemonPath) {
|
|
146
|
+
const fallback = [process.execPath, daemonPath];
|
|
147
|
+
try {
|
|
148
|
+
// stderr is dropped so a "not found" message never reaches the CLI output;
|
|
149
|
+
// `which` exits non-zero in that case and the catch below handles it.
|
|
150
|
+
const resolved = execFileSync('which', ['intutic-mcp-daemon'], {
|
|
151
|
+
encoding: 'utf-8',
|
|
152
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
153
|
+
})
|
|
154
|
+
.split('\n')[0]
|
|
155
|
+
.trim();
|
|
156
|
+
return path.isAbsolute(resolved) ? [resolved] : fallback;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return fallback;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
125
162
|
export function buildMcpPlist(opts, system = false) {
|
|
126
163
|
const paths = getPaths(system, true, 'darwin');
|
|
127
164
|
const config = loadConfig();
|
|
128
165
|
const workspaceRoot = config?.workspaceRoot ?? process.cwd();
|
|
129
166
|
const daemonPath = path.join(workspaceRoot, 'packages', 'mcp-proxy', 'dist', 'daemon', 'index.js');
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
execFileSync('which', ['intutic-mcp-daemon'], { stdio: 'ignore' });
|
|
133
|
-
execArgs = ['intutic-mcp-daemon'];
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
execArgs = [process.execPath, daemonPath];
|
|
137
|
-
}
|
|
167
|
+
const execArgs = resolveMcpDaemonExec(daemonPath);
|
|
138
168
|
const programArgsXml = execArgs.map(arg => ` <string>${arg}</string>`).join('\n');
|
|
139
169
|
const runAtLoad = system ? '' : '\n <key>RunAtLoad</key>\n <true/>';
|
|
140
170
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -198,7 +228,13 @@ async function installMacos(opts, system = false, dryRun = false) {
|
|
|
198
228
|
try {
|
|
199
229
|
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
200
230
|
}
|
|
201
|
-
catch {
|
|
231
|
+
catch {
|
|
232
|
+
// Best-effort: this unload exists only to release a previously installed
|
|
233
|
+
// agent before its plist is overwritten. On a first install there is no
|
|
234
|
+
// plist and nothing loaded, so launchctl exits non-zero — the expected
|
|
235
|
+
// case, not a failure. A real problem surfaces at the `load` below, which
|
|
236
|
+
// is deliberately not wrapped.
|
|
237
|
+
}
|
|
202
238
|
await fs.writeFile(paths.targetPath, plist, 'utf-8');
|
|
203
239
|
execFileSync('launchctl', ['load', '-w', paths.targetPath]);
|
|
204
240
|
console.log(`\n✅ Launch${system ? 'Daemon' : 'Agent'} installed and started.`);
|
|
@@ -206,7 +242,7 @@ async function installMacos(opts, system = false, dryRun = false) {
|
|
|
206
242
|
// ── Linux systemd ─────────────────────────────────────────────────────
|
|
207
243
|
export function buildUnit(opts, system = false) {
|
|
208
244
|
const paths = getPaths(system, false, 'linux');
|
|
209
|
-
let execStart
|
|
245
|
+
let execStart;
|
|
210
246
|
if (opts.binaryPath.endsWith('.js') || opts.binaryPath.endsWith('.ts')) {
|
|
211
247
|
execStart = `${process.execPath} ${opts.binaryPath}`;
|
|
212
248
|
}
|
|
@@ -249,14 +285,7 @@ export function buildMcpUnit(opts, system = false) {
|
|
|
249
285
|
const config = loadConfig();
|
|
250
286
|
const workspaceRoot = config?.workspaceRoot ?? process.cwd();
|
|
251
287
|
const daemonPath = path.join(workspaceRoot, 'packages', 'mcp-proxy', 'dist', 'daemon', 'index.js');
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
execFileSync('which', ['intutic-mcp-daemon'], { stdio: 'ignore' });
|
|
255
|
-
execStart = 'intutic-mcp-daemon';
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
execStart = `${process.execPath} ${daemonPath}`;
|
|
259
|
-
}
|
|
288
|
+
const execStart = resolveMcpDaemonExec(daemonPath).join(' ');
|
|
260
289
|
return `[Unit]
|
|
261
290
|
Description=Intutic MCP Daemon — Persistent policy and telemetry cache for MCP harnesses
|
|
262
291
|
Documentation=https://docs.intutic.ai/daemon
|
|
@@ -278,6 +307,119 @@ Environment=CONTROL_PLANE_URL=${opts.controlPlaneUrl}
|
|
|
278
307
|
StandardOutput=append:${paths.logPath}
|
|
279
308
|
StandardError=append:${paths.errPath}
|
|
280
309
|
|
|
310
|
+
[Install]
|
|
311
|
+
WantedBy=${system ? 'multi-user.target' : 'default.target'}
|
|
312
|
+
`;
|
|
313
|
+
}
|
|
314
|
+
// ── Standalone proxy (TD-465) ─────────────────────────────────────────
|
|
315
|
+
/**
|
|
316
|
+
* The environment `intutic start` hands the proxy, as a service file has to
|
|
317
|
+
* fix it: `PORT` always; `VALKEY_URL` when a Valkey was named, else
|
|
318
|
+
* `INTUTIC_STANDALONE=1` so the proxy skips the probe and says why;
|
|
319
|
+
* `UPSTREAM_URL` only when given. Insertion order is the order it is written.
|
|
320
|
+
*/
|
|
321
|
+
export function proxyServiceEnv(opts) {
|
|
322
|
+
return {
|
|
323
|
+
PORT: opts.port,
|
|
324
|
+
...(opts.valkeyUrl ? { VALKEY_URL: opts.valkeyUrl } : { INTUTIC_STANDALONE: '1' }),
|
|
325
|
+
...(opts.upstreamUrl ? { UPSTREAM_URL: opts.upstreamUrl } : {}),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Resolve the absolute `intutic-proxy` path a service file may reference.
|
|
330
|
+
*
|
|
331
|
+
* Same rule as `resolveMcpDaemonExec`: launchd and systemd exec the program
|
|
332
|
+
* directly, do not search PATH, and refuse a relative `ExecStart`, so the PATH
|
|
333
|
+
* shim `intutic start` falls back to is not usable here. An explicit path is
|
|
334
|
+
* taken as given but must be absolute; otherwise the launcher's version-pinned
|
|
335
|
+
* binary, then `which`. Nothing found is an error rather than a unit that
|
|
336
|
+
* fails on its first start with a message only the log file sees.
|
|
337
|
+
*/
|
|
338
|
+
export function resolveProxyServiceBinary(explicit) {
|
|
339
|
+
if (explicit) {
|
|
340
|
+
if (!path.isAbsolute(explicit)) {
|
|
341
|
+
throw new Error(`--binary-path must be absolute (a service file cannot search PATH): ${explicit}`);
|
|
342
|
+
}
|
|
343
|
+
return explicit;
|
|
344
|
+
}
|
|
345
|
+
const pinned = localProxyBinary();
|
|
346
|
+
if (pinned)
|
|
347
|
+
return pinned;
|
|
348
|
+
try {
|
|
349
|
+
const resolved = execFileSync('which', ['intutic-proxy'], {
|
|
350
|
+
encoding: 'utf-8',
|
|
351
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
352
|
+
})
|
|
353
|
+
.split('\n')[0]
|
|
354
|
+
.trim();
|
|
355
|
+
if (path.isAbsolute(resolved))
|
|
356
|
+
return resolved;
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
// `which` exits non-zero when nothing is on PATH; handled by the throw below.
|
|
360
|
+
}
|
|
361
|
+
throw new Error('Could not find the intutic-proxy binary. Install it with `npm install -g @intutic/proxy` ' +
|
|
362
|
+
'or pass --binary-path /absolute/path/to/intutic-proxy.');
|
|
363
|
+
}
|
|
364
|
+
export function buildProxyPlist(opts, system = false) {
|
|
365
|
+
const paths = getServicePaths(system, 'proxy', 'darwin');
|
|
366
|
+
const runAtLoad = system ? '' : '\n <key>RunAtLoad</key>\n <true/>';
|
|
367
|
+
const envXml = Object.entries(proxyServiceEnv(opts))
|
|
368
|
+
.map(([k, v]) => ` <key>${k}</key>\n <string>${v}</string>`)
|
|
369
|
+
.join('\n');
|
|
370
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
371
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
372
|
+
<plist version="1.0">
|
|
373
|
+
<dict>
|
|
374
|
+
<key>Label</key>
|
|
375
|
+
<string>${paths.label}</string>
|
|
376
|
+
|
|
377
|
+
<key>ProgramArguments</key>
|
|
378
|
+
<array>
|
|
379
|
+
<string>${opts.binaryPath}</string>
|
|
380
|
+
</array>
|
|
381
|
+
|
|
382
|
+
<key>KeepAlive</key>
|
|
383
|
+
<true/>
|
|
384
|
+
<key>ThrottleInterval</key>
|
|
385
|
+
<integer>5</integer>${runAtLoad}
|
|
386
|
+
|
|
387
|
+
<key>StandardOutPath</key>
|
|
388
|
+
<string>${paths.logPath}</string>
|
|
389
|
+
<key>StandardErrorPath</key>
|
|
390
|
+
<string>${paths.errPath}</string>
|
|
391
|
+
|
|
392
|
+
<key>EnvironmentVariables</key>
|
|
393
|
+
<dict>
|
|
394
|
+
${envXml}
|
|
395
|
+
</dict>
|
|
396
|
+
</dict>
|
|
397
|
+
</plist>
|
|
398
|
+
`;
|
|
399
|
+
}
|
|
400
|
+
export function buildProxyUnit(opts, system = false) {
|
|
401
|
+
const paths = getServicePaths(system, 'proxy', 'linux');
|
|
402
|
+
const envLines = Object.entries(proxyServiceEnv(opts))
|
|
403
|
+
.map(([k, v]) => `Environment=${k}=${v}`)
|
|
404
|
+
.join('\n');
|
|
405
|
+
return `[Unit]
|
|
406
|
+
Description=Intutic Proxy — Standalone governed LLM gateway
|
|
407
|
+
Documentation=https://docs.intutic.ai/integrations/standalone
|
|
408
|
+
After=network-online.target
|
|
409
|
+
Wants=network-online.target
|
|
410
|
+
|
|
411
|
+
[Service]
|
|
412
|
+
Type=simple
|
|
413
|
+
ExecStart=${opts.binaryPath}
|
|
414
|
+
|
|
415
|
+
Restart=always
|
|
416
|
+
RestartSec=5
|
|
417
|
+
|
|
418
|
+
${envLines}
|
|
419
|
+
|
|
420
|
+
StandardOutput=append:${paths.logPath}
|
|
421
|
+
StandardError=append:${paths.errPath}
|
|
422
|
+
|
|
281
423
|
[Install]
|
|
282
424
|
WantedBy=${system ? 'multi-user.target' : 'default.target'}
|
|
283
425
|
`;
|
|
@@ -337,11 +479,21 @@ export async function uninstallDaemon(opts = {}) {
|
|
|
337
479
|
try {
|
|
338
480
|
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
339
481
|
}
|
|
340
|
-
catch {
|
|
482
|
+
catch {
|
|
483
|
+
// Best-effort: uninstall is idempotent. launchctl exits non-zero when
|
|
484
|
+
// the agent is already unloaded or was never loaded, and in both cases
|
|
485
|
+
// the desired end state is reached. The plist removal below is what
|
|
486
|
+
// actually has to happen, so it must not be skipped on this failure.
|
|
487
|
+
}
|
|
341
488
|
try {
|
|
342
489
|
await fs.unlink(paths.targetPath);
|
|
343
490
|
}
|
|
344
|
-
catch {
|
|
491
|
+
catch {
|
|
492
|
+
// Best-effort: ENOENT means an earlier uninstall already removed the
|
|
493
|
+
// plist, which is success for this command. Other errors (EACCES on a
|
|
494
|
+
// /Library/LaunchDaemons plist) are already pre-empted by the
|
|
495
|
+
// checkRootPrivileges call above.
|
|
496
|
+
}
|
|
345
497
|
}
|
|
346
498
|
console.log(`✅ Launch${system ? 'Daemon' : 'Agent'} removed.`);
|
|
347
499
|
break;
|
|
@@ -353,12 +505,21 @@ export async function uninstallDaemon(opts = {}) {
|
|
|
353
505
|
try {
|
|
354
506
|
execFileSync('systemctl', [...cmdArgs, 'disable', '--now', paths.unitName], { stdio: 'ignore' });
|
|
355
507
|
}
|
|
356
|
-
catch {
|
|
508
|
+
catch {
|
|
509
|
+
// Best-effort: `disable --now` exits non-zero when the unit is not
|
|
510
|
+
// enabled, not loaded, or absent entirely — all of which mean the work
|
|
511
|
+
// this call was going to do is already done. The unit file removal
|
|
512
|
+
// below is the part that must still run.
|
|
513
|
+
}
|
|
357
514
|
try {
|
|
358
515
|
await fs.unlink(paths.targetPath);
|
|
359
516
|
execFileSync('systemctl', [...cmdArgs, 'daemon-reload'], { stdio: 'ignore' });
|
|
360
517
|
}
|
|
361
|
-
catch {
|
|
518
|
+
catch {
|
|
519
|
+
// Best-effort: ENOENT means the unit file is already gone, and the
|
|
520
|
+
// reload is deliberately inside the same block — if nothing was
|
|
521
|
+
// removed there is no change for systemd to pick up.
|
|
522
|
+
}
|
|
362
523
|
}
|
|
363
524
|
console.log(`✅ systemd ${system ? 'system' : 'user'} unit removed.`);
|
|
364
525
|
break;
|
|
@@ -419,7 +580,7 @@ export async function daemonStop() {
|
|
|
419
580
|
console.log(`✅ Successfully stopped ${system ? 'system' : 'user'} sync-daemon.`);
|
|
420
581
|
}
|
|
421
582
|
catch (e) {
|
|
422
|
-
console.log(`❌ Failed to stop ${system ? 'system' : 'user'} sync-daemon: ${e.message}`);
|
|
583
|
+
console.log(`❌ Failed to stop ${system ? 'system' : 'user'} sync-daemon: ${e instanceof Error ? e.message : String(e)}`);
|
|
423
584
|
}
|
|
424
585
|
}
|
|
425
586
|
}
|
|
@@ -436,7 +597,7 @@ export async function daemonStop() {
|
|
|
436
597
|
console.log(`✅ Successfully stopped ${system ? 'system' : 'user'} sync-daemon unit.`);
|
|
437
598
|
}
|
|
438
599
|
catch (e) {
|
|
439
|
-
console.log(`❌ Failed to stop ${system ? 'system' : 'user'} sync-daemon unit: ${e.message}`);
|
|
600
|
+
console.log(`❌ Failed to stop ${system ? 'system' : 'user'} sync-daemon unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
440
601
|
}
|
|
441
602
|
}
|
|
442
603
|
}
|
|
@@ -456,7 +617,7 @@ export async function daemonStart() {
|
|
|
456
617
|
console.log(`✅ Successfully started ${system ? 'system' : 'user'} sync-daemon.`);
|
|
457
618
|
}
|
|
458
619
|
catch (e) {
|
|
459
|
-
console.log(`❌ Failed to start ${system ? 'system' : 'user'} sync-daemon: ${e.message}`);
|
|
620
|
+
console.log(`❌ Failed to start ${system ? 'system' : 'user'} sync-daemon: ${e instanceof Error ? e.message : String(e)}`);
|
|
460
621
|
}
|
|
461
622
|
}
|
|
462
623
|
}
|
|
@@ -473,7 +634,7 @@ export async function daemonStart() {
|
|
|
473
634
|
console.log(`✅ Successfully started ${system ? 'system' : 'user'} sync-daemon unit.`);
|
|
474
635
|
}
|
|
475
636
|
catch (e) {
|
|
476
|
-
console.log(`❌ Failed to start ${system ? 'system' : 'user'} sync-daemon unit: ${e.message}`);
|
|
637
|
+
console.log(`❌ Failed to start ${system ? 'system' : 'user'} sync-daemon unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
477
638
|
}
|
|
478
639
|
}
|
|
479
640
|
}
|
|
@@ -507,7 +668,12 @@ export async function installMcpDaemon(opts) {
|
|
|
507
668
|
try {
|
|
508
669
|
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
509
670
|
}
|
|
510
|
-
catch {
|
|
671
|
+
catch {
|
|
672
|
+
// Best-effort: releases a previously installed MCP agent before its
|
|
673
|
+
// plist is overwritten. Nothing is loaded on a first install, so a
|
|
674
|
+
// non-zero exit here is the normal path. The `load` below is unwrapped
|
|
675
|
+
// and will report a genuine failure.
|
|
676
|
+
}
|
|
511
677
|
await fs.writeFile(paths.targetPath, plist, 'utf-8');
|
|
512
678
|
execFileSync('launchctl', ['load', '-w', paths.targetPath]);
|
|
513
679
|
console.log(`\n✅ MCP Launch${system ? 'Daemon' : 'Agent'} installed and started.`);
|
|
@@ -551,11 +717,21 @@ export async function uninstallMcpDaemon(opts = {}) {
|
|
|
551
717
|
try {
|
|
552
718
|
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
553
719
|
}
|
|
554
|
-
catch {
|
|
720
|
+
catch {
|
|
721
|
+
// Best-effort: uninstall is idempotent. An MCP agent that is already
|
|
722
|
+
// unloaded (or never was) makes launchctl exit non-zero while leaving
|
|
723
|
+
// the desired end state in place, and the plist removal below still
|
|
724
|
+
// has to run.
|
|
725
|
+
}
|
|
555
726
|
try {
|
|
556
727
|
await fs.unlink(paths.targetPath);
|
|
557
728
|
}
|
|
558
|
-
catch {
|
|
729
|
+
catch {
|
|
730
|
+
// Best-effort: ENOENT means a previous uninstall already removed the
|
|
731
|
+
// plist, which is the outcome this command wants. Permission failures
|
|
732
|
+
// on the /Library/LaunchDaemons copy are pre-empted by the
|
|
733
|
+
// checkRootPrivileges call above.
|
|
734
|
+
}
|
|
559
735
|
}
|
|
560
736
|
console.log(`✅ MCP Launch${system ? 'Daemon' : 'Agent'} removed.`);
|
|
561
737
|
break;
|
|
@@ -567,12 +743,21 @@ export async function uninstallMcpDaemon(opts = {}) {
|
|
|
567
743
|
try {
|
|
568
744
|
execFileSync('systemctl', [...cmdArgs, 'disable', '--now', paths.unitName], { stdio: 'ignore' });
|
|
569
745
|
}
|
|
570
|
-
catch {
|
|
746
|
+
catch {
|
|
747
|
+
// Best-effort: a unit that is not enabled, not loaded, or absent makes
|
|
748
|
+
// `disable --now` exit non-zero, and each of those already satisfies
|
|
749
|
+
// what this call was for. Removal of the unit file below must not be
|
|
750
|
+
// skipped because of it.
|
|
751
|
+
}
|
|
571
752
|
try {
|
|
572
753
|
await fs.unlink(paths.targetPath);
|
|
573
754
|
execFileSync('systemctl', [...cmdArgs, 'daemon-reload'], { stdio: 'ignore' });
|
|
574
755
|
}
|
|
575
|
-
catch {
|
|
756
|
+
catch {
|
|
757
|
+
// Best-effort: ENOENT means the unit file is already gone. The reload
|
|
758
|
+
// shares this block on purpose — with nothing removed there is no
|
|
759
|
+
// change for systemd to reload.
|
|
760
|
+
}
|
|
576
761
|
}
|
|
577
762
|
console.log(`✅ MCP systemd ${system ? 'system' : 'user'} unit removed.`);
|
|
578
763
|
break;
|
|
@@ -631,7 +816,7 @@ export async function mcpDaemonStop() {
|
|
|
631
816
|
console.log(`✅ Successfully stopped MCP Launch${system ? 'Daemon' : 'Agent'}.`);
|
|
632
817
|
}
|
|
633
818
|
catch (e) {
|
|
634
|
-
console.log(`❌ Failed to stop MCP Launch${system ? 'Daemon' : 'Agent'}: ${e.message}`);
|
|
819
|
+
console.log(`❌ Failed to stop MCP Launch${system ? 'Daemon' : 'Agent'}: ${e instanceof Error ? e.message : String(e)}`);
|
|
635
820
|
}
|
|
636
821
|
}
|
|
637
822
|
}
|
|
@@ -648,7 +833,7 @@ export async function mcpDaemonStop() {
|
|
|
648
833
|
console.log(`✅ Successfully stopped MCP ${system ? 'system' : 'user'} unit.`);
|
|
649
834
|
}
|
|
650
835
|
catch (e) {
|
|
651
|
-
console.log(`❌ Failed to stop MCP ${system ? 'system' : 'user'} unit: ${e.message}`);
|
|
836
|
+
console.log(`❌ Failed to stop MCP ${system ? 'system' : 'user'} unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
652
837
|
}
|
|
653
838
|
}
|
|
654
839
|
}
|
|
@@ -668,7 +853,7 @@ export async function mcpDaemonStart() {
|
|
|
668
853
|
console.log(`✅ Successfully started MCP Launch${system ? 'Daemon' : 'Agent'}.`);
|
|
669
854
|
}
|
|
670
855
|
catch (e) {
|
|
671
|
-
console.log(`❌ Failed to start MCP Launch${system ? 'Daemon' : 'Agent'}: ${e.message}`);
|
|
856
|
+
console.log(`❌ Failed to start MCP Launch${system ? 'Daemon' : 'Agent'}: ${e instanceof Error ? e.message : String(e)}`);
|
|
672
857
|
}
|
|
673
858
|
}
|
|
674
859
|
}
|
|
@@ -685,12 +870,262 @@ export async function mcpDaemonStart() {
|
|
|
685
870
|
console.log(`✅ Successfully started MCP ${system ? 'system' : 'user'} unit.`);
|
|
686
871
|
}
|
|
687
872
|
catch (e) {
|
|
688
|
-
console.log(`❌ Failed to start MCP ${system ? 'system' : 'user'} unit: ${e.message}`);
|
|
873
|
+
console.log(`❌ Failed to start MCP ${system ? 'system' : 'user'} unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* `intutic daemon status|stop|start --proxy` — the same three verbs the
|
|
883
|
+
* sync daemon and the MCP daemon have, for the standalone proxy service
|
|
884
|
+
* `installProxyService` writes. Both scopes are checked, as for the others:
|
|
885
|
+
* a user-level LaunchAgent and a system-level LaunchDaemon can coexist.
|
|
886
|
+
*/
|
|
887
|
+
export async function proxyServiceStatus() {
|
|
888
|
+
switch (process.platform) {
|
|
889
|
+
case 'darwin': {
|
|
890
|
+
for (const system of [false, true]) {
|
|
891
|
+
const paths = getServicePaths(system, 'proxy', 'darwin');
|
|
892
|
+
const plistExists = await fs.access(paths.targetPath).then(() => true).catch(() => false);
|
|
893
|
+
const scopeStr = system ? 'System-level (LaunchDaemon)' : 'User-level (LaunchAgent)';
|
|
894
|
+
if (plistExists) {
|
|
895
|
+
console.log(`✅ Proxy: ${scopeStr} installed at ${paths.targetPath}`);
|
|
896
|
+
try {
|
|
897
|
+
execFileSync('launchctl', ['list', paths.label], { stdio: 'inherit' });
|
|
898
|
+
}
|
|
899
|
+
catch {
|
|
900
|
+
console.log(` ⚠️ Proxy ${scopeStr} plist exists but is not loaded.`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
console.log(`⬜ Proxy: ${scopeStr} not installed`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
case 'linux': {
|
|
910
|
+
for (const system of [false, true]) {
|
|
911
|
+
const paths = getServicePaths(system, 'proxy', 'linux');
|
|
912
|
+
const scopeStr = system ? 'System-level' : 'User-level';
|
|
913
|
+
const cmdArgs = system ? [] : ['--user'];
|
|
914
|
+
try {
|
|
915
|
+
console.log(`\n--- Proxy ${scopeStr} Status ---`);
|
|
916
|
+
execFileSync('systemctl', [...cmdArgs, 'status', paths.unitName], { stdio: 'inherit' });
|
|
917
|
+
}
|
|
918
|
+
catch {
|
|
919
|
+
console.log(`⬜ Proxy: ${scopeStr} not running`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
export async function proxyServiceStop() {
|
|
927
|
+
switch (process.platform) {
|
|
928
|
+
case 'darwin': {
|
|
929
|
+
for (const system of [false, true]) {
|
|
930
|
+
const paths = getServicePaths(system, 'proxy', 'darwin');
|
|
931
|
+
const plistExists = await fs.access(paths.targetPath).then(() => true).catch(() => false);
|
|
932
|
+
if (plistExists) {
|
|
933
|
+
console.log(`\n🛑 Stopping proxy Launch${system ? 'Daemon' : 'Agent'}: ${paths.label}`);
|
|
934
|
+
try {
|
|
935
|
+
execFileSync('launchctl', ['unload', paths.targetPath], { stdio: 'ignore' });
|
|
936
|
+
console.log(`✅ Successfully stopped proxy Launch${system ? 'Daemon' : 'Agent'}.`);
|
|
937
|
+
}
|
|
938
|
+
catch (e) {
|
|
939
|
+
console.log(`❌ Failed to stop proxy Launch${system ? 'Daemon' : 'Agent'}: ${e instanceof Error ? e.message : String(e)}`);
|
|
689
940
|
}
|
|
690
941
|
}
|
|
691
942
|
}
|
|
692
943
|
break;
|
|
693
944
|
}
|
|
945
|
+
case 'linux': {
|
|
946
|
+
for (const system of [false, true]) {
|
|
947
|
+
const paths = getServicePaths(system, 'proxy', 'linux');
|
|
948
|
+
const unitExists = await fs.access(paths.targetPath).then(() => true).catch(() => false);
|
|
949
|
+
if (unitExists) {
|
|
950
|
+
try {
|
|
951
|
+
const cmdArgs = system ? [] : ['--user'];
|
|
952
|
+
execFileSync('systemctl', [...cmdArgs, 'stop', paths.unitName], { stdio: 'ignore' });
|
|
953
|
+
console.log(`✅ Successfully stopped proxy ${system ? 'system' : 'user'} unit.`);
|
|
954
|
+
}
|
|
955
|
+
catch (e) {
|
|
956
|
+
console.log(`❌ Failed to stop proxy ${system ? 'system' : 'user'} unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
export async function proxyServiceStart() {
|
|
965
|
+
switch (process.platform) {
|
|
966
|
+
case 'darwin': {
|
|
967
|
+
for (const system of [false, true]) {
|
|
968
|
+
const paths = getServicePaths(system, 'proxy', 'darwin');
|
|
969
|
+
const plistExists = await fs.access(paths.targetPath).then(() => true).catch(() => false);
|
|
970
|
+
if (plistExists) {
|
|
971
|
+
try {
|
|
972
|
+
execFileSync('launchctl', ['load', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
973
|
+
console.log(`✅ Successfully started proxy Launch${system ? 'Daemon' : 'Agent'}.`);
|
|
974
|
+
}
|
|
975
|
+
catch (e) {
|
|
976
|
+
console.log(`❌ Failed to start proxy Launch${system ? 'Daemon' : 'Agent'}: ${e instanceof Error ? e.message : String(e)}`);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
break;
|
|
981
|
+
}
|
|
982
|
+
case 'linux': {
|
|
983
|
+
for (const system of [false, true]) {
|
|
984
|
+
const paths = getServicePaths(system, 'proxy', 'linux');
|
|
985
|
+
const unitExists = await fs.access(paths.targetPath).then(() => true).catch(() => false);
|
|
986
|
+
if (unitExists) {
|
|
987
|
+
try {
|
|
988
|
+
const cmdArgs = system ? [] : ['--user'];
|
|
989
|
+
execFileSync('systemctl', [...cmdArgs, 'start', paths.unitName], { stdio: 'ignore' });
|
|
990
|
+
console.log(`✅ Successfully started proxy ${system ? 'system' : 'user'} unit.`);
|
|
991
|
+
}
|
|
992
|
+
catch (e) {
|
|
993
|
+
console.log(`❌ Failed to start proxy ${system ? 'system' : 'user'} unit: ${e instanceof Error ? e.message : String(e)}`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
// ── Public API (standalone proxy, TD-465) ────────────────────────────
|
|
1002
|
+
export async function installProxyService(opts = {}) {
|
|
1003
|
+
const resolved = {
|
|
1004
|
+
binaryPath: resolveProxyServiceBinary(opts.binaryPath),
|
|
1005
|
+
port: opts.port ?? '4000',
|
|
1006
|
+
valkeyUrl: opts.valkeyUrl,
|
|
1007
|
+
upstreamUrl: opts.upstreamUrl,
|
|
1008
|
+
};
|
|
1009
|
+
const system = !!opts.system;
|
|
1010
|
+
const mode = resolved.valkeyUrl ? `Valkey ${resolved.valkeyUrl}` : 'standalone (INTUTIC_STANDALONE=1)';
|
|
1011
|
+
switch (process.platform) {
|
|
1012
|
+
case 'darwin': {
|
|
1013
|
+
const paths = getServicePaths(system, 'proxy', 'darwin');
|
|
1014
|
+
if (!opts.dryRun) {
|
|
1015
|
+
checkRootPrivileges(system, paths.targetPath);
|
|
1016
|
+
}
|
|
1017
|
+
const plist = buildProxyPlist(resolved, system);
|
|
1018
|
+
console.log(`\n📦 Installing proxy macOS Launch${system ? 'Daemon' : 'Agent'}: ${paths.targetPath}`);
|
|
1019
|
+
console.log(` Binary : ${resolved.binaryPath}`);
|
|
1020
|
+
console.log(` Port : ${resolved.port} — ${mode}`);
|
|
1021
|
+
if (opts.dryRun) {
|
|
1022
|
+
console.log('\n[dry-run] Would write plist:');
|
|
1023
|
+
console.log(plist);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
await fs.mkdir(paths.targetDir, { recursive: true });
|
|
1027
|
+
await fs.mkdir(paths.logsDir, { recursive: true });
|
|
1028
|
+
try {
|
|
1029
|
+
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
1030
|
+
}
|
|
1031
|
+
catch {
|
|
1032
|
+
// Best-effort: releases a previously installed proxy agent before its
|
|
1033
|
+
// plist is overwritten. Nothing is loaded on a first install, so a
|
|
1034
|
+
// non-zero exit here is the normal path. The `load` below is unwrapped
|
|
1035
|
+
// and will report a genuine failure.
|
|
1036
|
+
}
|
|
1037
|
+
await fs.writeFile(paths.targetPath, plist, 'utf-8');
|
|
1038
|
+
execFileSync('launchctl', ['load', '-w', paths.targetPath]);
|
|
1039
|
+
console.log(`\n✅ Proxy Launch${system ? 'Daemon' : 'Agent'} installed and started.`);
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
case 'linux': {
|
|
1043
|
+
const paths = getServicePaths(system, 'proxy', 'linux');
|
|
1044
|
+
if (!opts.dryRun) {
|
|
1045
|
+
checkRootPrivileges(system, paths.targetPath);
|
|
1046
|
+
}
|
|
1047
|
+
const unit = buildProxyUnit(resolved, system);
|
|
1048
|
+
console.log(`\n📦 Installing proxy systemd ${system ? 'system' : 'user'} unit: ${paths.targetPath}`);
|
|
1049
|
+
console.log(` Binary : ${resolved.binaryPath}`);
|
|
1050
|
+
console.log(` Port : ${resolved.port} — ${mode}`);
|
|
1051
|
+
if (opts.dryRun) {
|
|
1052
|
+
console.log('\n[dry-run] Would write unit:');
|
|
1053
|
+
console.log(unit);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
await fs.mkdir(paths.targetDir, { recursive: true });
|
|
1057
|
+
await fs.mkdir(paths.logsDir, { recursive: true });
|
|
1058
|
+
await fs.writeFile(paths.targetPath, unit, 'utf-8');
|
|
1059
|
+
const cmdArgs = system ? [] : ['--user'];
|
|
1060
|
+
execFileSync('systemctl', [...cmdArgs, 'daemon-reload']);
|
|
1061
|
+
execFileSync('systemctl', [...cmdArgs, 'enable', '--now', paths.unitName]);
|
|
1062
|
+
console.log(`\n✅ Proxy systemd ${system ? 'system' : 'user'} unit installed and started.`);
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
default:
|
|
1066
|
+
console.log(`\n⚠️ Platform '${process.platform}' not supported.`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
export async function uninstallProxyService(opts = {}) {
|
|
1070
|
+
const system = !!opts.system;
|
|
1071
|
+
const paths = getServicePaths(system, 'proxy');
|
|
1072
|
+
if (!opts.dryRun) {
|
|
1073
|
+
checkRootPrivileges(system, paths.targetPath);
|
|
1074
|
+
}
|
|
1075
|
+
switch (process.platform) {
|
|
1076
|
+
case 'darwin': {
|
|
1077
|
+
console.log(`\n🗑 Removing proxy Launch${system ? 'Daemon' : 'Agent'}: ${paths.targetPath}`);
|
|
1078
|
+
if (!opts.dryRun) {
|
|
1079
|
+
try {
|
|
1080
|
+
execFileSync('launchctl', ['unload', '-w', paths.targetPath], { stdio: 'ignore' });
|
|
1081
|
+
}
|
|
1082
|
+
catch {
|
|
1083
|
+
// Best-effort: uninstall is idempotent. An agent that is already
|
|
1084
|
+
// unloaded (or never was) makes launchctl exit non-zero while leaving
|
|
1085
|
+
// the desired end state in place, and the plist removal below still
|
|
1086
|
+
// has to run.
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
await fs.unlink(paths.targetPath);
|
|
1090
|
+
}
|
|
1091
|
+
catch {
|
|
1092
|
+
// Best-effort: ENOENT means a previous uninstall already removed the
|
|
1093
|
+
// plist, which is the outcome this command wants. Permission failures
|
|
1094
|
+
// on the /Library/LaunchDaemons copy are pre-empted by the
|
|
1095
|
+
// checkRootPrivileges call above.
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
console.log(`✅ Proxy Launch${system ? 'Daemon' : 'Agent'} removed.`);
|
|
1099
|
+
break;
|
|
1100
|
+
}
|
|
1101
|
+
case 'linux': {
|
|
1102
|
+
console.log(`\n🗑 Removing proxy systemd ${system ? 'system' : 'user'} unit: ${paths.targetPath}`);
|
|
1103
|
+
if (!opts.dryRun) {
|
|
1104
|
+
const cmdArgs = system ? [] : ['--user'];
|
|
1105
|
+
try {
|
|
1106
|
+
execFileSync('systemctl', [...cmdArgs, 'disable', '--now', paths.unitName], { stdio: 'ignore' });
|
|
1107
|
+
}
|
|
1108
|
+
catch {
|
|
1109
|
+
// Best-effort: a unit that is not enabled, not loaded, or absent makes
|
|
1110
|
+
// `disable --now` exit non-zero, and each of those already satisfies
|
|
1111
|
+
// what this call was for. Removal of the unit file below must not be
|
|
1112
|
+
// skipped because of it.
|
|
1113
|
+
}
|
|
1114
|
+
try {
|
|
1115
|
+
await fs.unlink(paths.targetPath);
|
|
1116
|
+
execFileSync('systemctl', [...cmdArgs, 'daemon-reload'], { stdio: 'ignore' });
|
|
1117
|
+
}
|
|
1118
|
+
catch {
|
|
1119
|
+
// Best-effort: ENOENT means the unit file is already gone. The reload
|
|
1120
|
+
// shares this block on purpose — with nothing removed there is no
|
|
1121
|
+
// change for systemd to reload.
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
console.log(`✅ Proxy systemd ${system ? 'system' : 'user'} unit removed.`);
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
default:
|
|
1128
|
+
console.log(`\n⚠️ Platform '${process.platform}' is not supported.`);
|
|
694
1129
|
}
|
|
695
1130
|
}
|
|
696
1131
|
//# sourceMappingURL=install-daemon.js.map
|